关于你的第一个问题,元类应该是MyMetaclass
(事情是这样的):
In [7]: print(type(MyClass), type(MyDerived))
<class '__main__.MyMetaclass'> <class '__main__.MyMetaclass'>
原因是,如果元类不是类型的实例,python 通过将这些参数传递给它来调用元类name, bases, ns, **kwds
(see new_class
)并且由于您在该函数中返回真实的元类,因此它获得了元类的正确类型。
关于第二个问题:
接受任意可调用的目的是什么?
没有什么特别的目的,这实际上是元类的本质这是因为从类创建实例总是通过调用它来调用元类__call__
method:
Metaclass.__call__()
这意味着您可以将任何可调用对象作为元类传递。因此,例如,如果您使用嵌套函数测试它,结果仍然是相同的:
In [21]: def metaclass_callable(name, bases, namespace):
def inner():
return MyMetaclass(name, bases, namespace)
return inner()
....:
In [22]: class MyClass(metaclass=metaclass_callable):
pass
....:
In [23]: print(type(MyClass), type(MyDerived))
<class '__main__.MyMetaclass'> <class '__main__.MyMetaclass'>
有关更多信息,请参阅 Python 如何创建类:
它称为new_class
它调用的函数prepare_class
在其内部,然后正如您在内部所看到的prepare_class
python 调用__prepare__
适当的元类的方法,除了找到适当的元(使用_calculate_meta
function ) 并为类创建适当的命名空间。
因此,这里的全部内容就是执行元类方法的层次结构:
-
__prepare__
1
__call__
__new__
__init__
这是源代码:
# Provide a PEP 3115 compliant mechanism for class creation
def new_class(name, bases=(), kwds=None, exec_body=None):
"""Create a class object dynamically using the appropriate metaclass."""
meta, ns, kwds = prepare_class(name, bases, kwds)
if exec_body is not None:
exec_body(ns)
return meta(name, bases, ns, **kwds)
def prepare_class(name, bases=(), kwds=None):
"""Call the __prepare__ method of the appropriate metaclass.
Returns (metaclass, namespace, kwds) as a 3-tuple
*metaclass* is the appropriate metaclass
*namespace* is the prepared class namespace
*kwds* is an updated copy of the passed in kwds argument with any
'metaclass' entry removed. If no kwds argument is passed in, this will
be an empty dict.
"""
if kwds is None:
kwds = {}
else:
kwds = dict(kwds) # Don't alter the provided mapping
if 'metaclass' in kwds:
meta = kwds.pop('metaclass')
else:
if bases:
meta = type(bases[0])
else:
meta = type
if isinstance(meta, type):
# when meta is a type, we first determine the most-derived metaclass
# instead of invoking the initial candidate directly
meta = _calculate_meta(meta, bases)
if hasattr(meta, '__prepare__'):
ns = meta.__prepare__(name, bases, **kwds)
else:
ns = {}
return meta, ns, kwds
def _calculate_meta(meta, bases):
"""Calculate the most derived metaclass."""
winner = meta
for base in bases:
base_meta = type(base)
if issubclass(winner, base_meta):
continue
if issubclass(base_meta, winner):
winner = base_meta
continue
# else:
raise TypeError("metaclass conflict: "
"the metaclass of a derived class "
"must be a (non-strict) subclass "
"of the metaclasses of all its bases")
return winner
1. Note that it get called implicitly inside the new_class function and before the return.