【python 笔记】元类中的__call__方法
在学到用metaclass实现单例时,发现用的是元类的__call__方法,而不是__new__方法。对元类__call__比较疑惑,看起来似乎和普通类的__call__不一样。学习之后,一些基础的总结如下。
先是一些基本的概念等
- 元类是类的类,元类之于类就相当于类之于实例。
- 元类的new方法会创建一个类并返回,就像类的new方法会创建一个实例并返回一样。
- 元类中其他方法的定义类似于类中方法的定义。
__call__
方法是元类中有一个特殊的方法。
在元类方法中,参数cls对应的对象:
class Meta(type):
def __new__(cls, name, bases, dct): # cls为元类Meta
return type.__new__(cls, name, bases, dct)
def __init__(cls, *args, **kwargs): # cls为元类创建的类
pass
def __call__(cls, *args, **kwargs): # cls为元类创建的类
pass
对元类__new__、__call__的分析
Code1:元类 改写__new__方法
class SingletonType(type):
def __new__(cls, class_name, class_parents, class_attr): # cls为元类
print('元类__new__')
return type(class_name, class_parents, class_attr)
def __init__(cls, *args, **kwargs): #cls为元类创建的类
print('元类__init__')
super().__init__(*args, **kwargs)
class Foo(metaclass=SingletonType):
def __init__(self, name):
print("Foo __init__")
self.name = name
def __new__(cls, *args, **kwargs):
print('Foo __new__')
return object.__new__(cls)
obj = Foo('name')
print(obj)
obj = Foo('name')
print(obj)
# 输出:
# 元类__new__
# Foo __new__
# Foo __init__
# <__main__.Foo object at 0x7f7312016080>
# Foo __new__
# Foo __init__
# <__main__.Foo object at 0x7f73120160b8>
Code1中:
- 定义类时,元类定义并执行了__new__,但元类的__init__不会再被调用。
- 创建对象时,Foo类中的__new__方法和__init__方法依次执行。
- 第二次创建对象时,元类的__new__不会再被调用。因为元类的__new__方法会创建一个类并返回,而这个类在定义时已经存在了。
Code2: 元类 改写__call__特殊方法,不改写__new__方法
class SingletonType(type):
def __call__(cls, *args, **kwargs): # cls为元类创建的类
print('元类__call__')
# obj = cls.__new__(cls, *args, **kwargs) # 手动调用Foo.__new__(obj)
# cls.__init__(obj, *args, **kwargs) # 手动调用Foo.__init__()
obj = object.__new__(cls)
return obj
def __init__(cls, *args, **kwargs): #cls为元类创建的类
print('元类__init__')
super().__init__(*args, **kwargs)
class Foo(metaclass=SingletonType):
def __init__(self, name):
print("Foo __init__")
self.name = name
def __new__(cls, *args, **kwargs):
print('Foo __new__')
return object.__new__(cls)
obj = Foo('name')
print(obj)
obj2 = Foo('name')
print(obj2)
# 输出:
# 元类__init__
# 元类__call__
# <__main__.Foo object at 0x7f2f9367b048>
# 元类__call__
# <__main__.Foo object at 0x7f2f9367b080>
- 两次创建对象都会调用元类的__call__方法。因为 元类__call__方法返回的是实例,和类的
__new__
方法返回的一样。 - 类的__new__方法和__init__方法并没有被执行。因为 元类
__call__
方法会截断类的__new__
和__init__
方法,阻止其执行。(可以手动调用执行)
Code3:元类 进一步改写__call__特殊方法,将其返回改为type.__call__(cls, *args, **kwargs)
class SingletonType(type):
def __call__(cls, *args, **kwargs): # cls为元类创建的类
print('元类__call__')
# obj = cls.__new__(cls, *args, **kwargs) # 手动调用Foo.__new__(obj)
# cls.__init__(obj, *args, **kwargs) # 手动调用Foo.__init__()
# obj = object.__new__(cls)
obj = type.__call__(cls, *args, **kwargs)
return obj
def __init__(cls, *args, **kwargs): #cls为元类创建的类
print('元类__init__')
super().__init__(*args, **kwargs)
class Foo(metaclass=SingletonType):
def __init__(self, name):
print("Foo __init__")
self.name = name
def __new__(cls, *args, **kwargs):
print('Foo __new__')
return object.__new__(cls)
obj = Foo('name')
print(obj)
obj2 = Foo('name')
print(obj2)
# 输出:
# 元类__init__
# 元类__call__
# Foo __new__
# Foo __init__
# <__main__.Foo object at 0x7f5da0368048>
# 元类__call__
# Foo __new__
# Foo __init__
# <__main__.Foo object at 0x7f5da0368080>
Code3中,元类的__call__
中返回type.__call__(cls, *args, **kwargs),则
type创建的对象,里面会调用Foo的__new__
方法,和__init__
方法,而不会被屏蔽。
Code4: 元类 改写__call__特殊方法,也改写__new__方法
class SingletonType(type):
def __new__(cls, class_name, class_parents, class_attr): # cls为元类
print('元类__new__')
return type(class_name, class_parents, class_attr)
def __init__(cls, *args, **kwargs): #cls为元类创建的类
print('元类__init__')
super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs): # cls为元类创建的类
print('元类__call__')
# obj = cls.__new__(cls, *args, **kwargs) # 手动调用Foo.__new__(obj)
# cls.__init__(obj, *args, **kwargs) # 手动调用Foo.__init__()
# obj = object.__new__(cls)
obj = type.__call__(cls, *args, **kwargs)
return obj
class Foo(metaclass=SingletonType):
def __init__(self, name):
print("Foo __init__")
self.name = name
def __new__(cls, *args, **kwargs):
print('Foo __new__')
return object.__new__(cls)
obj = Foo('name')
print(obj)
obj2 = Foo('name')
print(obj2)
# 输出:
# 元类__new__
# Foo __new__
# Foo __init__
# <__main__.Foo object at 0x7fcc476fb048>
# Foo __new__
# Foo __init__
# <__main__.Foo object at 0x7fcc476fb080>
1.执行了元类__new__方法,而__call__方法并没有执行,所以也没有因为__call__方法而屏蔽了类的__new__
和__init__
方法。
总结
- 元类的__new__方法会创建一个类并返回,类似类的new方法会创建一个实例并返回。
- 从代码看,元类同是改写__new__和__init__时,后者不会被调用(存疑)
- 元类__call__方法返回的是实例,和类的
__new__
方法返回的一样。 __call__
方法返回值不是 type.__call__(cls, *args, **kwargs) 时,元类__call__
方法会截断类的__new__
和__init__
方法,阻止其执行。- 如果元类的
__call__
中返回type.__call__(cls, *args, **kwargs),
type创建的对象,里面会调用类的__new__
方法,和__init__
方法。
对比
在元类中:
创建的对象是类,如果希望能够自定义它,一般改写__new__;
如果有需要的话,也可以在__init__中做些事情;
一些高级的用法会涉及到改写__call__特殊方法。
-----end-----
水平有限,出错难免,还望指正!