zoukankan      html  css  js  c++  java
  • 元类,异常处理

    元类的介绍:

    1.什么时元类?

      在python中,一切皆对象,类也是对象,可以把一个类当成普通对象来使用,比如存储到列表中,或者作为参数传给函数等等。

    对象时如何产生的?

      通过类实例化产生的

      类对象是由type实例化产生的

    obj = type("TestClass",(object,),{})
    print(obj)         # <class '__main__.TestClass'>

    一个类由三个部分组成:

      1.类名,2.类的基类(object) 3. 类的名称空间

    而使用type(类名,继承的父类,名称空间字典) 可以获得一个新的类。

    所以,总结出来,当定义一个class时,解释器会自动调用type来完成类的实例化

     案例:

    # 模拟解释器创建类的过程
    obj = type('TeatClass', (object,), {})
    print(obj)  # <class '__main__.TeatClass'>  得到一个类
    
    
    # 名称空间也可以放实际的函数
    def test1(a):
        print(a)
    
    
    def test2(self, b):
        print(self, b)
    
    
    class_name = 'C'
    bases = (object,)
    name_dict = {'name': 'jack', 'test1': test1, 'test2': test2} # 可以把函数作为名称空间字典
    
    C = type(class_name, bases, name_dict)  # 创建一个新的类,
    c1 = C()
    c1.test2(100)  # 可以通过点语法来调用定义的方法。

    exec与eval

    exec:执行储存在字符串或文件中的 Python 语句,相比于 eval,exec可以执行更复杂的 Python 代码。

    语法:exec(object, globals, locals )

      object : 必选参数,表示需要被指定的Python代码。它必须是字符串或code对象。如果object是一个字符串,该字符串会先被解析为一组Python语句,然后在执行(除非发生语法错误)。如果object是一个code对象,那么它只是被简单的执行。

      globals:可选参数,表示全局命名空间(存放全局变量),如果被提供,则必须是一个字典对象。

      locals:可选参数,表示当前局部命名空间(存放局部变量),如果被提供,可以是任何映射对象。如果该参数被忽略,那么它将会取与globals相同的值。

    返回值

    exec 返回值永远时None

     语法:

    glob = {}
    locl = {}
    
    code = '''
    def test(a):
        print(a)
    '''
    print(exec(code,glob,locl))

    eval: 用来执行一个字符串表达式,并返回表达式的值,但是不能由任何特殊的语法

    元类:metaclass

    用于产生类的类,称之为元类

    元类翻译为:metaclass , 所以一般在定义元类时,尽量在类名后面添加MetaClass,方便阅读

    元类的作用:

      当我们需要高度定制类时,比如限制类名,或着属性的名字等等。就需要使用元类,不过因为原类type中的代码无法被修改,所以我们一般创建新的元类方法是使用继承自type的子类,通过覆盖__init__来完成对类的限制

    自定义元类的语法:

    # 默认创建类时,是找的type来实例化的
    class MyMetaClass(type):
        pass
    
    
    # 使用metaclass关键字来定义原类的继承
    class Person(metaclass=MyMetaClass):
        pass
    
    class Student:
        def __init__(self):
            pass
    
    print(type(Person))     # <class '__main__.MyMetaClass'>
    print(type(Student))    # <class 'type'>
    # 创建对象也是一样,会先创建空的类对象,再调用__init__方法
    # Person = MyMetaClass()

    1.__init__方法*****(重点)

      实例化对象时会自动执行类中的__init__方法,类也是对象,在实例化类对象时会自动执行元类中的__init__方法

      并且传入类的三个必要参数:类的名字,继承的父类们,名称空间

      会自动传入类对象本身作为第一个参数(self)

    自定义原类案例:判断类名首字母是否为大写,判断类中的方法名称是否都为小写

    class MyMetaClass(type):
        def __init__(self, class_name, bases, name_dict):
            # 元类中的self表示的都是类对象
            # 不要忘记调用父类的初始化
            super().__init__(class_name, bases, name_dict)
            print(name_dict)
            # 判断类名,必须首字母大写,否则直接抛出异常
            if not class_name.istitle():
                print('类名首字母必须大写')
                raise Exception
    
            # 控制类中方法名必须全部小写
            # 遍历类中的名称空间,如果发现方法有大写,则报错
            for k in name_dict:
                if str(type(name_dict[k])) == "<class 'function'>":
                    if not k.islower():
                        print('方法名称必须全小写')
                        raise Exception
    
    
    # 会自动调用其元类中的__init__方法传入 类对象本身 类名称 父类们 名称空间
    class Student(object, metaclass=MyMetaClass):  # # MyMetaClass("Student",(object,),{})
        NAME = 10
    
        def say(self):
            print('SAY')
    
        pass

    案例:需求:要求每个类必须包含__doc__属性,__doc__用于访问一个对象的注释信息

    # 如果你要控制类的创建,那就自定义元类 覆盖__init__
    
    class DocMeatClass(type):
        def __init__(self,class_name,bases,name_dict):
            super().__init__(class_name,bases,name_dict)
            if not self.__doc__:
                raise Exception
    
    class Person(metaclass=DocMeatClass):
        pass

    2.__new__方法*****(重点)

    元类中的new方法会创建类对象时执行,并且先于init方法,它的作用是创建一个类对象

    执行步骤:1.执行__new__方法,拿到一个类对象, 2. 执行__init__方法,传入类对象以及其他属性,进行初始化

    注意:定义类的时候,本身有一个默认的__new__,如果覆盖了__new__一定也要调用type中的__new__并返回执行结果,不然无法定义类

    使用new方法也可以完成定制类的工作,和init有什么区别?

      在调用__init__方法前,类对象已经创建完成了,所以如果对性能要求高的话,可以选择在__new__中完成定制,如果发现有问题,就不用创建类对象了

    语法:

    class MyMetaClass(type):
        # 传入类的三大组成部分
        def __init__(self, class_name, bases, name_dict):
            super().__init__(class_name, bases, name_dict)
            print('init')
    
        # 该方法会在实例化类对象时自动调用并且优先在__init__之前调用
        # 其作用是用于创建新的类对象的
        # 注意这里必须调用type类中的__new__否则将无法产生类对象 并且返回其结果
        def __new__(cls, *args, **kwargs):
            # cls 表示元类自己,即MyMetaClass
            return type.__new__(cls, *args, **kwargs)  # 类本身有一个__new__方法,如果覆盖__new__一定要将__new__返回
    
    
    class Person(metaclass=MyMetaClass):
        pass
    
    print(Person)    # None 如果__new__后面没有 return type.__new__(...),则返回None,代表没有生成类对象
                # 反之,则 <class '__main__.Person'>


    # 就算__init__中什么都不写 这个类对象其实已经创建完成了 该有的属性都有了 # 这是与普通类不同之处

    3.__call__方法*****(重点)

    元类中的__call__方法会在调用类时执行,可以用于控制对象的创建过程

    总结:当你要定制类时,就自定义元类并覆盖__init__方法

     案例:使传入的字符串全部改为大写

    class MyMeta(type):
    
        # 获得某个类的实例
        def __call__(self, *args, **kwargs):
            new_args = []
            for i in args:
                if isinstance(i, str):
                    new_args.append(i.upper())
                else:
                    new_args.append(i)
            return super().__call__(*new_args, **kwargs)
    
    
    # 注意注意注意!!!__new__  __init__是创建类对象时还会执行
    # __call__类对象要产生实例时执行
    class Student(metaclass=MyMeta):
        def __init__(self, name, gender, age):
            self.name = name
            self.gender = gender
            self.age = age
    
    s= Student('jack','man',18)
    print(s.age)
    
    
    class Person(metaclass=MyMeta):
        def __init__(self,name,gender):
            self.name = name
            self.gender = gender
    
    p = Person('rose','woman')
    print(p.gender)

    元类实现单例模式

    什么是单例:

      某个类如果只有一个实例对象,那么该类成为单例类

    单例的好处:

      当某个类的所有对象特征和行为完全一样时,避免重复创建对象,浪费资源,如果是避免重复创建对象

    案例:

    # 实例化传入相同参数,得到的实例地址不同,浪费资源
    class People:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    p1 = People('李果',25)  # <__main__.People object at 0x000002A28FC38400>
    p2 = People('李果',25)  # <__main__.People object at 0x000002A28FC389B0>
    p3 = People('李果',25)  # <__main__.People object at 0x000002A28FC38AC8>
    print(p1)
    print(p2)
    print(p3)

    单列需要与__call__一起使用,在__call__内部加判断

    案例:

    class SingletonMetaClass(type):
        # 创建类时会执行init 在这为每个类设置一个obj属性,默认为None
        def __init__(self, a, b, c):
            super().__init__(a, b, c)
            self.obj = None
    
        # d当类要创建对象时会执行,该方法
        def __call__(self, *args, **kwargs):
            # 判断这个类,如果已经有实例了就 直接返回,从而实现单例
            if self.obj:
                return self.obj
    
            # 没有则创建新的实例并保存到类中
            obj = type.__call__(self, *args, **kwargs)
            self.obj = obj
            return obj
    
    
    class Person(metaclass=SingletonMetaClass):
        def __init__(self, name, age, gender):
            self.name = name
            self.age = age
            self.gender = gender
    
        def say(self):
            print('my name is %s my age is %s my gender is %s' % self.name,self.age,self.gender)
    
    
    
    stu1 = Student("布兰", 16, "man")
    stu2 = Student("布兰", 16, "man")
    stu3 = Student("布兰", 16, "man")
    print(stu1, stu2, stu3) # 地址完全一样

     案例二:生成单例播放器

    class Single(type):
        def __init__(self, a, b, c):
            super().__init__(a, b, c)
            self.obj = None
    
        def __call__(self, *args, **kwargs):
            if self.obj:
                return self.obj
    
            obj = type.__call__(self, *args, **kwargs)
            self.obj = obj
            return obj
    
    
    class QQPlayer(metaclass=Single):
        def __init__(self, voice_value, repeat=False):
            self.voice_value = voice_value
            self.repeat = repeat
    
        def play(self, file_path):
            if hasattr(self, 'file_path'):
                self.stop()
    
            print('正在播放%s' % file_path)
            self.file_path = file_path
    
        def stop(self):
            print('%s停止播放' % self.file_path)
    
    
    playerl = QQPlayer(100,True)
    playerl.play('如果我是DJ.mp3')
    
    player2 = QQPlayer(100,True)
    player2.play('你会爱我吗.mp3')
    
    player3 = QQPlayer(100,True)
    player3.play('我的滑板鞋.mp3')

       异常

    1.什么是异常

      异常是程序运行过程中发生的非正常情况,是一个错误发生时的信号,异常如果没有被正常处理的话,将导致程序被终止,这对于用户体验是非常差的,可能导致严重的后果。处理异常的目的就是提高程序的健壮性

    2.异常的分类

      

    TypeError: 'int' object is not subscriptable     对象不能被切片  
    TypeError: 'list' object is not callable        对象不能被调用
    IndexError: list index out of range                索引超出范围
    TypeError: 'builtin_function_or_method' object is not iterable     对象不能被迭代
    KeyError: 'xxx'      不存在这个key
    FileNotFoundError: [Errno 2] No such file or directory: 'xxxxx'  文件找不到

    3.异常的组成

    Traceback (most recent call last):
      File "F:/python8期/课堂内容/day29/11.常见异常.py", line 22, in <module>
        with open("xxxxx") as f:
    FileNotFoundError: [Errno 2] No such file or directory: 'xxxxx'

    Traceback : 是异常追踪信息,用于展示错误发生的具体位置,以及调用的过程,其中包括了错误发生的  模块  ,  文件路径  ,行号  ,函数名称  ,具体的代码  

    最后一行: 前面是错误的类型,后面是错误的详细信息,在查找错误时,主要参考的就是详细信息

    4.异常的处理

      异常发生后,如果不正确处理将会导致程序终止,所以我们应该尽量的避免这种情况发生

    必须掌握的语法:

    try:

      可能会出现异常的代码,放到try里面

    except 具体的类型 as e:

      如果真的发生异常就会执行except

    5.如何正确处理异常

      1.当发生异常时,不是立马加try, 要先找出错误原因并解决它

      2.try 仅在 即使你知道为什么发生错误,但是你却无法避免

        例如,你明确告诉用户需要一真确的文件路径,然而用户依然传入了错误的路径

        如 socket 双方都要使用管道,但是如果以访由于某些原因强行关闭了,即使你只要原因也无法避免出错,那就只能try 保证程序正常结束

        能不加try 就不加 try

    6.自定义异常类:

      当系统提供异常类不能准确描叙错误原因时,就可以自定义异常类,继承自Exception即可

    class MyException(Exception) :

      pass

    7.主动抛出异常

    什么时候需要主动抛出异常?

      当我们做功能的提供者,给外界提供一个功能接口

      但是使用者不按照相应的方式来使用,或者参数类型不正确等原因,导致功能无法正常执行时,就应该主动抛出异常

    主动抛出异常使用 raise 关键字

    后面可以跟任何Exception的 子类 或是 对象。

    raise MyException
    raise MyException("错误具体原因!")

    8.断言assert

    断言,其实可以理解为断定的意思

    即非常肯定某个条件是成立的

    条件是否成立其实可以使用 if 来判断

    其存在的目的就是为了简化 if 判断而生的

  • 相关阅读:
    Android开发如何定制framework层服务
    Intellij IDEA通过SVN导入基于Springboot的maven项目以及对已有项目做更新
    intelliJ IDEA 怎么添加本地的idea web项目
    Android热修复之AndFix使用教程
    iOS友盟分享的使用总结
    iOS 传感器集锦
    IOS CALayer的属性和使用
    Swift使用Alamofire实现网络请求
    Android踩坑随笔Fragment中onActivityResult方法不被调用
    上周热点回顾(4.30-5.6)团队
  • 原文地址:https://www.cnblogs.com/liguodeboke/p/10920571.html
Copyright © 2011-2022 走看看