先给结论:__new__ 才是真正的构造函数,它负责"造出"对象;__init__ 只是初始化函数,它拿到的是已经造好的对象,往里填属性。
很多人写了几年 Python,一直把 __init__ 当成构造函数。这在多数场景下不影响写代码,但一旦遇到不可变类型、单例模式、对象缓存,就会撞上一些怎么调都不对的诡异问题。这篇文章把两者的执行顺序、分工和 5 个高频坑一次讲清楚。
一、一句话区分
python
class Demo:
def __new__(cls, *args, **kwargs):
print("1. __new__ 造对象", cls)
return super().__new__(cls)
def __init__(self, x):
print("2. __init__ 填属性", x)
self.x = x
d = Demo(10)
输出顺序永远是:
markdown
1. __new__ 造对象 <class '__main__.Demo'>
2. __init__ 填属性 10
关键点:
__new__是隐式静态方法 (Python 会自动把它当成 staticmethod,尽管你没加装饰器),第一个参数是cls,必须返回一个实例;__init__的第一个参数是self,也就是__new__返回的那个对象,不允许返回任何值 (返回非 None 会直接TypeError)。
二、完整调用链:真正调度它们的是 type.__call__
你写 Demo(10) 的时候,实际发生的是:
python
Demo(10)
└── type.__call__(Demo, 10) # 元类在调度
├── obj = Demo.__new__(Demo, 10) # 第一步:造
└── if isinstance(obj, Demo): # 第二步:判断
obj.__init__(10) # 是自家实例才初始化
return obj
注意那个 isinstance 判断------这是理解很多"为什么我的 __init__ 没执行"的关键。
三、坑 1:__new__ 忘了 return,__init__ 静默不执行
python
class Bad:
def __new__(cls, *args, **kwargs):
print("new") # 忘了 return
def __init__(self):
print("init") # 永远不会执行
b = Bad()
print(b) # None
__new__ 没写 return,默认返回 None。None 不是 Bad 的实例,于是 __init__ 被直接跳过,不报错,安静地返回一个 None。这类 bug 排查起来非常费时间,因为没有任何异常抛出。
四、坑 2:给 object.__new__ 多传参数会炸
python
class A:
def __new__(cls, x):
return super().__new__(cls, x) # TypeError!
def __init__(self, x):
self.x = x
A(1)
报错类似:TypeError: object.__new__() takes exactly one argument (the type to instantiate)。
正确写法:只把 cls 传下去。
python
def __new__(cls, x):
return super().__new__(cls) # 只传 cls
参数留给 __init__ 去接,各司其职。
五、坑 3:不可变类型必须用 __new__,__init__ 改不动
这是 __new__ 最刚需的场景。一旦对象已经创建完成,str / int / tuple 这类不可变对象就不能再改 ,__init__ 拿到的 self 已经是成品,塞不进任何东西。
python
class PositiveInt(int):
def __new__(cls, value):
if value <= 0:
raise ValueError("必须为正整数")
return super().__new__(cls, value)
n = PositiveInt(5)
print(n, type(n), n + 10) # 5 <class '__main__.PositiveInt'> 15
PositiveInt(-1) # ValueError: 必须为正整数
同样的思路还能做出"带单位的字符串""自动去重的元组",都属于"在造的时候动手脚"的场景。
六、坑 4:单例写法能跑,但别当银弹
python
class Singleton:
_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 # 注意:每次实例化都会重新赋值
a, b = Singleton("A"), Singleton("B")
print(a is b) # True
print(a.name) # B ------ 被第二次调用覆盖了
三个必须知道的副作用:
__init__每次都会跑 ,属性会被后来者覆盖(要避免就得加hasattr判断或用__init__幂等设计);- 上面这段代码不是线程安全的,并发下可能造出两个实例(需要加锁或用模块级单例);
- 单例会被子类共享 (
cls._instance挂在父类上),子类和父类可能拿到同一个对象。
日常项目里,模块级对象、依赖注入容器通常比手写单例更省心。
七、坑 5:__new__ 返回别的类型,__init__ 直接被跳过
python
class Shape:
def __new__(cls, kind):
if kind == "circle":
return Circle()
return super().__new__(cls)
def __init__(self, kind):
print("Shape.__init__")
class Circle:
pass
s = Shape("circle") # 不会打印 Shape.__init__
print(type(s)) # <class '__main__.Circle'>
因为返回值不是 Shape 的实例,__init__ 被跳过。这其实是工厂模式 的实现基础:__new__ 可以决定"到底造出什么",这是 __init__ 永远做不到的。
八、什么时候该用 __new__
按经验排序:
| 场景 | 用哪个 | 说明 |
|---|---|---|
| 普通业务对象、加属性 | __init__ |
99% 的情况 |
继承 int / str / tuple 并加约束 |
__new__ |
不可变,只能在造的时候动手 |
| 单例、对象池、缓存复用 | __new__ |
控制"是否新建" |
| 工厂:返回不同类型 | __new__ |
控制"造什么" |
| 元类编程 | __call__ / __new__ |
更靠底层一层 |
判断标准就一句:你要动的是"对象怎么被造出来",还是"造完之后填什么"。 前者用 __new__,后者用 __init__。
九、小结
__new__(静态、返回实例)= 构造函数,管"从无到有";__init__(实例方法、不能返回值)= 初始化函数,管"从有到全";- 只有
__new__返回的是当前类的实例时,__init__才会被调用; - 不可变类型、单例、工厂模式才需要动
__new__,其余一律用__init__。
你在项目里真的用过 __new__ 吗?是做单例、做不可变类型,还是被某个"忘了 return"的 bug 坑过?评论区聊聊你踩的那个坑。