
正文
Python 元类 - Metaclasses
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
Python 元类 - Metaclasses
默认情况下儿, classes 是有 type() 构造的.
类的结构体在一个新的 namespace 被执行, 类的名字 class name 绑定(bound locally)到
type(name, bases, namespace) 的结果上.
然而, 类的构造过程可以用户定义 - 在定义类的时候通过传入一个 metaclass 关键字;
或者通过继承至一个有 metaclass 关键字的父类.
如,
class Meta(type):
pass class MyClass(metaclass=Meta):
pass
or
class MySubclass(MyClass):
pass 在 class 定义中的其他关键字参数会被传给所有 metaclass 的操作.
当 class 构造体被执行的时候,python 解释器按照下列顺序 interpretation,
确定 class 对应的 metaclass ,
准备 class 的 namespace,
执行 class 定义体 - class body,
创建 class 对象 - class object. 下面我们逐一详细看一下儿以上几个解释步骤,
metaclass 的确定 - Determining the appropriate metaclass
metaclass 确定原则, 以 class B(metaclass = A, C) 为例阐述,
如果 class 的定义中没有指定基类 bases, 并且没有 metaclass 关键字,采用 type() 作为 metaclass.
如果有关键字 metaclass = A, 并且 A 不是 type() 的实例, 直接采用 A 作为 metaclass.
如果 A 是 type() 一个实例 instance, 或者有定义基类 bases, 则 采用 the most derived metaclass 注,
candidate metaclasses 有2部分原来:
a, metaclass 关键字, 如上例子中的 A
b, 所有基类 bases的 metaclasses, 如上例子中的 type(c)
the most derived metaclass 是 candidate metaclasses 集合中的一个,
它需要满足是所有其他的 candidate 的 subtype,如果没有任何一个 candidate 满足这个条件,
则 class 的定义会报错,raise TypeError exception. namespace 的确定 - Preparing the class namespace
metaclass 确定了之后, 到了该确定 namespace 的时候.
如果 metaclass 有 __prepare__ attribute, 下面的语句将被调用,以确定 namespace
namespace = metaclass.__prepare__(name, bases, **kwds),
**kwds 来至于 class 的定义语句.
若 metaclass 没有 __prepare__ attribute, 则 类的 namespace 被初始化为空 an empty ordered mapping. class body 的执行 - Executing the class body
class body 的执行, 可以用如下伪代码来表示,
exec(body, globals(), namespace) 跟一般的对 exec()的调用的关键区别在于, 当 class 定义在一个函数中时候,
允许类的定义体 class body (包括其中的方法) 引用 '当前' 和 '外层' 作用域 scope 的 names
"The key difference from a normal call to exec() is that lexical scoping allows
the class body (including any methods) to reference names from the current and
outer scopes when the class definition occurs inside a function."
然而,及时 class 被定义在一个函数中, 在 class scope 所定义的 names 对 class 中定义的 methods
仍然不可见.
对类变量 Class variables 的访问, 必须通过 self.variable 的形式访问,或作为第一个参数传入.
即,
class A(object):
a = "variable - a" def func1(self): # 必须通过 self.variable 的形式访问 Class variables
print(self.a) def func2(self, a): # 作为第一个参数传入的形式访问 Class variables
print(a) 再或者通过 隐式词法作用于 implicit lexically scoped __class__ 引用, 见下一节描述. class object 的创建 - Creating the class object
class namespace 被确定之后, class object 由下面方法创建,
metaclass(name, bases, namespace, **kwds)
**kwds 跟传给 __prepare__ 的 **kwds, 即来至于 class 的定义语句. class object 即 super().__class__, 是一个在 class boby 中存在引用 __class__ 或 super 的方法
的情况下儿被编译器 compiler 创建一个隐式闭包引用 an implicit closure reference.
这保证了通过把 class 或者 instance 作为第一个参数来调用(属性/方法)的时候,无参数的 super()
可以正确的识别对应的 class, 即 class body 中的 self 机制. CPython implementation detail:
从 python 3.6 开始, __class__ 被传给 metaclass 作为 __classcell__ 存储在类的 namespace 中.
如果 __class__ 存在, 需要向上反推到 type.__new__ 的调用,以保证 class 的正确初始化.
如果 type.__new__ 调用失败,将报告 DeprecationWarning, 之后报 RuntimeWarning (python 3.6).
当采用默认 metaclass, 或某一 metaclass 调用了 type.__new__, 在创建了 class object 下面额外
的步骤姜维调用,
a, type.__new__ 收集 class namespace 中的所有 descriptors, 定义 __set_name__() 方法
b, 在所定义的类上调用 __set_name__ 中的描述符方法,
class being defined and the assigned name of that particular descriptor 作为参数.
c, 在被定义 class 的最近的基类上(MRO)调用 __init_subclass__(),
当 class object 被创建后, 如果 class 存在 decorators 将 class object 传给 decorators 装饰.
将返回的新对象绑定到 local namespace.
当一个新的 class 是由 type.__new__ 创建的, namespace parameter 复制一个新的 ordered mapping
中, 源对象被弃用. 新的 ordered mapping 将被包装成 read-only proxy, 最终成为 class object 的
__dict__ 属性. 最后来看例子 - Metaclass example
metaclasses 的潜在用途有很多, 包括 enum, logging, interface checking, automatic delegation,
automatic property creation, proxies, frameworks, and automatic resource locking/synchronization. 下面是一个通过 collections.OrderedDict 来记录类参数定义顺序的 metaclasses 应用的例子,
import collections
class OrderedClass(type): @classmethod
def __prepare__(metacls, name, bases, **kwds):
return collections.OrderedDict() def __new__(cls, name, bases, namespace, **kwds):
result = type.__new__(cls, name, bases, dict(namespace))
result.members = tuple(namespace)
return result class A(metaclass=OrderedClass):
A = 1
C = 2
B = 3
def one(self): pass
def two(self): pass
def three(self): pass
def four(self): pass
Output,
>>> A.members
('__module__', '__qualname__', 'A', 'C', 'B', 'one', 'two', 'three', 'four') 当类 A 的定义体被执行的时候, 先调用 metaclass 的 __prepare__ 方法, __prepare__()
返回一个空的 collections.OrderedDict. 这个 OrderedDict 被用来按声明顺序记录 A 中
定义的 methods and attributes. 当解释器解释到 attributes 被声明的位置时, 通过执行声明
将 attributes 被添加到 ordered dictionary. 并且调用 metaclass 的 __new__() method 被,
结果就是 __new__() 创建一个新的的 type 并将 ordered dictionary 的 keys 存储在 members
属性中. Reference,
python doc,
https://docs.python.org/3/reference/datamodel.html#metaclasses







