通过定义__call__
,可以调用可调用对象。一个类应该是一个对象......或者至少有一些例外。这个例外是我未能正式澄清的,因此这个问题发布在这里。
让A
成为一个简单的类:
class A(object):
def call(*args):
return "In `call`"
def __call__(*args):
return "In `__call__`"
第一个功能是故意命名为“call”,以明确目的是与另一个进行比较。
让我们实例化它并忘记它暗示的表达式:
a = A() # Think of it as `a = magic` and forget about `A()`
现在值得:
print(A.call())
print(a.call())
print(A())
print(a())
结果:
>>> In `call`
>>> In `call`
>>> <__main__.A object at 0xNNNNNNNN>
>>> In `__call__`
输出(第三个语句没有运行__call__
)并不令人感到意外,但是当我认为每个地方都说“Python类是对象”时......
这更明确,但运行__call__
print(A.__call__())
print(a.__call__())
>>> “In `__call__`”
>>> “In `__call__`”
所有这一切只是为了表明最终A()
看起来有多奇怪。
Python规则中有例外,但the documentation about “object.call”对__call__
没有太多说明......不超过:{/ p>
3.3.5。模拟可调用对象
object.__call__(self[, args...])
当实例被“调用”为函数时调用; [...]
但是,Python如何告诉“它被称为函数”并尊重或不尊重object.__call__
规则?
这可能是一个类型问题,但即使是类型也有object
作为基类。
我在哪里可以了解更多(并且正式)有关它的信息?
顺便问一下,Python2和Python3之间有什么不同吗?
-----%&lt; -----编辑-----&gt;%-----
在@ Veedrac的回答和@Chepner的评论之后,我进入了另一个测试,完成了两者的评论:
class M(type):
def __call__(*args):
return "In `M.__call__`"
class A(object, metaclass=M):
def call(*args):
return "In `call`"
def __call__(*args):
return "In `A.__call__`"
print(A())
结果是:
>>> In `M.__call__`
所以似乎是驱动“呼叫”操作的元类。如果我理解正确的话,元类只与类有关,也与类实例无关。
另一个相关测试,它表明这不是重要对象的属性,而是对象类型的属性:
class A(object):
def __call__(*args):
return "In `A.__call__`"
def call2(*args):
return "In `call2`"
a = A()
print(a())
正如所料,它打印:
>>> In `A.__call__`
现在这个:
a.__call__ = call2
print(a())
打印:
>>> In `A.__call__`
在分配属性之前的相同a。它不会打印In call2
,它仍然是In A.__call__
。这一点很重要,也可以解释为什么这是调用的元类的__call__
(请记住,元类是类对象的类型)。用于调用函数的__call__
不是来自对象,而是来自其类型。
答案 0 :(得分:5)
x(*args, **kwargs)
与type(x).__call__(x, *args, **kwargs)
相同。
所以你有
>>> type(A).__call__(A)
<__main__.A object at 0x7f4d88245b50>
这一切都有道理。
chepner在type(A) == type
的评论中指出。这有点奇怪,因为type(A)(A)
只会再次发出type
!但请记住,我们使用的是type(A).__call__(A)
不一样。
所以这解析为type.__call__(A)
。这是类的构造函数,它构建数据结构并完成所有构造魔法。
大多数dunder(双下划线)方法也是如此,例如__eq__
。在这些情况下,这部分是优化。