Python中类实例化时调用__call__方法的问题,为何有时会调用,有时却不会?

class MyType(type):

 def __init__(self, what, bases=None, dict=None):   
     print('call myType.__init__()')   #语句 1  
     super().__init__(what, bases, dict)   

def new(cls, name, bases, attrs):
print(“call MyType.new()”) #语句 2
return type.new(cls, name, bases, attrs)

def call(self, *args, **kwargs):
print(“MyType.call”)

class Foo(object, metaclass=MyType):

 def __init__(self, name=None):           
     self.name = name   
     print("Foo self.name=", self.name)   
 def __new__(cls, *args, **kwargs):   
     print("Foo.__new__ cls=", cls)   
     return(object.__new__(cls, *args, **kwargs))   
 def __call__(self, cls):   
     print("Foo.__call__ cls=", cls)   

if name == 'main':
print("---------test---------") #语句 3
obj=Foo() #语句 4

上面代码在 PYTHON3.6 中输出如下:
call MyType.new() #语句 2 的输出 call myType.init() #语句 1 的输出
---------test---------
MyType.call #语句 4 的输出

请问,为何语句 4 “ obj=Foo()” 会导致__call__方法的执行? 我看了这篇博文 python.jobbole.com/83747/ 博文的“ 5.call”小节写了这段话:“注:构造方法的执行是由创建对象触发的,即:对象=类名();而对于__call__方法的执行是由对象后加括号触发的,即:对象()或者类()()”

看了这段话后再结合上面这个程序,我就觉得有问题,obj=Foo()这个语句应该是博文中的 对象=类名() 这个形式,所以不应该调用 Foo.call()方法才对啊? 关于这个观点,我还有一个例子可以说明,比如下面这段代码和对应的输出,请注意语句 1 就是一个把对象实例化的语句,这个语句并没有调用类的__call__()方法,所以两个例子出现了矛盾,同样是类的实例化,上面的程序调用了类的 call 方法,下面的却没有:

class Deco:

def __init__(self,func): 
    self.func=func 
    print("__init__执行完毕。func=",self.func) 

def call(self,*arg,**arg2): print(“开始执行__call__。”) self.func(‘abc’) print(self,arg,arg2)

class MyCls():

[@Deco](/user/Deco) 
def myFunc(self): 
    print('this is my work arg is %s'%self) 

mycls=MyCls()
deco=Deco(mycls.myFunc)

代码输出如下: __init__执行完毕。func= <function MyCls.myFunc at 0x01C49AE0>

__init__执行完毕。func= <main.Deco object at 0x01C4B190>


Python中类实例化时调用__call__方法的问题,为何有时会调用,有时却不会?

9 回复

初学不要去关心 new 之类的黑魔法 先避开

装饰器也尽量避开不要折腾

装饰器只是个套娃语法糖,
具体工作的时候涉及到闭包还有描述器之类的黑魔法

等你比较熟了再回来弄这个


这个问题问得好,很多人在理解 __call____init__ 时都会混淆。

简单来说,你搞混了两个完全不同的东西:

  • __init__ 是在你创建一个类实例时调用的,比如 obj = MyClass()
  • __call__ 是在你像函数一样调用一个已经存在的实例时调用的,比如 obj()

看个例子就全明白了:

class MyClass:
    def __init__(self, value):
        print(f"__init__ 被调用,初始化实例。传入的值是:{value}")
        self.value = value

    def __call__(self, x):
        print(f"__call__ 被调用,实例被像函数一样调用。传入的参数是:{x}")
        return self.value + x

# 1. 实例化对象:这里只会调用 __init__
my_instance = MyClass(10)
# 输出:__init__ 被调用,初始化实例。传入的值是:10

# 2. 调用实例:这里才会调用 __call__
result = my_instance(5)
# 输出:__call__ 被调用,实例被像函数一样调用。传入的参数是:5
print(result) # 输出:15

所以,结论很直接:

  • “有时会调用”:发生在你写 my_instance() 的时候。
  • “有时不会调用”:发生在你写 MyClass() 的时候(这步只触发 __init__)。

__call__ 让类的实例变得“可调用”,这是实现装饰器类、函数对象或让实例行为像函数的关键机制。而 __init__ 只是构造器,负责把实例“造出来”。

总结:__init__是造对象,__call__是用对象。

你自己写的 metaclass 的__call__啊

注意到 Foo 中的 metaclass 了么,这里面涉及到元类,这个东西一时半会讲不清楚(限于自身水平原因),你可以去掉 metaclass 部分,打印一下 obj.class, 自己慢慢体会下

为何写了 metaclass 的__call__就会导致实例化时的调用呢,我也看过一些介绍 metaclass 的博文,但是没有针对这个问题作出解释

去掉 metaclass 的话,确实就不会发生调用 Foo 类的__call__方法了,但是就是不明白为何加上 metaclass 就会如此呢

https://stackoverflow.com/questions/6966772/using-the-call-method-of-a-metaclass-instead-of-new

感觉就是在做蠢事,metaclass 上定义了 call,导致根本没法通过()实例化了。

因为 class 是 metaclass 的实例 相当于你这个 Foo 已经是 MyType(…) 出来的 就有 MyType.call
但是装饰器之间没有实例关系 不调用__call__

实际上很常用啊

回到顶部