类元编程:python
在运行时建立或定制类的技艺。在Python中,类是一等对象,所以任什么时候候均可以使用函数新建类。而无需使用class关键字。数据库
类装饰器也是函数,不过能审查、修改,甚至把被装饰的类替换成其余类。编程
类工厂函数:缓存
collections.nametuple。咱们把一个类名和几个属性名传给这个函数,它会建立一个tuple的子类,其中的元素经过名称获取,还为调式提供了友好的字符串表示形式(__repr__)app
自定义一个类工厂函数:ide
def record_factory(cls_name, field_name): try: field_name = field_name.replace(',',' ').split() except AttributeError: pass field_name = tuple(field_name) def __init__(self,*args,**kwargs): attr = dict(zip(self.__slots__, args)) attr.update(kwargs) for key,value in attr.items(): setattr(self, key, value) def __iter__(self,): for name in self.__slots__: yield getattr(self,name) def __repr__(self,): values = ', '.join('{}={!r}'.format(*i) for i in zip(self.__slots__,self)) temp = '{}({})'.format(self.__class__.__name__,values) return temp cls_attrs = dict(__slots__ = field_name, __init__ = __init__, __iter__ = __iter__, __repr__ = __repr__) return type(cls_name, (object,), cls_attrs) Dog = record_factory('Dog','name age owner') rex = Dog('Rex' , 39 , 'DDD') print(rex.name)
(1)咱们把type视做函数,由于咱们像函数那样使用它,调用type(my_object)获取对象所属的类,做用与my_object.__class__相同;然而,type是一个类。当成类使用时,传入三个参数能够新建一个类。函数
MyClass = type(‘MyClass’, (MySuperClass, MyMixin), {'x':42, 'x2':lambda self:self.x *2})spa
type的三的参数分别是name、bases和dict,最后一个参数是一个映射,指定新类的属性名和值。等效于code
class MyClass(MySuperClass, MyMixin): x = 42 def x2(self): return self.x * 2
特别的,type的实例是类。orm
__slots__属性的主要特点是节省内存,能处理数百万个实例,不过也有一些缺点。
把三个参数传给type是动态建立类的经常使用方式。
collections.nametuple函数有另外一种方式:先声明一个_class_template变量,其值是字符串形式的源码模板,而后再namedtuple函数中调用_class_templete.format(...)方法,填充模板里的空白,
最后,使用内置的exec函数计算获得的源码字符串。
▲ record_factory函数建立的类,其实例有个局限:不能序列化,即不能使用pickle模块里的dump/load函数处理。
定制描述符的类装饰器:
原由:咱们不能使用描述性的存储属性名称,由于实例化描述符时,没法得知托管属性的名称。(即绑定到描述符上的类属性)
但是,一旦组建好整个类,并且把描述符绑定到类属性上以后,咱们就能够审查类,并为描述符设置合理的存储属性名称。
但是,一旦LineItem类构建好了,描述符与托管属性之间的绑定就不会变了。所以,咱们要在建立类时设置存储属性的名称。
使用类装饰器或元类能够作到这一点。
def entity(cls): for key, attr in cls.__dict__.items(): if isinstance(attr,Validated): attr.storage_name = '_{}#{}'.format(type(attr).__name__, key) return cls @entity class LineItem: description = NonBlank() weight = Quantity() price = Quantity() def __init__(self, description, weight, price): self.description = description self.weight = weight self.price = price
(1)类装饰器能以较简单的方式作到之前须要使用元类去作的事情--建立类时定制类。
(2)类装饰器有个重大缺点:只对直接依附的类有效。
(3)被装饰的类的子类可能继承也可能不继承装饰器所作的改动。
导入时和运行时比较:
导入时,Python解释器从上到下一次性解析完.py模块的源码,而后生成用于执行的字节码。若是句法有错误,就在此时报告。
若是本地__pycache__文件夹中有最新的.pyc文件,解释器会跳过上述步骤,由于已经有运行所需的字节码了。
编译确定是导入时的活动,不过那个时期还会作其余事,由于Python中的语句几乎都是可执行的,也就是说语句可能会运行用户代码,修改用户程序的状态。
尤为是import语句。它不仅是声明,在进程中首次导入模块时,还会运行所导入模块中的所有顶层代码。
之后导入相同的模块则使用缓存,只作名称绑定。那些顶层代码能够作任何事情,包括一般在“运行时”作的事,(链接数据库)
导入时和运行时界线是模糊的,import语句能够触发任何“运行时”的行为。
解释器会编译函数的定义体(首次导入模块时),把函数对象绑定到对应的全局名称上,可是显然解释器不会执行函数的定义体。
一般这意味着解释器在导入时定义顶层函数,可是仅当在运行时调用函数时才会执行函数的定义体。
对类来讲,在导入时,解释器会执行每一个类的定义体,甚至会执行嵌套类的定义体。执行类定义体的结果是,定义了类的属性和方法,并构建了类对象。
类的定义体属于“顶层代码”,由于他在导入时运行。
计算时间的练习:
场景1:>>> import evaltime
场景2:>>> python3 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') 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')
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')
场景1运行结果:
>>> import evaltime <[100]> evalsupport module start <[400]> MetaAleph body # 说明类的 body 在导入时就执行 <[700]> evalsupport module end <[1]> evaltime module start <[2]> ClassOne body <[6]> ClassTwo body # 类的 body 中有其余类, 也会被执行 <[7]> ClassThree body # 先执行类的body, 而后将类做为类装饰器的参数 <[200]> deco_alpha <[9]> ClassFour body # 类继承并不会继承类装饰器 <[14]> evaltime module end
场景2运行结果:
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]> deco_alpha:inner_1 # ClassFour 继承的是经由装饰器修改后的ClassThree <[14]> evaltime module end <[4]> ClassOne.__del__ # 退出时垃圾回收
总结:
(1)解释器会执行所导入模块及其依赖中的每一个类定义体。
(2)解释器先计算类的定义体,而后调用依附在类上的装饰器函数,先构建类对象,装饰器才有类对象可处理。
(3)类装饰器对子类没有影响,除非子类使用了super()语句。
元类基础知识:
元类是制造类的工厂,不过不是函数,而是类。
根据Python对象模型,类是对象,所以类确定是另外某个类的实例。
默认状况下,Python中的类是type类的实例。也就是说,type是大多数内置的类和用户定义的类的元类;
为了不无限回溯,type是其自身的实例。(object是type的实例,而type是object的子类)
除了type,标准库中还有一些别的元类,如:ABCMeta和Enum。
元类从type类继承了构建类的能力。(全部类都是type的实例,可是元类仍是type的子类。)
元类能够经过实现__init__方法定制实例。元类的__init__方法能够作到类装饰器能作到的任何事情,可是做用更大。
元类计算时间的练习:
场景3:>>> import evaltime_meta
场景4:>>> python3 evaltime_meta.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')
场景3运行结果:
>>> 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__ # 先 ClassFour 定义, 而后交给其元类对他加工 <[9]> ClassSix body <[500]> MetaAleph.__init__ # 先 ClassFour 定义, 而后交给其元类对他加工, 因为继承自 ClassFive, 其元类同上(注意区分基类与元类) <[15]> evaltime_meta module end
场景4运行结果:
python3 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
总结:
(1)建立ClassFive时调用了MetaAleph.__init__方法。
(2)建立ClassFive的子类ClassSix时也调用了MetaAleph.__init__方法。
(3)__init__方法,四个参数:self(或者写成 cls):要初始化的类对象,(如ClassFive),name、bases、dic与构建类时传给type的参数同样。
(4)先执行类的定义体,再去执行元类的__init__方法。
注意:
ClassSix类没有直接引用MetaAleph类,可是却受到了影响,由于它是ClassFive的子类,进而也是MetaAleph类的实例,因此由MetaAleph.__init__方法初始化。
class FooMeta(type): print('FooMeta__class__') def __init__(cls,name,bases,dic): print('FooMeta.__init__') class Foo(metaclass=FooMeta): print('Foo__class__') def __init__(self): print('Foo.__init__') FooMeta__class__ Foo__class__ FooMeta.__init__
定制描述符的元类:
class EntityMeta(type): def __init__(cls, name, bases, attr_dict): super().__init__(name, bases, attr_dict) for key, attr in attr_dict.items(): if isinstance(attr, Validated): attr.storage_name = '_{}#{}'.format(type(attr).__name__, key) class Entity(metaclass=EntityMeta): """""" class LineItem(Entity): description = NonBlank() weight = Quantity() price = Quantity() def __init__(self, description, weight, price): self.description = description self.weight = weight self.price = price
元类的特殊方法__prepare__:
元类或者类装饰器得到映射时,属性在类定义体中的顺序已经丢失。
特殊方法__prepare__只在元类中有用,并且必须声明为类方法(用@classmethod 装饰器定义)。
解释器调用元类的__new__方法前,会先调用__prepare__方法,使用类定义体中的属性建立映射。
__prepare__方法的第一个参数是元类,随后两个参数分别是要构建的类的名称和基类组成的元组。返回值必须是映射。
元类构建新类时,__prepare__方法返回的映射会传给__new__方法的最后一个参数,而后再传给__init__方法。
class EntityMeta(type): @classmethod def __prepare__(metacls, name, bases): return collections.OrderedDict() def __init__(self, cls, bases, attr_dict): super().__init__(cls, bases, attr_dict) cls._field_names = [] for key, attr in attr_dict.items(): if isinstance(attr, Validated): attr.storage_name = '_{}#{}'.format(type(attr).__name__, key) cls._field_names.append(key) class Entity(metaclass=EntityMeta): """带有验证字段的业务实体""" @classmethod def field_names(cls): for key in cls._field_names: yield key
类做为对象:
__mor__、__class__、__name__
cls.__bases__;由类的基类组成的元组
cls.__qualname__;类或函数的限定名称,
cls.__subclasses__();返回列表,包含类的直接子类,是内存里现存的子类。
cls.mro();构建类时,若是须要获取存储在类属性__mro__中的超类元组,解释器会调用这个方法。