Python 的 __new__ 和 __init__ 到底有什么区别?写个例子你就彻底懂了.
很多学 Python 的朋友都会在类这里卡住,尤其是 __new__ 和 __init__ 这两个方法。光看文档觉得挺绕,写代码又分不清该用哪个。今天咱们用大白话把这事说透。
先记一个核心:__new__ 管生,__init__ 管养。生就是创建对象,养就是给对象填属性。一个萝卜一个坑,各干各的活。
拿盖房子打个比方
把类想象成一张房子图纸。__new__ 就是施工队,负责把房子从无到有盖起来。房子盖好了,毛坯房一间。__init__ 是装修队,负责给房子刷墙铺地装家具。施工队不干活,装修队就没法进场。装修队干完了,房子才能住人。
代码里也是一样的道理。__new__ 先被调用,它返回一个实例对象。然后 Python 自动把这个对象传给 __init__,让 __init__ 给对象加属性。
先看一个最简单的例子
class House: def __new__(cls, args, kwargs):
print('施工队进场,造房子')
instance = super().__new__(cls)
return instance
def __init__(self, color, rooms):
print('装修队进场,开始装修')
self.color = color
self.rooms = rooms
house = House('白色', 3)
print(house.color)
运行结果会打印两行。第一行是“施工队进场,造房子”,第二行是“装修队进场,开始装修”。这就是调用顺序。你注意看,__new__ 的参数里有个 cls,它代表类本身。__init__ 的第一个参数是 self,就是 __new__ 返回的那个实例。
什么时候必须用 __new__
大部分场景你只需要写 __init__,但有两个情况你必须动 __new__。第一个是写单例模式,就是整个程序里某类只能有一个实例。第二个是继承不可变类型,比如 int、str、tuple。
咱们看单例模式的例子。
class SingleDog: _instance = None
def __new__(cls, args, kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, name):
self.name = name
dog1 = SingleDog('旺财')
dog2 = SingleDog('来福')
print(dog1 is dog2) 输出 True
print(dog1.name) 输出 '来福'
这个例子里 __new__ 在创建实例前先检查有没有现成的,有就直接返回旧的。所以 dog1 和 dog2 其实是同一个对象。你第二次传的参数“来福”把第一次的“旺财”覆盖了。这就是单例的特点。
再看继承不可变类型的例子。
class PositiveNumber(int): def __new__(cls, value):
if value < 0: value = 0
return super().__new__(cls, value)
n = PositiveNumber(-5)
print(n) 输出 0
int 是不可变类型,你没法在 __init__ 里改它的值,只能通过 __new__ 来控制创建时的值。
__new__ 和 __init__ 的签名不同
__new__ 的第一个参数是类,后面可以跟任意参数。__init__ 的第一个参数是实例,后面参数要跟 __new__ 保持一致。Python 在调用 __new__ 时会把相同的参数传给 __init__。如果你在 __new__ 里改了参数,__init__ 接到的还是原来的值,不会跟着变。
一个容易搞混的点
有人以为 __init__ 可以代替 __new__,其实不行。如果你不写 __new__,Python 会自动调用 object 的 __new__ 来创建实例。但如果你把 __new__ 写错了,比如忘了 return,那对象就创建失败,__init__ 根本不会执行。
记住一句话:__new__ 必须返回一个实例,__init__ 不能返回任何值(只能返回 None)。这是规矩,破了就报错。
把两个方法一起写出花
你可以让 __new__ 返回一个完全不同的实例,比如返回一个缓存过的对象,或者根据条件返回不同的子类实例。这时候 __init__ 会在那个返回的实例上执行。
class Factory: def __new__(cls, type):
if type == 'A':
return super().__new__(cls)
elif type == 'B':
return super().__new__(cls)
else:
return object.__new__(cls)
def __init__(self, type):
print(f'当前对象类型是 {type}')
f1 = Factory('A')
f2 = Factory('B')
你看,__new__ 虽然根据参数做了不同处理,但它返回的都是同一个类的实例,所以 __init__ 能被正确调用。如果 __new__ 返回了别的类的实例,__init__ 就压根不会跑。
实际开发怎么用
写普通业务代码,你基本只用 __init__ 给对象初始化属性就够了。只有涉及元类编程、单例模式、缓存对象池、或者扩展不可变类型时,才去碰 __new__。没必要为了炫技瞎用,容易把自己绕进去。
最后给你一个自查清单。如果你不确定该覆盖哪个方法,问自己两个问题。我要不要控制实例的创建过程,比如要不要只返回一个已有的对象,要不要改创建时的类型。如果要,就动 __new__。如果只是给建好的对象赋属性,那就只写 __init__。
生和养,一个是构造函数一个是初始化函数。搞懂这个,你就不再迷糊了。