zoukankan      html  css  js  c++  java
  • Python类元编程

    类元编程是指在运行时创建或定制类。在Python中,类是一等对象,因此任何时候都可以使用函数创建新类,而无需用class关键字。类装饰器也是函数,不过能够审查、修改,甚至把被装饰的类替换成其他类。元类是类元编程最高级的工具:使用元类可以创建具有某种特性的全新类种,例如我们见过的抽象基类

    首先,我们先尝试在运行时创建一个类,collections.namedtuple是一个类工厂函数。我们把一个类名和几个属性名传给这个函数,它会创建一个tuple的子类,其中的元素通过名称获取,还为调试提供了友好的字符串表示形式(__repr__)

    假设我们有个宠物狗Dog的类,这个类有三个字段,狗的名字(那么)、重量(weight)和主人(owner),正常大家都会想这个类应该是这样实现的:

    class Dog:
    
        def __init__(self, name, weight, owner):
            self.name = name
            self.weight = weight
            self.owner = owner
    

      

    如果现在猫的类或者一个水果类,而这些类中只要有一个__init__来初始化一些字段,会发现产生许多冗余的代码,现在,让我们写一个函数record_factory,这个函数可以即时创建简单的类,这里我们先看一下record_factory的效果,再分析record_factory函数

    >>> Dog = record_factory("Dog", "name weight owner")  # <1>
    >>> rex = Dog("Rex", 30, "Bob")
    >>> rex  # <2>
    Dog(name='Rex', weight=30, owner='Bob')
    >>> name, weight, _ = rex  # <3>
    >>> name, weight
    ('Rex', 30)
    >>> "{2}'s dog weighs {1}kg".format(*rex)  # <4>
    "Bob's dog weighs 30kg"
    Dog(name='Rex', weight=32, owner='Bob')
    >>> rex.weight = 32  # <5>
    >>> rex
    Dog(name='Rex', weight=32, owner='Bob')
    >>> Dog.__mro__  # <6>
    (<class 'factories.Dog'>, <class 'object'>)
    

        

    1. 这个工厂函数要求传入两个参数,第一个参数是类名,第二个参数是属性名,由若干空格隔开的属性名
    2. 字符串表示
    3. 实例是可迭代的对象,因此赋值时可以方便拆包
    4. 传给format等函数也可以拆包
    5. 实例是可变对象
    6. 新建的类继承自object,与我们的工厂函数没有关系

    现在,我们来看下record_factory()函数

    def record_factory(cls_name, field_names):
        try:
            field_names = field_names.replace(',', ' ').split()  # <1>
        except AttributeError:  # no .replace or .split
            pass  # assume it's already a sequence of identifiers
        field_names = tuple(field_names)  # <2>
    
        def __init__(self, *args, **kwargs):  # <3>
            attrs = dict(zip(self.__slots__, args))
            attrs.update(kwargs)
            for name, value in attrs.items():
                setattr(self, name, value)
    
        def __iter__(self):  # <4>
            for name in self.__slots__:
                yield getattr(self, name)
    
        def __repr__(self):  # <5>
            values = ', '.join('{}={!r}'.format(*i) for i
                               in zip(self.__slots__, self))
            return '{}({})'.format(self.__class__.__name__, values)
    
        cls_attrs = dict(__slots__=field_names,  # <6>
                         __init__=__init__,
                         __iter__=__iter__,
                         __repr__=__repr__)
    
        return type(cls_name, (object,), cls_attrs)  # <7>
    

      

    1. 这里尝试在逗号或空格处拆分field_names,如果失败则假定field_names是一个可迭代的对象,一个元素对应一个属性名
    2. 使用属性名构建元组,这将成为新建类的__slots__属性,此外,这么做还设定了拆包和字符串表示形式各字段的顺序
    3. 这个函数将成为新建类的__init__方法,参数有位置参数和关键字参数
    4. 实现__iter__函数,把类的实例变成可迭代的对象,按照__slots__设定的顺序产出字段值
    5. 迭代__slots__和self,生成类的字符串形式
    6. 组建类属性字典
    7. 调用type构造方法,构建新类,然后返回

    通常,我们把type视为函数,利用它返回一个对象的类型,如type(obj),作用与obj.__calss__相同。然而,type是一个类,当成类使用时,传入三个参数可以组建一个新的类,如:

    MyClass = type('MyClass', (MySuperClass, MyMixin), {'x': 42, 'x2': lambda self: self.x * 2})
    

     

    type的三个参数分别是name,bases和dict。最后一个参数是一个映射,指定新类的属性名和值,上述代码与下面的代码有相同的作用

    class MyClass(MySuperClass, MyMixin):
    	x = 42
    	def x2(self):
    		return self.x * 2
    

      

    record_factory函数的最后一行会构建出一个类,类的名称是cls_name参数的值,唯一的直接超类是object,有__slots__、__init__、__iter__和__repr__四个类属性,其中后三个是实例方法

    定义描述符的类装饰器:在Python属性描述符(一)这个章节中,LineItem类还有些问题没解决:存储属性的名称不具有描述性,即属性(如weight)的值存储名名_Quantity#{uuid},这样的名称不便于调试。由于实例化描述符时无法得到托管类属性,可是,一旦组件好整个类,而且把描述符实例绑定到类属性上之后,我们就可以审查类了。

    import abc
    import uuid
    
    
    class AutoStorage:
    
        def __init__(self):
            cls = self.__class__
            prefix = cls.__name__
            identity = str(uuid.uuid4())[:8]
            self.storage_name = '_{}#{}'.format(prefix, identity)
    
        def __get__(self, instance, owner):
            if instance is None:
                return self
            else:
                return getattr(instance, self.storage_name)
    
        def __set__(self, instance, value):
            setattr(instance, self.storage_name, value)
    
    
    class Validated(abc.ABC, AutoStorage):
    
        def __set__(self, instance, value):
            value = self.validate(instance, value)
            super().__set__(instance, value)
    
        @abc.abstractmethod
        def validate(self, instance, value):
            """return validated value or raise ValueError"""
    
    
    class Quantity(Validated):
        """a number greater than zero"""
    
        def validate(self, instance, value):
            if value <= 0:
                raise ValueError('value must be > 0')
            return value
    
    
    class NonBlank(Validated):
        """a string with at least one non-space character"""
    
        def validate(self, instance, value):
            value = value.strip()
            if len(value) == 0:
                raise ValueError('value cannot be empty or blank')
            return value
    
    
    def entity(cls):  # <2>
        for key, attr in cls.__dict__.items():  # <3>
            if isinstance(attr, Validated):  # <4>
                type_name = type(attr).__name__
                attr.storage_name = '_{}#{}'.format(type_name, key)  # <5>
        return cls  # <6>
    
    
    @entity  # <1>
    class LineItem:
        description = NonBlank()
        weight = Quantity()
        price = Quantity()
    
        def __init__(self, description, weight, price):
            self.description = description
            self.weight = weight
            self.price = price
    
        def subtotal(self):
            return self.weight * self.price
    

      

    1. 相比Python属性描述符(一)这个章节中的LineItem类,这里新增加了一个装饰器
    2. 装饰器的参数是一个类
    3. 迭代存储类属性的字典
    4. 判断属性是否是Validated描述符的实例
    5. 使用描述符类的名称和托管属性的名称重新命名storage_name
    6. 返回修改后的类
    >>> raisins = LineItem('Golden raisins', 10, 6.95)
    >>> dir(raisins)[:3]
    ['_NonBlank#description', '_Quantity#price', '_Quantity#weight']
    >>> LineItem.description.storage_name
    '_NonBlank#description'
    >>> raisins.description
    'Golden raisins'
    >>> getattr(raisins, '_NonBlank#description')
    'Golden raisins'
    

      

    可以看出,类装饰器能以比较简单的方式做到以前需要使用元类做的事情,即创建类时定制类。类装饰器有一个缺点:只对依附类有效,这意味着,被装饰的类的子类可能继承也可能不继承装饰器所做的改动

    导入时和运行时比较:为了正确地做元编程,必须知道Python解释器什么时候计算各个代码块。代码在导入时,解释器会从上到下一次性解析完.py模块的源码,然后生成用于执行的字节码。如果句法有错误,就在此时报告。如果本地__pycache__目录下中有最新的.pyc文件,解释器会跳过上述的步骤,因为已经有运行所需的字节码了

    编译肯定是导入时的动作,不过那个时期还会做其他的事,因为Python中的语句几乎都是可执行的,也就是说语句可能会运行用户代码,修改用户程序的状态。尤其是import语句,它不只是声明,在进程中首次导入模块时,还会运行所导入模块的全部顶层代码,以后导入相同的模块则使用缓存,只做名称绑定。那些顶层代码可以做任何事,包括通常在运行时做的事,例如连接数据库。因此,“导入时”与“运行时”之间的界限是模糊的,import语句可以触发运行时行为

    导入时会运行全部顶层代码,但是顶层代码会经过一些加工。导入模块时,解释器会执行顶层的def语句,解释器会编译函数的定义体(首次导入模块时),把函数对象绑定到对应的全局名称上,但是解释器显然不会执行函数的定义体。通常这意味着解释器在导入时定义顶层函数,但是仅当在运行时调用函数才会执行函数的定义体

    对类来说,情况就不同了:在导入时,解释器会执行每个类的定义体,甚至会执行嵌套类的定义体,执行类定义体的结果是,定义了类属性和方法,并构建了类对象,从这个意义上理解,类的定义体属于“顶层代码”,因为它在导入时运行

     先来看两个脚本:

    脚本evalsupport.py

    print('<[100]> evalsupport module start')
    
    
    def deco_alpha(cls):
        print('<[200]> deco_alpha')
    
        def inner_1(self):
            print('<[300]> deco_alpha:inner_1')
    
        cls.method_y = inner_1
        return cls
    
    
    class MetaAleph(type):
        print('<[400]> MetaAleph body')
    
        def __init__(cls, name, bases, dic):
            print('<[500]> MetaAleph.__init__')
    
            def inner_2(self):
                print('<[600]> MetaAleph.__init__:inner_2')
    
            cls.method_z = inner_2
    
    
    print('<[700]> evalsupport module end')
    

      

    脚本evaltime.py

    from evalsupport import deco_alpha
    
    print('<[1]> evaltime module start')
    
    
    class ClassOne():
        print('<[2]> ClassOne body')
    
        def __init__(self):
            print('<[3]> ClassOne.__init__')
    
        def __del__(self):
            print('<[4]> ClassOne.__del__')
    
        def method_x(self):
            print('<[5]> ClassOne.method_x')
    
        class ClassTwo(object):
            print('<[6]> ClassTwo body')
    
    
    @deco_alpha
    class ClassThree():
        print('<[7]> ClassThree body')
    
        def method_y(self):
            print('<[8]> ClassThree.method_y')
    
    
    class ClassFour(ClassThree):
        print('<[9]> ClassFour body')
    
        def method_y(self):
            print('<[10]> ClassFour.method_y')
    
    
    if __name__ == '__main__':
        print('<[11]> ClassOne tests', 30 * '.')
        one = ClassOne()
        one.method_x()
        print('<[12]> ClassThree tests', 30 * '.')
        three = ClassThree()
        three.method_y()
        print('<[13]> ClassFour tests', 30 * '.')
        four = ClassFour()
        four.method_y()
    
    print('<[14]> evaltime module end')
    

      

    现在让我们尝试在python控制台中导入evaltime.py模块,和用python解释器运行evaltime.py文件

    首先是在python控制台导入evaltime.py模块

    >>> import evaltime
    <[100]> evalsupport module start
    <[400]> MetaAleph body
    <[700]> evalsupport module end
    <[1]> evaltime module start
    <[2]> ClassOne body
    <[6]> ClassTwo body
    <[7]> ClassThree body
    <[200]> deco_alpha
    <[9]> ClassFour body
    <[14]> evaltime module end
    

      

    • [100]:evalsupport模块中所有顶层代码在导入模块时运行:解释器会比编译deco_alpha函数,但是不会执行定义体
    • [400]:MetaAleph类的定义体运行了
    • [2]:每个类的定义体都执行了
    • [6]:包括嵌套类的定义体执行了
    • [200]:先执行被装饰类ClassThree的定义体,然后运行装饰器函数
    • [14]:在这个场景中,evaltime模块时导入的,因此不会运行if __name__ == "__main__":块

    这里有一点要注意的:解释器先执行类的定义体,然后再调用依附在类上的装饰器函数,这是合理的行为,因为必须先构建类对象,装饰器才有类对象处理

    再来看另外一个例子,用python解释器执行evaltime.py文件

    python evaltime.py
    <[100]> evalsupport module start
    <[400]> MetaAleph body
    <[700]> evalsupport module end
    <[1]> evaltime module start
    <[2]> ClassOne body
    <[6]> ClassTwo body
    <[7]> ClassThree body
    <[200]> deco_alpha
    <[9]> ClassFour body
    <[11]> ClassOne tests ..............................
    <[3]> ClassOne.__init__
    <[5]> ClassOne.method_x
    <[12]> ClassThree tests ..............................
    <[300]> deco_alpha:inner_1
    <[13]> ClassFour tests ..............................
    <[10]> ClassFour.method_y
    <[14]> evaltime module end
    <[4]> ClassOne.__del__
    

      

    • [9]:到本条为止,输出和上一个例子相同
    • [3]:类的标准行为
    • [300]:deco_alpha装饰器修改了ClassThree.method_y方法,因此调用three.method_y()时运行inner_1函数的定义体
    • [4]:只有程序结束时,绑定在全局变量one上的Classone实例才会被垃圾回收程序收回

    在第二个例子中主要想说明的是,类装饰器可能对类的子类没有影响,我们把ClassFour定义为ClassThree的子类,ClassThree类上依附着@deco_alpha装饰器把method_y方法替换掉了,这对ClassFour类根本没有影响。当然,如果ClassFour.method_y()方法使用super()调用ClassThree.method_y()方法,我们便会看到装饰器起作用了,执行inner_1函数

    根据Python对象模型,类是对象,因此,类肯定是另外某个类的实例。默认情况下,Python中的类是type类的实例,也就是说,type是大多数内置的内或用户定义的类的元类:

    >>> "spam".__class__
    <class 'str'>
    >>> str.__class__
    <class 'type'>
    >>> LineItem.__class__
    <class 'type'>
    >>> type.__class__
    <class 'type'>
    

      

    为了避免无限回溯,type是其自身的实例,注意,str或LineItem是type的实例,而并不是继承自type,这两个类都是object的子类,str、LineItem、type和object这几个对象的关系如下图:

    两个示意图都是正确的,左边强调的是,str、type和LineItem都是object的子类,右边的示意图则表明,str、object和LineItem是type的实例,因为他们都是类。object类和type类之间的关系很独特:object是type的实例,而type是object的子类,除了type,标准库中还有一些别的元类,例如ABCMeta和Enum。collections.Iterable所属的类是abc.ABCMeta。Iterable是抽象类,而ABCMeta不是,Iterable是ABCMeta的实例:

    >>> import collections
    >>> collections.Iterable.__class__
    <class 'abc.ABCMeta'>
    >>> import abc
    >>> abc.ABCMeta.__class__
    <class 'type'>
    >>> abc.ABCMeta.__mro__
    (<class 'abc.ABCMeta'>, <class 'type'>, <class 'object'>)
    

      

    向上追溯,ABCMeta最终所属的类也是type。所有的类都直接或间接地是type的实例,不过只有元类同时也是type的子类,若想理解元类一定要知道这种关系,元类(如ABCMeta)从type类继承了构建类的能力

    Iterable是object的子类,是ABCMeta的实例。object和ABCMeta都是type的实例,但这里重要关系是,ABCMeta还是type的子类,因为ABCMeta是元类。所有类都是type的实例,但是元类还是type的子类,因此可以作为制造类的工厂。具体通过实现__init__方法定制实例,元类__init__方法可以做到类装饰器能做的事情,而且作用更大

    evaltime_meta.py:ClassFive是MetaAleph元类的实例,evalsupport模块在上面的evalsupport.py中

    from evalsupport import deco_alpha
    from evalsupport import MetaAleph
    
    print('<[1]> evaltime_meta module start')
    
    
    @deco_alpha
    class ClassThree():
        print('<[2]> ClassThree body')
    
        def method_y(self):
            print('<[3]> ClassThree.method_y')
    
    
    class ClassFour(ClassThree):
        print('<[4]> ClassFour body')
    
        def method_y(self):
            print('<[5]> ClassFour.method_y')
    
    
    class ClassFive(metaclass=MetaAleph):
        print('<[6]> ClassFive body')
    
        def __init__(self):
            print('<[7]> ClassFive.__init__')
    
        def method_z(self):
            print('<[8]> ClassFive.method_y')
    
    
    class ClassSix(ClassFive):
        print('<[9]> ClassSix body')
    
        def method_z(self):
            print('<[10]> ClassSix.method_y')
    
    
    if __name__ == '__main__':
        print('<[11]> ClassThree tests', 30 * '.')
        three = ClassThree()
        three.method_y()
        print('<[12]> ClassFour tests', 30 * '.')
        four = ClassFour()
        four.method_y()
        print('<[13]> ClassFive tests', 30 * '.')
        five = ClassFive()
        five.method_z()
        print('<[14]> ClassSix tests', 30 * '.')
        six = ClassSix()
        six.method_z()
    
    print('<[15]> evaltime_meta module end')
    

      

    现在,我们再用控制台和解释器执行的方式来执行evaltime_meta.py模块

    在控制台导入evaltime_meta.py模块

    >>> import evaltime_meta
    <[100]> evalsupport module start
    <[400]> MetaAleph body
    <[700]> evalsupport module end
    <[1]> evaltime_meta module start
    <[2]> ClassThree body
    <[200]> deco_alpha
    <[4]> ClassFour body
    <[6]> ClassFive body
    <[500]> MetaAleph.__init__
    <[9]> ClassSix body
    <[500]> MetaAleph.__init__
    <[15]> evaltime_meta module end
    

      

    • [6]:创建ClassFive时,执行了ClassFive的定义体,同时调用了MetaAleph.__init__方法
    • [9]:创建ClassFive的子类ClassSix时执行了ClassSix的定义体,同时调用了MetaAleph.__init__方法

    Python解释器执行ClassFive类的定义体时没有调用type构建工具的类定义体,而是调用MetaAleph类,看下evalsupport.py文件中的MetaAleph.__init__方法,可以发现有四个参数:

    • cls:这是要初始化的类对象
    • name、bases、dic:与构建类时传给type的参数一样

    evalsupport.py定义MetaAleph元类

    class MetaAleph(type):
        print('<[400]> MetaAleph body')
    
        def __init__(cls, name, bases, dic):
            print('<[500]> MetaAleph.__init__')
    
            def inner_2(self):
                print('<[600]> MetaAleph.__init__:inner_2')
    
            cls.method_z = inner_2
    

      

    通常编写元类时,__init__第一个参数self会改成cls,这样就能清楚表明要构建的实例是类

    在命令行中执行evaltime_meta.py脚本

    python evaltime_meta.py
    <[100]> evalsupport module start
    <[400]> MetaAleph body
    <[700]> evalsupport module end
    <[1]> evaltime_meta module start
    <[2]> ClassThree body
    <[200]> deco_alpha
    <[4]> ClassFour body
    <[6]> ClassFive body
    <[500]> MetaAleph.__init__
    <[9]> ClassSix body
    <[500]> MetaAleph.__init__
    <[11]> ClassThree tests ..............................
    <[300]> deco_alpha:inner_1
    <[12]> ClassFour tests ..............................
    <[5]> ClassFour.method_y
    <[13]> ClassFive tests ..............................
    <[7]> ClassFive.__init__
    <[600]> MetaAleph.__init__:inner_2
    <[14]> ClassSix tests ..............................
    <[7]> ClassFive.__init__
    <[600]> MetaAleph.__init__:inner_2
    <[15]> evaltime_meta module end
    

      

    • [300]:装饰器依附到ClassThree类上之后,method_y方法被替换成inner_1方法
    • [5]:虽然ClassFour是ClassThree的子类,但是依附的装饰器并没有对ClassFour造成影响
    • [600]:MetaAleph类的__init__函数把ClassFive和ClassSix的method_z函数替换成inner_2函数

    定制描述符的元类:回到LineItem类,我们是否能提供一个类,通过继承可以代替描述符或元类?

    import abc
    import uuid
    
    
    class AutoStorage:
        __counter = 0
    
        def __init__(self):
            cls = self.__class__
            prefix = cls.__name__
            identity = str(uuid.uuid4())[:8]
            self.storage_name = '_{}#{}'.format(prefix, identity)
    
        def __get__(self, instance, owner):
            if instance is None:
                return self
            else:
                return getattr(instance, self.storage_name)
    
        def __set__(self, instance, value):
            setattr(instance, self.storage_name, value)
    
    
    class Validated(abc.ABC, AutoStorage):
    
        def __set__(self, instance, value):
            value = self.validate(instance, value)
            super().__set__(instance, value)
    
        @abc.abstractmethod
        def validate(self, instance, value):
            """return validated value or raise ValueError"""
    
    
    class Quantity(Validated):
        """a number greater than zero"""
    
        def validate(self, instance, value):
            if value <= 0:
                raise ValueError('value must be > 0')
            return value
    
    
    class NonBlank(Validated):
        """a string with at least one non-space character"""
    
        def validate(self, instance, value):
            value = value.strip()
            if len(value) == 0:
                raise ValueError('value cannot be empty or blank')
            return value
    
    
    class EntityMeta(type):
        """Metaclass for business entities with validated fields"""
    
        def __init__(cls, name, bases, attr_dict):
            super().__init__(name, bases, attr_dict)  # <3>
            for key, attr in attr_dict.items():  # <4>
                if isinstance(attr, Validated):
                    type_name = type(attr).__name__
                    attr.storage_name = '_{}#{}'.format(type_name, key)
    
    
    class Entity(metaclass=EntityMeta):  # <2>
        """Business entity with validated fields"""
    
    
    class LineItem(Entity):  # <1>
        description = NonBlank()
        weight = Quantity()
        price = Quantity()
    
        def __init__(self, description, weight, price):
            self.description = description
            self.weight = weight
            self.price = price
    
        def subtotal(self):
            return self.weight * self.price
    

      

    1. LineItem是Entity的子类
    2. Entity类的存在只是为了便利,用户直接继承这个类,而无需关心EntityMeta元类,甚至可以不用知道他的存在
    3. 在超类(即type)调用__init__方法
    4. 获取描述符实例的类名和属性名,重定义描述符的storage_name

    元类的特殊方法__prepare__

    某些应用中,可能需要知道类的属性定义的顺序,type构造方法及元类的__new__和__init__方法都会收到要执行的类的定义体,形式是名称到属性的映像。在默认情况下,那个映射是字典,也就是说,元类或类装饰器获得映射时,属性在类定义中的顺序已经丢失了

    这个问题的解决办法是:使用Python3引入的特殊方法__prepare__。这个方法只在元类中有用,而且必须声明为类方法(即用@classmethod装饰器定义)。解释器调用元类的__new__方法之前会先调用__prepare__方法,使用类定义体中的属性创建映射。__prepare__方法的第一个参数是元类,随后两个参数分别是要构建的类名称和基类组成的元组,返回值必须是映射。元类构建新类时,__prepare__方法返回的映射会传给__new__方法的最后一个参数,然后再传给__init__方法

    class EntityMeta(type):
        """Metaclass for business entities with validated fields"""
    
        @classmethod
        def __prepare__(cls, name, bases):
            return collections.OrderedDict()  # <1>
    
        def __init__(cls, name, bases, attr_dict):
            super().__init__(name, bases, attr_dict)
            cls._field_names = []  # <2>
            for key, attr in attr_dict.items():  # <3>
                if isinstance(attr, Validated):
                    type_name = type(attr).__name__
                    attr.storage_name = '_{}#{}'.format(type_name, key)
                    cls._field_names.append(key)  # <4>
    
    
    class Entity(metaclass=EntityMeta):
        """Business entity with validated fields"""
    
        @classmethod
        def field_names(cls):  # <5>
            for name in cls._field_names:
                yield name
    

      

    1. 返回一个空的OrderDict实例,类属性将存储在里面
    2. 构建的类中创建一个_field_names属性
    3. attr_dict是之前那个OrderDict对象,由解释器在调用__init__方法之前调用__prepare__方法时获得。因此,这个for循环会按照添加属性的顺序迭代属性
    4. 找到各个Validated字段添加进_field_names列表
    5. _field_names类方法作用简单,按照添加字段的顺序产出字段的名称
    class LineItem(Entity):  
        description = NonBlank()
        weight = Quantity()
        price = Quantity()
    
        def __init__(self, description, weight, price):
            self.description = description
            self.weight = weight
            self.price = price
    
        def subtotal(self):
            return self.weight * self.price
    
    
    for name in LineItem.field_names():
        print(name)
    

      

    运行结果:

    description
    weight
    price
    

      

    在开发框架或库时,使用元类会协助我们执行很多任务,如:

    • 验证属性
    • 一次把装饰器依附到多个地方
    • 序列化对象或转换数据
    • 对象关系映射
    • 基于对象的持久存储
    • 动态转换使用其他语言编写的类结构

    类作为对象:__mro__、__class__和__name__已经见了很多次了,除此之外,类对象还有以下属性:

    • cls.__bases__:由类的基类组成的元组
    • cls.__qualname__:即从模块的全局作用域到类的点分路径,例如在A类中定义了B类,那么B类对象的__qualname__就是A.B
    • cls.__subclasses__():这个方法返回一个列表,包含类的直接子类
    • cls.mro():构建类时,如果需要获取存储在类属性的__mro__中的超类元组,解释器会调用这个方法。元类可以覆盖这个方法,定制要构建的类的解析顺序
  • 相关阅读:
    20180420首旅酒店的操作记录
    股票的站上均线和反弹购买的学习
    linux里面的fork函数创建一个新进程
    allegro导入网表过程中出现的错误信息
    海思NB-IOT的SDK添加AT指令方法
    如何在ubuntun中安装pycharm并将图标显示在桌面上
    流畅python学习笔记:第十九章:动态属性和特性
    一起来学linux:目录与路径
    一起来学linux:用户与用户组
    python自动化运维五:pexpect
  • 原文地址:https://www.cnblogs.com/beiluowuzheng/p/9248764.html
Copyright © 2011-2022 走看看