Click to Visit Homepage : zzyzz.top


Python 元类 - Metaclasses

  1 Python 元类 - Metaclasses
  2 
  3 默认情况下儿, classes 是有 type() 构造的.
  4 类的结构体在一个新的 namespace 被执行, 类的名字 class name 绑定(bound locally)到 
  5 type(name, bases, namespace) 的结果上.
  6 然而, 类的构造过程可以用户定义 - 在定义类的时候通过传入一个 metaclass 关键字;
  7 或者通过继承至一个有 metaclass 关键字的父类.
  8     如,
  9             class Meta(type):
 10                  pass
 11     
 12             class MyClass(metaclass=Meta):
 13                 pass
 14         or
 15             class MySubclass(MyClass):
 16                 pass
 17     
 18class 定义中的其他关键字参数会被传给所有 metaclass 的操作.
 19class 构造体被执行的时候,python 解释器按照下列顺序 interpretation,
 20         确定 class 对应的 metaclass ,
 21         准备 class 的 namespace,
 22         执行 class 定义体 - class body,
 23         创建 class 对象 - class object.
 24         
 25 
 26 下面我们逐一详细看一下儿以上几个解释步骤,
 27     metaclass 的确定 - Determining the appropriate metaclass
 28         metaclass 确定原则, 以 class B(metaclass = A, C) 为例阐述,
 29             如果 class 的定义中没有指定基类 bases, 并且没有 metaclass 关键字,采用 type() 作为 metaclass.
 30             如果有关键字 metaclass = A, 并且 A 不是 type() 的实例, 直接采用 A 作为 metaclass.
 31             如果 A 是 type() 一个实例 instance, 或者有定义基类 bases, 则 采用 the most derived metaclass  
 32             
 33             注,
 34                 candidate metaclasses 有2部分原来: 
 35                     a, metaclass 关键字, 如上例子中的 A 
 36                     b, 所有基类 bases的 metaclasses, 如上例子中的 type(c)  
 37                 the most derived metaclass 是 candidate metaclasses 集合中的一个, 
 38                 它需要满足是所有其他的 candidate 的 subtype,如果没有任何一个 candidate 满足这个条件,
 39class 的定义会报错,raise  TypeError  exception.
 40     
 41     namespace 的确定 - Preparing the class namespace
 42         metaclass 确定了之后, 到了该确定 namespace 的时候.
 43         如果 metaclass 有 __prepare__ attribute, 下面的语句将被调用,以确定 namespace
 44             namespace = metaclass.__prepare__(name, bases, **kwds), 
 45                 **kwds 来至于 class 的定义语句.   
 46         若 metaclass 没有 __prepare__ attribute, 则 类的 namespace 被初始化为空 an empty ordered mapping.
 47     
 48     class body 的执行 - Executing the class body
 49         class body 的执行, 可以用如下伪代码来表示,
 50             exec(body, globals(), namespace)
 51         
 52         跟一般的对 exec()的调用的关键区别在于, 当 class 定义在一个函数中时候,
 53         允许类的定义体 class body (包括其中的方法) 引用 '当前''外层' 作用域 scope 的 names
 54            "The key difference from a normal call to exec() is that lexical scoping allows 
 55             the class body (including any methods) to reference names from the current and
 56             outer scopes when the class definition occurs inside a function."
 57         然而,及时 class 被定义在一个函数中, 在 class scope 所定义的 names 对 class 中定义的 methods
 58         仍然不可见.
 59         对类变量 Class variables 的访问, 必须通过 self.variable 的形式访问,或作为第一个参数传入.
 60             即,
 61                 class A(object):
 62                     a =  "variable - a"
 63                     
 64                     def func1(self):         # 必须通过 self.variable 的形式访问 Class variables
 65                         print(self.a)
 66                     
 67                     def func2(self, a):       # 作为第一个参数传入的形式访问 Class variables   
 68                         print(a)
 69             
 70             再或者通过 隐式词法作用于 implicit lexically scoped __class__ 引用, 见下一节描述.
 71     
 72     class object 的创建 - Creating the class object            
 73         class namespace 被确定之后, class object 由下面方法创建,
 74             metaclass(name, bases, namespace, **kwds)
 75                 **kwds  跟传给 __prepare__ 的 **kwds, 即来至于 class 的定义语句.
 76         
 77         class object 即 super().__class__, 是一个在 class boby 中存在引用 __class__ 或 super 的方法
 78         的情况下儿被编译器 compiler 创建一个隐式闭包引用 an implicit closure reference.
 79         这保证了通过把 class 或者 instance 作为第一个参数来调用(属性/方法)的时候,无参数的 super() 
 80         可以正确的识别对应的 class, 即 class body 中的 self 机制.
 81     
 82         CPython implementation detail:
 83             从 python 3.6 开始, __class__ 被传给 metaclass 作为 __classcell__ 存储在类的 namespace 中.
 84             如果 __class__ 存在, 需要向上反推到 type.__new__ 的调用,以保证 class 的正确初始化.
 85             如果 type.__new__ 调用失败,将报告 DeprecationWarning, 之后报 RuntimeWarning (python 3.6).
 86             当采用默认 metaclass, 或某一 metaclass 调用了 type.__new__, 在创建了 class object 下面额外
 87             的步骤姜维调用,
 88                 a, type.__new__ 收集 class namespace 中的所有 descriptors, 定义 __set_name__() 方法
 89                 b, 在所定义的类上调用 __set_name__ 中的描述符方法,
 90                    class being defined and the assigned name of that particular descriptor 作为参数.
 91                 c, 在被定义 class 的最近的基类上(MRO)调用 __init_subclass__(),
 92class object 被创建后, 如果 class 存在 decorators 将 class object 传给 decorators 装饰.
 93             将返回的新对象绑定到 local namespace. 
 94             当一个新的 class 是由 type.__new__ 创建的, namespace parameter 复制一个新的 ordered mapping 
 95             中, 源对象被弃用. 新的 ordered mapping 将被包装成 read-only proxy, 最终成为 class object 的
 96             __dict__ 属性.  
 97             
 98 最后来看例子 - Metaclass example
 99     metaclasses 的潜在用途有很多, 包括  enum, logging, interface checking, automatic delegation, 
