Python 3 documentation清楚地描述了如何确定类的元类:
- 如果没有给出基数且没有给出明确的元类,则使用类型()
- 如果给出了显式元类并且它不是type()的实例,那么它将直接用作元类
- 如果给出了类型()的实例作为显式元类,或者定义了基数,则使用最派生的元类
因此,根据第二条规则,可以使用callable指定元类。如,
class MyMetaclass(type):
pass
def metaclass_callable(name, bases, namespace):
print("Called with", name)
return MyMetaclass(name, bases, namespace)
class MyClass(metaclass=metaclass_callable):
pass
class MyDerived(MyClass):
pass
print(type(MyClass), type(MyDerived))
是MyClass
的元类:metaclass_callable
还是MyMetaclass
?文档中的第二条规则说,提供的可调用“直接用作元类”。但是,从
MyMetaclass
似乎更有意义
MyClass
和MyDerived
的类型为MyMetaclass
,metaclass_callable
被调用一次,然后似乎无法恢复,metaclass_callable
(他们使用MyMetaclass
)。对于type
的实例,您无法对callable执行任何操作吗?接受任意可调用的目的是什么?
答案 0 :(得分:7)
关于你的第一个问题,元类应该是MyMetaclass
(它是这样的):
In [7]: print(type(MyClass), type(MyDerived))
<class '__main__.MyMetaclass'> <class '__main__.MyMetaclass'>
原因是,如果元类不是python类型的实例,则通过将这些参数传递给它name, bases, ns, **kwds
来调用methaclass(参见new_class
),因为你要在该函数中返回真正的元类它获得了元类的正确类型。
关于第二个问题:
接受任意可调用的目的是什么?
没有特殊目的,它实际上是元类的本质,这是因为从类中创建实例总是通过调用它来调用元类{{1方法:
__call__
这意味着你可以传递任何可调用的元类作为你的元类。因此,例如,如果使用嵌套函数对其进行测试,结果仍将是相同的:
Metaclass.__call__()
有关更多信息,请参阅Python如何创建类:
它调用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'>
函数,它在其自身内部调用new_class
,然后正如您在prepare_class
python中看到的那样调用相应元类的prepare_class
方法,旁边找到合适的元(使用__prepare__
函数)并为类创建适当的命名空间。
所以这里所有的都是执行metacalss方法的层次结构:
_calculate_meta
1 __prepare__
__call__
__new__
这是源代码:
__init__
<子> 1.请注意,它会在 new_class 函数内部以及返回之前隐式调用。 子>
答案 1 :(得分:2)
嗯,type
当然是MyMetaClass
。 metaclass_callable
最初被选中&#39;}作为自it's been specified in the metaclass
kwarg以来的元类,因此,它将执行__call__
(一个简单的函数调用)。
恰好调用它print
,然后调用MyMetaClass.__call__
(因为type.__call__
__call__
已被覆盖MyMetaClass
而调用MyMetaClass
})。 There the assignment of cls.__class__
is made到metaclass_callable
。
MyMetaClass
被调用一次然后似乎无法恢复
是的,它最初只是被调用,然后手动控制到metaclass_callable
。我不知道任何保留该信息的类属性。
派生类不以任何方式使用(据我所知)
metaclass
。
不,如果没有明确定义MyClass
,则会使用the best match for the metaclasses of bases
(此处为MyMetaClass
)(生成2
)。
对于问题__call__
,通过使用相应覆盖answer == 10 // YES - It'll print "You are correct!"
answer == 9 // No - Skip to the next If block
answer == 11? // No - skip to the else and print "You are wrong! Try again!"
的类型实例,可以确保使用可调用的所有操作。至于为什么,如果您只是想在实际创建类时进行微小的更改,则可能不想进行全面的类创建。
答案 2 :(得分:2)
关于问题1,我认为课程cls
的“元类”应理解为type(cls)
。这种理解方式与以下示例中的Python错误消息兼容:
>>> class Meta1(type): pass
...
>>> class Meta2(type): pass
...
>>> def metafunc(name, bases, methods):
... if methods.get('version') == 1:
... return Meta1(name, bases, methods)
... return Meta2(name, bases, methods)
...
>>> class C1:
... __metaclass__ = metafunc
... version = 1
...
>>> class C2:
... __metaclass__ = metafunc
... version = 2
...
>>> type(C1)
<class '__main__.Meta1'>
>>> type(C2)
<class '__main__.Meta2'>
>>> class C3(C1,C2): pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
即,根据错误消息,类的元类是一个类,即使用于构造类的可调用类也可以是任何类。
关于第二个问题,实际上使用类型的子类作为元类,您可以像使用任何其他可调用类一样。特别是,它可能会产生一些不是它的实例:
>>> class Mockup(type):
... def __new__(cls, name, bases, methods):
... return Meta1(name, bases, methods)
...
>>> class Foo:
... __metaclass__ = Mockup
...
>>> type(Foo)
<class '__main__.Meta1'>
>>> isinstance(Foo, Mockup)
False
>>> Foo.__metaclass__
<class '__main__.Mockup'>
至于为什么Python允许使用任何可调用的自由:前面的例子表明,callable是否是一个类型实际上是无关紧要的。
顺便说一下,这是一个有趣的例子:可以编写元类,它们本身具有与type
不同的元类 - 让我们称之为元类。 metametaclass实现调用元类时发生的事情。通过这种方式,可以创建一个具有两个基类的类,其中元类是不是彼此的子类(与上面示例中的Python错误消息相比!)。实际上,只有结果类的元类是基类的元类的子类,并且这个元类是动态创建的:
>>> class MetaMeta(type):
... def __call__(mcls, name, bases, methods):
... metabases = set(type(X) for X in bases)
... metabases.add(mcls)
... if len(metabases) > 1:
... mcls = type(''.join([X.__name__ for X in metabases]), tuple(metabases), {})
... return mcls.__new__(mcls, name, bases, methods)
...
>>> class Meta1(type):
... __metaclass__ = MetaMeta
...
>>> class Meta2(type):
... __metaclass__ = MetaMeta
...
>>> class C1:
... __metaclass__ = Meta1
...
>>> class C2:
... __metaclass__ = Meta2
...
>>> type(C1)
<class '__main__.Meta1'>
>>> type(C2)
<class '__main__.Meta2'>
>>> class C3(C1,C2): pass
...
>>> type(C3)
<class '__main__.Meta1Meta2'>
没什么好玩的:前面的例子在Python 3中不起作用。如果我理解正确,Python 2会创建类并检查它的元类是否是其所有基类的子类,而Python 3 首先< / em>检查是否有一个基类,其元类是所有其他基类的元类的超类,只有然后创建新类。从我的角度来看,这是一个回归。但这将成为我即将发布的新问题的主题......
修改:新问题为here