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__方法的问题,为何有时会调用,有时却不会?
初学不要去关心 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__
实际上很常用啊

