为什么 metaclass 定义改变了 classic class 的 mro?
Why the metaclass definition changed the mro of classic class?
Classic classes used a simple MRO scheme: when looking up a method,
base classes were searched using a simple depth-first left-to-right
scheme.
这可以在Python 2.6
中验证
In [1]: import sys
In [2]: sys.version
Out[2]: '2.6.6 (r266:84292, Jul 23 2015, 15:22:56) \n[GCC 4.4.7 20120313 (Red Hat 4.4.7-11)]'
In [3]:
class D:
def f(self): return 'D'
class B(D): pass
class C(D):
def f(self): return 'C'
class A(B, C): pass
...:
In [4]: A().f()
Out[4]: 'D'
但是,如果我定义了一个元类,我在 Python 2.7.12 中得到了不同的结果:
Python 2.7.12 (default, Nov 19 2016, 06:48:10)
Type "copyright", "credits" or "license" for more information.
IPython 5.4.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]: class D: # Note: Old-style
...: def f(self): return "D.f()"
...: class B(D): pass
...: class C(D):
...: def f(self): return "C.f()"
...: class A(B, C): pass
...:
In [2]: A().f()
Out[2]: 'D.f()' # It works as expected.
In [3]: class __metaclass__(type):
...: "All classes are metamagically modified to be nicely printed"
...: __repr__ = lambda cls: cls.__name__
...:
In [4]: class D: # Note: Old-style
...: def f(self): return "D.f()"
...: class B(D): pass
...: class C(D):
...: def f(self): return "C.f()"
...: class A(B, C): pass
...:
In [5]: A().f()
Out[5]: 'C.f()' # WTF??
Metaclasses 仅适用于新式 classes,并且通过在 class 正文中添加名为 __metaclass__
的字段来添加。拥有 __metaclass__
字段会导致 class 成为新样式 class 无论您是否继承自 object
:
class J:
pass
class K(object):
pass
class L:
__metaclass__ = type
type(J) # <type 'classobj'>
type(K) # <type 'type'>
type(L) # <type 'type'>
所以你的例子中发生的事情似乎是由于 lexical/static 范围规则,当 class 的主体时,你的名为 __metaclass__
的元 class 在范围内D
第二次执行。由于 Python 在执行正文时在范围内看到 __metaclass__
,因此它将其用作 D
的元 class 并将其转换为新样式 class, 这改变了方法解析顺序。
您可能不应该命名元class __metaclass__
。将其命名为 PrettyPrintClassRepr
之类的描述性名称,并在需要时通过在 class 正文中使用 __metaclass__ = PrettyPrintClassRepr
将其应用于 classes:
class D:
__metaclass__ = PrettyPrintClassRepr
Classic classes used a simple MRO scheme: when looking up a method, base classes were searched using a simple depth-first left-to-right scheme.
这可以在Python 2.6
中验证In [1]: import sys
In [2]: sys.version
Out[2]: '2.6.6 (r266:84292, Jul 23 2015, 15:22:56) \n[GCC 4.4.7 20120313 (Red Hat 4.4.7-11)]'
In [3]:
class D:
def f(self): return 'D'
class B(D): pass
class C(D):
def f(self): return 'C'
class A(B, C): pass
...:
In [4]: A().f()
Out[4]: 'D'
但是,如果我定义了一个元类,我在 Python 2.7.12 中得到了不同的结果:
Python 2.7.12 (default, Nov 19 2016, 06:48:10)
Type "copyright", "credits" or "license" for more information.
IPython 5.4.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]: class D: # Note: Old-style
...: def f(self): return "D.f()"
...: class B(D): pass
...: class C(D):
...: def f(self): return "C.f()"
...: class A(B, C): pass
...:
In [2]: A().f()
Out[2]: 'D.f()' # It works as expected.
In [3]: class __metaclass__(type):
...: "All classes are metamagically modified to be nicely printed"
...: __repr__ = lambda cls: cls.__name__
...:
In [4]: class D: # Note: Old-style
...: def f(self): return "D.f()"
...: class B(D): pass
...: class C(D):
...: def f(self): return "C.f()"
...: class A(B, C): pass
...:
In [5]: A().f()
Out[5]: 'C.f()' # WTF??
Metaclasses 仅适用于新式 classes,并且通过在 class 正文中添加名为 __metaclass__
的字段来添加。拥有 __metaclass__
字段会导致 class 成为新样式 class 无论您是否继承自 object
:
class J:
pass
class K(object):
pass
class L:
__metaclass__ = type
type(J) # <type 'classobj'>
type(K) # <type 'type'>
type(L) # <type 'type'>
所以你的例子中发生的事情似乎是由于 lexical/static 范围规则,当 class 的主体时,你的名为 __metaclass__
的元 class 在范围内D
第二次执行。由于 Python 在执行正文时在范围内看到 __metaclass__
,因此它将其用作 D
的元 class 并将其转换为新样式 class, 这改变了方法解析顺序。
您可能不应该命名元class __metaclass__
。将其命名为 PrettyPrintClassRepr
之类的描述性名称,并在需要时通过在 class 正文中使用 __metaclass__ = PrettyPrintClassRepr
将其应用于 classes:
class D:
__metaclass__ = PrettyPrintClassRepr