为了更好地理解元类,我一直在通读this excellent post,遇到了一些让我困惑的行为。你知道吗
设置
class AMeta(type):
# Disclaimer: this is not what a real-world metaclass __call__ should look like!
def __call__(self, *args):
print("AMeta.__call__, self = {}, args = {}".format(self, args))
class A(metaclass=AMeta):
def __call__(self, *args):
print("A.__call__, self = {}, args = {}".format(self, args))
问题1
A.__call__(1, 2)
打印A.__call__, self = 1, args = (2,)
。你知道吗
参数直接转发(1出现在self的位置)
因此,似乎没有触发任何描述器。基于these lookup rules这可能是因为AMeta.__call__
是一个非数据描述符(因此不是)
优先于A.__call__
),也可能是因为__call__
是一种神奇的方法,因此解释器会给它一些特殊的直接查找。你知道吗
我不确定这两个假设中哪一个是正确的,如果有的话。 如果有人知道答案,我将不胜感激!你知道吗
问题2A(1, 2)
打印AMeta.__call__, self = <class '__main__.A'>, args = (1, 2)
。你知道吗
元类的__call__
被调用,这与AMeta
的__call__
的portrayal here在创建A
实例时作为傀儡主控符是一致的。你知道吗
为什么元类的__call__
在这里被调用?当我调用A(1, 2)
构造函数样式时,什么查找规则在起作用?显然A(1, 2)
不仅仅是A.__call__(1, 2)
的语法糖。你知道吗
我见过很多其他的问题围绕着这些问题跳舞,但没有一个能直接回答它们。你知道吗
我正在使用/关注python3.7+。你知道吗
问题1:
尽管
__call__
确实是一个由解释器专门处理的神奇方法,但是只有在说A()
并隐式委托给__call__
方法时,才会触发这种特殊行为。说A.__call__
的执行方式与a.foo
完全相同,这意味着,正如您所说,AMeta.__call__
是一个非数据描述符,因此被A.__call__
的显式定义覆盖问题2:
与问题1不同,解释器对魔术方法的特殊处理在这里被调用。但是,包含元类会使事情变得有点复杂。测试用例大致相当于以下没有元类的代码:
我上面提到的魔术方法的特殊处理是,当试图使用
A(1, 2)
这样的语法隐式调用魔术方法时,Python会忽略直接在对象上设置的任何属性,而只关心在类型上设置的属性。你知道吗因此,
A(1, 2)
是type(A).__call__(A, 1, 2)
的糖分(除了忽略类型上的任何__getattr__
和__getattribute__
方法的事实)。这就解释了为什么要调用元类上的__call__
方法。你知道吗相关问题 更多 >
编程相关推荐