100     automatic property creation, proxies, frameworks, and automatic resource locking/synchronization.
101     
102     下面是一个通过 collections.OrderedDict 来记录类参数定义顺序的 metaclasses 应用的例子, 
103         import collections
104         class OrderedClass(type):
105 
106             @classmethod
107             def __prepare__(metacls, name, bases, **kwds):
108                 return collections.OrderedDict()
109         
110             def __new__(cls, name, bases, namespace, **kwds):
111                 result = type.__new__(cls, name, bases, dict(namespace))
112                 result.members = tuple(namespace)
113                 return result
114         
115         class A(metaclass=OrderedClass):
116             A = 1
117             C = 2
118             B = 3
119             def one(self): pass
120             def two(self): pass
121             def three(self): pass
122             def four(self): pass
123     Output,
124         >>> A.members
125         ('__module__', '__qualname__', 'A', 'C', 'B', 'one', 'two', 'three', 'four')
126     
127     当类 A 的定义体被执行的时候, 先调用 metaclass 的 __prepare__ 方法, __prepare__() 
128     返回一个空的 collections.OrderedDict. 这个 OrderedDict 被用来按声明顺序记录 A 中
129     定义的 methods and attributes. 当解释器解释到 attributes 被声明的位置时, 通过执行声明
130     将 attributes 被添加到 ordered dictionary. 并且调用 metaclass 的 __new__() method 被, 
131     结果就是 __new__() 创建一个新的的 type 并将 ordered dictionary 的 keys 存储在 members
132     属性中.
133 
134 Reference,
135     python doc,
136         https://docs.python.org/3/reference/datamodel.html#metaclasses

 

posted @ 2017-10-31 21:11  zzYzz  阅读(317)  评论(0编辑  收藏  举报


Click to Visit Homepage : zzyzz.top