__call__
定义在普通类中当在普通的类中定义
__call__
方法时,会使得实例对象变成一个可调用对象
python的可调用对象都会有__call__
方法
class T:
pass
t = T()
t()
输出
TypeError: 'T' object is not callable
说T的实例对象是不可调用的;但是当你在T中实现了__call__
方法以后
class T:
def __call__(self):
print('__call__')
t = T()
t() # __call__
实例对象t就变成了可调用对象。
通过上述例子,我们也可以得出一个结论,python中的可调用对象都会存在__call__
方法。
比如函数:
def func():
print('hello')
print(hasattr(func, '__call__')) # True
func.__call__() # hello
func()
其实就相当于func.__call__()
hasattr()
的作用是判断对象func是否含有方法或属性__call__
。
想了解hasattr()
的可以看这篇文章:Python 的hasattr(), getattr() 和 setattr()函数
那么通过上面的例子,我们可以判断出func对象含有__call__
方法,因此就可以断定他是一个可调用对象。
同时利用__call__
也可以解决hasattr
的一个缺点,hasattr
仅能判断对象obj是否含有某个属性或方法,但是无法判断出其到底是属性还是方法。再利用__call__
就可以判断出到底是属性还是方法了。
如下
class T:
def __init__(self):
self.name = 'Jack'
def func(self):
print('hello')
t = T()
print(hasattr(t, 'name')) # True
print(hasattr(t.name, '__call__')) # False
print(hasattr(t, 'func')) # True
print(hasattr(t.func, '__call__')) # True
通过如上的组合拳就能判断出t含有name和func,且name为属性,func为可调用对象(方法)
__call__
在Metaclass中的作用在实例化的时候,会调用Meatclass中的call方法,而Meatclass中的call方法又会调用类中的new和init去生成一个实例对象。
先把结论放在前面吧:
在实例化的过程中,会优先调用metaclass中的__call__
,而这个__call__
会先后默认调用类对象中的__new__
和__init__
,最终获得一个完整的实例对象。
metaclass的作用以及__call__
的调用顺序在之前的两篇文章中有研究过:
Python 元类
Python 中的 new
这里算是再来复习一下,写的就不会那么详细了。
首先要弄清楚类中__new__
和__init__
的作用:
__new__
:类对象通过调用自己的__new__
方法在内存中申请一个空间让实例对象居住。因此它需要接受一个参数cls用来明示是哪个类对象要申请空间,然后返回一个对应的实例对象。__init__
:当类对象为实例对象创建好空间以后,实例对象需要在调用__init__
对这个空间进行初始化,因此__init__
需要接受一个self参数,来告诉自己要实例化哪一个实例对象。弄清楚以后看个例子
class Meta(type):
def __call__(cls):
print('Meta: __call__', cls) # cls为类对象 T
i = cls.__new__(cls) # i 为实例对象 t, 此处调用了T的__new__方法,返回了t
i.__init__() # 调用了实例对象的__init__方法
return i
class T(metaclass=Meta):
def __new__(cls):
print('T: __new__', cls)
return super().__new__(cls)
def __init__(self):
print('T: __init__', self)
t = T()
# __new__的作用就是为实例对象创建空间,而且哪个类中的new创建的空间就由对应的实例对象居住
# 因此__new__接收的第一个参数就是类对象,而返回的结果是实例对象
# __init__的作用是初始化实例对象,因此接收的参数是实例对象
输出结果
Meta: __call__ <class '__main__.T'>
T: __new__ <class '__main__.T'>
T: __init__ <__main__.T object at 0x0000015C017513A0>
结果证明:在实例化的过程中,会优先调用metaclass中的__call__
,而这个__call__
会先后默认调用类对象中的__new__
和__init__
,最终获得一个完整的实例对象。