Python中创建对象时,会涉及__init__
和__new__
两个函数,个人感觉前者较为常见。
创建对象时发生了什么
创建一个类对象首先会调用该类的__new__方法(接下来省略下划线),new方法本身接受一个类参数cls,一般就是自身,随后会通过super函数得到该类的父类,并调用父类的new方法进行对象的创建(因为所有类都基于object,所以最终都会调用object的new方法)
super函数(实际上是类,这里为了简明,使用相同功能的函数替代)的输入为类cls,和某个实例obj,super做的事情就是找到obj的父类并返回,这里我们使用python的mro函数得到obj对象对应类的父类列表。mro这个类方法是python解决多重继承问题的方案,简单理解就是mro会返回调用类的父类链,列表靠前的元素是靠后元素的子类。那么super的做法就是得到obj对象类的mro列表,取出mro列表中cls的后一个元素,也就是cls的最近父类。
def super(cls, obj):
mro = obj.__class__.mro()
return mro[mro.index(cls) + 1]
当然,super函数的输入也不一定是obj,比如super(cls, obj_cls)
,因为我们想在new函数中调用父类的方法,此时对象还没创建,所以只能传obj对应的类。对应的操作无非就是通过obj_cls.mro()
来得到父类列表了。
注1:super函数既适用于实例也适用于类本身,例如super(cls, cls)返回的是cls的父类,而super(cls, cls_obj)返回的是一个cls的父类实例;两者的区别在于调用init或其他实例方法时是否要传入self,这个细节可以看接下来的代码注释
类实例创建完成后,就会调用该实例的init方法进行初始化,init函数至少有一个参数self,这就指向之前new方法创建出来的对象。
一段例子
以一段代码为例,首先定义class B,父类是object(缺省也可以,从python3开始默认的基类就是object);定义class A,父类是B;A和B都各自重写了new/init两个函数。接下来创建A对象,结果见注释。
class B(object):
def __new__(cls):
print("B.__new__ called")
return super(B, cls).__new__(cls)
def __init__(self):
pass
class A(B):
def __new__(cls):
print("A.__new__ called")
print(cls.mro())
return super(A, cls).__new__(cls)
def __init__(self):
print("A.__init__ called")
super(A, self).__init__() # 注1:如果super传入的不是实例self,而是类对象本身,那么这行代码应该写成 super(A, A).__init__(self)
A()
# output
# A.__new__ called
# [<class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
# B.__new__ called
# A.__init__ called
结果解释:
- 首先A()会调用A本身的new方法,从而打印出了
A.__new__ called
- 随后
super(A, cls)
返回了cls的父类,实际上这里的cls也就是A,写成super(A, A)
或者super(cls, cls)
都可以- 得到父类的方法是:首先调用cls的mro类方法,返回父类列表lst,lst中定位到第一个参数A的位置,返回下一个元素。通过打印A.mro()可以知道,A的下一个元素是B,所以super(A, cls)返回了class B
- 接下来调用
super(A, cls).__new__(cls)
,即B.__new__(cls)
,因此输出B.__new__ called
- 同理,
super(B, cls).__new__(cls)
等同于object.__new__(cls)
,也就是一切对象创建的起点,然后嵌套(嵌套这里表达欠妥,可见注3)返回,此时我们得到一个创建出来的A对象a - 最后这个对象a会调用自身的init方法,即
a.__init__(a)
,从而输出A.__init__ called
注2:以上代码中很多可以省略,例如
__init__(self)
中的self,super(A, cls)
中的A和cls,这里写出来是为了更好的解释每个函数的作用
在菱形继承中,init调用顺序更加复杂,但本质上还是和super函数涉及到的mro列表有关,可见以下代码
class A(object):
def __init__(self):
print("A init")
super().__init__()
class B(A):
def __init__(self):
print("B init")
print("B's mro: " + str(B.__mro__))
print("self's mro: " + str(type(self).__mro__))
super().__init__() # 根据self实例对应类的mro列表调用init函数,如果这里使用super(B, B).__init__(),就会跳过C的实例化
class C(A):
def __init__(self):
print("C init")
super().__init__()
class D(B, C):
def __init__(self):
print("D init")
super().__init__()
d = D()
# output
# D init
# B init
# B's mro:(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
# self's mro:(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
# C init
# A init
撒花,mro相关内容可能之后会更吧(
这里说的其实不太清楚,把类对象换成实例会好一点(毕竟类本身也是一个对象