Python 中 __new__ 和 __init__ 到底谁才是构造函数?一文讲透区别与 5 个坑

0 阅读4分钟

先给结论:__new__ 才是真正的构造函数,它负责"造出"对象;__init__ 只是初始化函数,它拿到的是已经造好的对象,往里填属性。

很多人写了几年 Python,一直把 __init__ 当成构造函数。这在多数场景下不影响写代码,但一旦遇到不可变类型、单例模式、对象缓存,就会撞上一些怎么调都不对的诡异问题。这篇文章把两者的执行顺序、分工和 5 个高频坑一次讲清楚。

一、一句话区分

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)

输出顺序永远是:

1. __new__ 造对象 <class '__main__.Demo'>
2. __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__ 静默不执行

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__ 多传参数会炸

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 传下去。

def __new__(cls, x):
    return super().__new__(cls)          # 只传 cls

参数留给 __init__ 去接,各司其职。

五、坑 3:不可变类型必须用 __new__,__init__ 改不动

这是 __new__ 最刚需的场景。一旦对象已经创建完成,str / int / tuple 这类不可变对象就不能再改,__init__ 拿到的 self 已经是成品,塞不进任何东西。

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:单例写法能跑,但别当银弹

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 —— 被第二次调用覆盖了

三个必须知道的副作用:

  1. __init__ 每次都会跑,属性会被后来者覆盖(要避免就得加 hasattr 判断或用 __init__ 幂等设计);
  2. 上面这段代码不是线程安全的,并发下可能造出两个实例(需要加锁或用模块级单例);
  3. 单例会被子类共享(cls._instance 挂在父类上),子类和父类可能拿到同一个对象。

日常项目里,模块级对象、依赖注入容器通常比手写单例更省心。

七、坑 5:__new__ 返回别的类型,__init__ 直接被跳过

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 坑过?评论区聊聊你踩的那个坑。