目前仍有较多的初学者,或者从其他语言转Python的朋友认为__init__函数就是Python中类的构造函数,其实是不对的。
Python 2的早期,确实是没有__new__函数的,但那是很多年前的事情了,现在的Python 2和Python 3中,类实例的创建过程均遵循先调用__new__函数构造类实例,然后调用**__init__函数对实例进行初始化**。
1. 先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__函数。
2. 无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
3. 自定义__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 <module>
s = Sample()
TypeError: __init__() should return None, not 'int'
4. 单例模式(Singleton)
重写__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都指向同一个实例。
5. 工厂模式(Factory)
上面我们看到,我们可以自定义__new__函数的返回值,那么我们可以使用这个特性,实现工厂模式。同样以游戏为例,我们需要一个兵工厂来给我们生产各种类型的战士,例如普通小兵、buff兵等等:
lass 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!!!