
Python 中__new__和__init__到底谁才是构造函数一文讲透区别与 5 个坑先给结论__new__才是真正的构造函数它负责造出对象__init__只是初始化函数它拿到的是已经造好的对象往里填属性。很多人写了几年 Python一直把__init__当成构造函数。这在多数场景下不影响写代码但一旦遇到不可变类型、单例模式、对象缓存就会撞上一些怎么调都不对的诡异问题。这篇文章把两者的执行顺序、分工和 5 个高频坑一次讲清楚。## 一、一句话区分pythonclass Demo: def __new__(cls, *args, **kwargs): print(1. __new__ 造对象, cls) return super().__new__(cls) def __init__(self, x): print(2. __init__ 填属性, x) self.x xd Demo(10)输出顺序永远是1. __new__ 造对象 class __main__.Demo2. __init__ 填属性 10关键点-__new__是隐式静态方法Python 会自动把它当成 staticmethod尽管你没加装饰器第一个参数是cls必须返回一个实例-__init__的第一个参数是self也就是__new__返回的那个对象不允许返回任何值返回非 None 会直接TypeError。二、完整调用链真正调度它们的是type.__call__你写Demo(10)的时候实际发生的是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__静默不执行pythonclass Bad: defnew(cls, *args,kwargs): print(“new”) # 忘了 return definit(self): print(“init”) # 永远不会执行b Bad()print(b) # Nonenew没写 return默认返回None。None不是Bad的实例于是init被直接跳过**不报错安静地返回一个None。这类 bug 排查起来非常费时间因为没有任何异常抛出。四、坑 2给object.__new__多传参数会炸pythonclass A: def __new__(cls, x): return super().__new__(cls, x) # TypeError def __init__(self, x): self.x xA(1)报错类似TypeError: object.__new__() takes exactly one argument (the type to instantiate)。正确写法只把cls传下去。pythondef __new__(cls, x): return super().__new__(cls) # 只传 cls参数留给__init__去接各司其职。## 五、坑 3不可变类型必须用__new____init__改不动这是__new__最刚需的场景。一旦对象已经创建完成str/int/tuple这类不可变对象就不能再改__init__拿到的self已经是成品塞不进任何东西。pythonclass PositiveInt(int): defnew(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’ 15PositiveInt(-1) # ValueError: 必须为正整数同样的思路还能做出带单位的字符串自动去重的元组都属于在造的时候动手脚的场景。## 六、坑 4单例写法能跑但别当银弹pythonclass Singleton: _instance None defnew(cls, *args,kwargs): if cls._instance is None: cls._instance super().new(cls) return cls._instance definit(self, name): self.name name # 注意每次实例化都会重新赋值a, b Singleton(“A”), Singleton(“B”)print(a is b) # Trueprint(a.name) # B —— 被第二次调用覆盖了三个必须知道的副作用1. __init__ **每次都会跑**属性会被后来者覆盖要避免就得加 hasattr 判断或用 __init__ 幂等设计2. 上面这段代码**不是线程安全的**并发下可能造出两个实例需要加锁或用模块级单例3. 单例会被**子类共享**cls._instance 挂在父类上子类和父类可能拿到同一个对象。日常项目里模块级对象、依赖注入容器通常比手写单例更省心。## 七、坑 5__new__ 返回别的类型__init__ 直接被跳过pythonclass Shape: defnew(cls, kind): if kind “circle”: return Circle() return super().new(cls) definit(self, kind): print(“Shape.init”)class Circle: passs 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 坑过评论区聊聊你踩的那个坑。