目前仍有较多的初学者,或者从其他语言转Python的朋友认为__init__
函数就是Python中类的构造函数,其实是不对的。
Python 2的早期,确实是没有__new__
函数的,但那是很多年前的事情了,现在的Python 2和Python 3中,类实例的创建过程均遵循先调用__new__
函数构造类实例,然后调用__init__
函数对实例进行初始化。
看一个简单的例子:
class Sample(object):
def __new__(cls):
print("Sample.__new__ called")
return super().__new__(cls)
def __init__(self):
print("Sample.__init__ called")
s = Sample()
上面的代码会输出:
Sample.__new__ called
Sample.__init__ called
即Python解释器会先调用__new__
函数再调用__init__
函数。
上面代码中__new__
函数中调用了父类(object类)的__new__
函数,父类的__new__
函数会创建并返回一个cls
类,也就是Sample
类的实例对象。Sample类的__new__
函数继续返回这个对象。接着这个实例对象被传入__init__
函数,用于初始化该对象中的一些成员。过程如下图所示:
注意:__new__
是静态函数,__init__
是实例函数。
如果,__new__
函数不返回实例对象,那么__init__
函数就不会被调用:
class Sample(object):
def __new__(cls):
print("Sample.__new__ called")
# return super().__new__(cls)
def __init__(self):
print("Sample.__init__ called")
s = Sample()
print(s)
上面的代码会输出:
Sample.__new__ called
None
也就是说,__new__
函数不返回实例对象,那就无法创建类实例。
如果我们把代码改成这样:
class Sample(object):
def __new__(cls):
print("Sample.__new__ called")
return 666
def __init__(self):
print("Sample.__init__ called")
s = Sample()
print(s)
输出:
Sample.__new__ called
666
可以看到,__new__
函数决定了我们最终创建的是什么类型的对象。这里Sample.__init__
函数没有被调用,因为666是一个int
类型的实例,调用的是int.__init__
函数,这个函数不会输出任何信息。当然,我们还可以返回其他类型的对象,所以__new__
函数给我们带来了很多灵活性。
另外还有一点需要注意,Python规定__init__
函数只能返回None
,否则会引起TypeError
:
class Sample(object):
def __new__(cls):
print("Sample.__new__ called")
return super().__new__(cls)
def __init__(self):
print("Sample.__init__ called")
return 1
s = Sample()
输出:
Sample.__new__ called
Sample.__init__ called
Traceback (most recent call last):
File "main.py", line 11, in
s = Sample()
TypeError: __init__() should return None, not 'int'
重写__new__
函数可以带给我们很多灵活性,例如实现单例模式,例如在游戏中同一时刻只允许出现一个大Boss,只有Boss被打死之后,才能新召唤一个Boss:
class Boss(object):
_instance = None
def __new__(cls):
print("Sample.__new__ called")
if cls._instance is None:
cls._instance = super().__init__(cls)
return cls._instance
def __init__(self):
print("Sample.__init__ called")
boss1 = Boss()
boss2 = Boss()
print(id(boss1)) # 140620000188688
print(id(boss2)) # 140620000188688
可以看到boss1和boss2都指向同一个实例。
上面我们看到,我们可以自定义__new__
函数的返回值,那么我们可以使用这个特性,实现工厂模式。同样以游戏为例,我们需要一个兵工厂来给我们生产各种类型的战士,例如地精、萨满祭司等等:
class Soldier(object):
def __init__(self):
pass
def attack(self):
pass
def retreat(self):
print("Retreat!!!")
class Goblin(Soldier):
""" 地精 """
def __init__(self):
pass
def attack(self):
print("Goblins are attacking!!!")
class Shaman(Soldier):
""" 萨满 """
def __init__(self):
pass
def attack(self):
print("Shamans are attacking!!!")
class SoldierFactory(object):
# 兵工厂能够生产的战士类型
soldiers = {'goblin': Goblin, 'shaman': Shaman}
def __new__(cls, name):
if name in cls.soldiers.keys():
return cls.soldiers[name]()
return None
goblin1 = SoldierFactory("goblin")
goblin2 = SoldierFactory("goblin")
shaman1 = SoldierFactory("shaman")
shaman2 = SoldierFactory("shaman")
goblin1.attack() # Goblins are attacking!!!
goblin2.attack() # Goblins are attacking!!!
shaman1.attack() # Shamans are attacking!!!
shaman2.attack() # Shamans are attacking!!!
goblin1.retreat() # Retreat!!!
goblin2.retreat() # Retreat!!!
shaman1.retreat() # Retreat!!!
shaman2.retreat() # Retreat!!!
以上就是关于始化函数__init__
和构造函数__new__
的一些简介。
我的知乎:奔三的鑫鑫
欢迎关注微信公众号:小鑫的代码日常
欢迎加入Python学习交流群:532232743,这里有各路高手等着你~