__call__实际上如何运作?

时间:2015-09-30 00:35:00

标签: python python-internals

每当您尝试调用对象时,都会调用Python的魔术方法__call__。因此Cls()()等于Cls.__call__(Cls())

函数是Python中的第一类对象,这意味着它们只是可调用对象(使用__call__)。但是,__call__本身就是一个函数,因此它也有__call__,它也有自己的__call__,它也有自己的__call__

因此,Cls.__call__(Cls())等于Cls.__call__.__call__(Cls()),并且等同于Cls.__call__.__call__.__call__(Cls()),依此类推。依此类推。

这个无限循环如何结束? __call__如何实际执行代码?

3 个答案:

答案 0 :(得分:9)

在幕后,Python中的所有调用都使用相同的机制,几乎所有调用都在CPython实现中达到相同的C函数。无论对象是具有__call__方法的类的实例,函数(本身是对象)还是内置对象,所有调用(优化的特殊情况除外)都会到达函数PyObject_Call。该C函数从对象的ob_type结构的PyObject字段获取对象的类型,然后从类型(另一个PyObject结构)获取{ {1}}字段,它是一个函数指针。如果tp_call不是tp_call,则会通过调用,其中args和kwargs结构也会传递给NULL

当一个类定义PyObject_Call方法时,它会相应地设置__call__字段。

这是一篇详细解释所有这些内容的文章:Python internals: How callables work。它甚至列出并解释了整个tp_call函数,它并不是很大。如果你想在其原生栖息地中看到该功能,它会在CPython回购中的Objects/abstract.c中。

这个stackoverflow Q& A:What is a "callable" in Python?也是相关的。

答案 1 :(得分:1)

没有实际的无限循环,因为__call__方法实际上并未针对所有这些情况调用("称为#34;)。只有在提供__call__方法的对象上有类似函数的调用时,才会直接调用它。

普通类实例化Cls(...)和常规功能调用f()是直接处理的已知案例。通常没有__call__()的实际调用,因此即使在具有深度继承,元类等的复杂情况下,也可能发生有限数量的__call__方法调用。

因为关于概念无限循环的短路是否真的发生存在争议,让我们看一下反汇编的字节码。请考虑以下代码:

def f(x):
    return x + 1

class Adder(object):
    def something(self, x):
        return x + 19
    def __call__(self, x):
        return x + 1

def lotsacalls(y):
    u = f(1)
    a = Adder()
    z = u + a.something(y)
    return a(z * 10)

对不起,它有点复杂,因为我想展示几个短路实例 - 即正常def函数,__init__调用,普通方法和{{1}特殊方法。现在:

annotated disassembly

所以,如果Python确实是真正的,那么这里有很多次#34;走在树上"在概念__call__调用中,它将引用__call__(以及可能的Function类,并调用它们的Method方法。它没有。它在所有情况下都使用简单的字节码__call__,使概念树向下走线短路。 逻辑上你可以想象有一个类CALL_FUNCTION有一个Function方法,当一个函数(即__call__的一个实例被调用时类)被称为。但它并没有真正发挥作用。编译器,字节码解释器和C语言基础的其他部分不实际走元类树。他们像疯了一样短路。

答案 2 :(得分:0)

我没有查看任何文档,但是从我的测试中看来__call__并不总是被调用:

def func1(*args, **kargs):
    print "func1 called", args, kargs

def func2(*args, **kargs):
    print "func2 called", args, kargs

func1.__call__ = func2

func1() # here is still called func1

class Cls:
    def __init__(*args, **kargs):
        print "init called", args, kargs
    def __call__(*args, **kargs):
        print "object called", args, kargs

obj = Cls() # here is actually called __init__
obj()  # here is called __call__

打印

func1 called () {}
init called (<__main__.Cls instance at 0x0000000002A5ED88>,) {}
object called (<__main__.Cls instance at 0x0000000002A5ED88>,) {}