类元编程
元类编程指在运行时创建或定制类。
- 在运行时创建类
类工厂函数
类是一等对象,可以使用函数新建类,而无须使用class关键字
def record_factory(cls_name, field_names):
try:
field_names = field_names.replace(',', ' ').split()
except AttributeError:
psss
field_names = tuple(field_names)
def __init__(self, *args, **kwargs):
attrs = dict(zip(self.__slots__, args))
attrs.update(kwargs)
for name, value in attrs.items():
setattr(self, name, 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))
return '{}({})'.format(self.__class__.__name__ , values)
cls_attrs = dict(__slots__ = field_names,
__init = __init__,
__iter__ = __iter__,
__repr__ = __repr__)
return type(cls_name, (object,), cls_attrs) # type传入三个参数可以新建一个类,第二个参数是继承的类,第三个参数是dict类型的属性名和值
Dog = record_factory('Dog', 'name weight owner')
rex = Dog('Rex', 30, 'Bob')
print(rex)
name, weight, _ = rex # 解包
name, weight
"{2}'s dog weighs {1}kg".format(*rex)
rex.weight = 32
print(rex)
Dog.__mro__ # 继承的类
以上创建的类,名称是传入的cls_name,唯一的超类object,有__slots__
、__init__
、__iter__
和__repr__
四个类属性,三个实例方法。
定制描述符的类装饰器
类装饰器,参数为类对象的函数,返回原来的类或修改后的类
import abc
class AutoStorage:
__counter = 0
def __init__(self):
cls = self.__class__
prefix = cls.__name__
index = cls.__counter
self.storage_name = '_{}#{}'.format(prefix, index)
cls.__counter += 1
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):
"""raise"""
class Quantity(Validated):
"""处理数值"""
def validate(self, instance, value):
if value <= 0:
raise ValueError('value must be > 0')
else:
return value
class NonBlank(Validated):
"""处理description为空"""
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):
"""类装饰器,修改各个描述符实例的storage_name属性"""
for key, attr in cls.__dict__.items():
if isinstance(attr, Validated):
type_name = type(attr).__name__
attr.storage_name = '_{}#{}'.format(type_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
def subtotal(self):
return self.weight * self.price
raisins = LineItem('Golden raisins', 10, 6.95)
dir(raisins)[:3]
LineItem.description.storage_name
raisins.description
getattr(raisins, '_NonBlank#description')
类装饰器对被装饰的子类可能继承也可能不继承装饰器所做的改动。
导入时和运行时比较
解释器编译函数的定义体,把函数对象绑定到对应的全局名称上,但是不会执行函数的定义体。
对类,解释器会执行每个类的定义体,甚至会执行嵌套类的定义体。执行定义体时,定义了类的属性和方法,并构建了类对象。
调用在何时执行
# 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, base, dic):
print('<[500]> MetaAleph.__init__:inner_2')
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]> ClassOne.method_x')
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模块
>> import evaltime
100 # evalsupport 开始
400 # 类的定义体会运行
700 # evalsupport 结束
1 #evaltime 开始
2 # ClassOne 定义体
6 # ClassTwo 嵌套类定义体也会执行
7 # ClassThree 定义体
200 # 先计算被装饰的类的定义体,然后运行装饰器函数
9 # ClassFour 定义体
14 evaltime 结束
- 在命令行中运行evaltime.py模块
> python evaltime.py
100 # evalsupport 开始
400 # MetaAleph 定义体
700 # evalsupport 结束
1 #evaltime 开始
2 # ClassOne 定义体
6 # ClassTwo 嵌套类定义体也会执行
7 # ClassThree 定义体
200 # 先计算被装饰的类的定义体,然后运行装饰器函数
9 # ClassFour 定义体
# 以上和import 导入相同
11 #
3 # 运行main中的,ClassOne.__init__执行
5 # ClassOne.method_x方法运行
12 # 运行带类装饰器的
300 # ClassThree.method_y方法被装饰器修改了,运行deco_alpha的inner_1函数的定义体
13 # 运行继承被装饰的类ClassThree
10 # 继承的子类的ClassFour.method_y不被装饰器起作用,运行ClassFour.method_y定义体
14 # evaltime 结束
4 # 程序结束,运行ClassOne.__del__ 垃圾回收
元类
元类是制造类的工厂
默认情况下,Python中的类是type类的实例。type是大多数内置类和用户自定义类的元类。
'spam'.__class__
str.__class__
LineItem.__class__
type.__class__ # type是自身的实例
- 元类从type类继承了构造类的能力
所有类都是type的实例,但是元类还是type的子类。
import collections
collections.Iterable.__class__
import abc
abc.ABCMeta.__class__
abc.ABCMeta.__mro__
元类执行顺序
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_z')
class ClassSix(ClassFive):
print('<[9]> ClassSix body')
def method_z(self):
print('<[10]> ClassSix.method_z')
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()
print('<[15]> evaltime_meta module end')
- 在控制台导入evaltime_meta模块
>>> import evaltime_meta
<[100]> evalsupport module start # evalsupport 开始
<[400]> MetaAleph body # MetaAleph 定义体
<[700]> evalsupport module end # evalsupport 结束
<[1]> evaltime_meta module start # evaltime_meta 开始
<[2]> ClassThree body # ClassThree 定义体
<[200]> deco_alpha # 执行类装饰器
<[4]> ClassFour body # ClassFour 定义体
<[6]> ClassFive body # ClassFive 定义体 继承MetaAleph类
<[500]> MetaAleph.__init__:inner_2 # method_z方法被装饰器修改为inner_2
<[9]> ClassSix body # ClassSix 定义体 继承ClassFive类
<[500]> MetaAleph.__init__:inner_2 # method_z方法被装饰器修改为inner_2
<[15]> evaltime_meta module end # evaltime_meta 结束
- 在命令行运行evaltime_meta.py模块
D:\PythonProject\Learning_python\fluent_python\code>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__:inner_2
<[9]> ClassSix body
<[500]> MetaAleph.__init__:inner_2
<[11]> ClassThree tests .............................. # 执行被类装饰器的类
<[300]> deco_alpha:inner_1 # method_z方法被替换成inner_1方法
<[12]> ClassFour tests .............................. # 继承ClassThree
<[5]> ClassFour.method_y # 不受装饰器的影响
<[13]> Classfive tests .............................. # 继承MetaAleph元类
<[7]> ClassFive.__init__
<[600]> MetaAleph.__init__:inner_2 # MetaAleph类的__init__方法把method_z方法替换成inner_2函数
<[14]> ClassSix tests .............................. # 继承ClassFive
<[7]> ClassFive.__init__
<[600]> MetaAleph.__init__:inner_2 # 与父类相同,method_z方法被替换成inner_2函数
<[15]> evaltime_meta module end # evaltime_meta 结束
定制描述符的元类
- prepare 使字段和属性按顺序映射
class EntityMeta(type):
"""元类"""
@classmethod
def __prepare__(cls, name, bases):
return collections.OrderedDict()
def __init__(cls, name, bases, attr_dict):
super().__init__(name, bases, attr_dict)
cls._field_names = []
for key, attr in attr_dict.items():
if isinstance(attr, Validated):
type_name = type(attr).__name__
attr.storage_name = '_{}#{}'.format(type_name, key)
cls._field_names.append(key)
class Entity(metaclass=EntityMeta):
"""实体"""
@classmethod
def field_names(cls):
for name in cls._field_names:
yield name
- 继承元类,无需关心机体实现
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)
类作对象
- 类属性
LineItem.__mro__
LineItem.__class__
LineItem.__name__
LineItem.__bases__ # 类的基类
LineItem.__qualname__ # 限定名称
Entity.__subclass__() # 直接子类
Entity.mro() # 超类元组
dir(LineItem)
总结
- type当类使用时,传入三个参数可以新建一个类,name、bases和dict,类名、基类和属性名:值
- 元类在运行时创建或定制类
- 类工厂,以及类装饰器
- 类和函数,在导入时和运行时的执行顺序
- 类属性
- 最后一章了
流畅的Python