首先我们知道,实例衍生自类对象,类对象衍生自元类对象
class Demo:
def __new__(cls, *args, **kwargs):
return super().__new__(cls, *args, **kwargs)
def __init__(self):
pass
def __call__(self, *args, **kwargs):
pass
这是一个普通的类对象,其中
- __ new __() 代表 类对象 产生 实例对象 的过程。实例对象 在这里被生产并 return 回去
- __ init __() 代表 实例对象 初始化的过程,也称构造函数
- __ call __() 代表 实例对象 被调用的行为
类对象之于实例对象,正如元类对象之于类对象,他们之间的关系是完全相同的
class DemoMeta(type):
def __new__(mcs, *args, **kwargs):
return super().__new__(mcs, *args, **kwargs)
def __init__(cls, *args, **kwargs):
super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
return super().__call__(*args, **kwargs)
我们同样可以推断出
- __ new __() 代表 元类对象 产生 类对象 的过程。类对象 在这里被生产并 return 回去
- __ init __() 代表 类对象 初始化的过程,也称构造函数
- __ call __() 代表 元类对象 被调用的行为
那么上述几个方法何时被调用?
我们不妨根据其作用来推断
-
__ call __()
是被对象被调用时的行为,当我们使用 类对象 来创建新实例时。本质就是调用 类对象 的行为。
例如d = Demo()
,在此时, 类对象 的call就被调用了。那么call产生的结果是什么?,是创建 实例对象 且初始化。
所以我们就可以推断出,类对象的call实际内容为
def __call__(cls, *args, **kwargs):
instance = cls.__new__(cls)
instance.__init__(*args, **kwargs)
return instance
# return super().__call__(*args, **kwargs)
-
__ new __()
这是我们在metaclass中最常用的方法,他代表的 元类对象 产生其 实例(类对象) 的过程 -
__ init __()
类对象的init并不常见,其中最大的问题是,我们定义class的时候,并没有显示的传参过程。
实际上我们在类对象中定义的参数,都会经过init。
class DemoMeta(type):
def __init__(cls, *args, **kwargs):
super().__init__(*args, **kwargs)
class Demo(metaclass=DemoMeta):
num = 10
我们在metaclass的init处打断点,便可以看到num:10在参数当中
当然我们也可以手动定义类对象,就像创建实例那样简单
Demo = DemoMeta('Demo', (object,), {num: 10})
# 等价于
class Demo(metaclass=DemoMeta):
num = 10
如果按类推,DemoMeta()是调用元类的__ call __()
。
没错,是这样,按理来说这是个无限的循环,当然实际上python解释器不会允许这样的情况。
无论是元类,类,实例,都不具有特殊性。他们都遵循着同样的行为
唯一的不同是,有些动作,python帮我们自行实现了。例如定义类时,会自动执行元类的实例化。
网友评论