如果你写过一段时间的 Python,多半见过 __init__,但凡面试或者看框架源码,又躲不开 __new__。这两个双下划线方法都跟实例化有关,但一个是“创建对象”,一个是“初始化对象”,很多人学了几年也没彻底分清楚。这篇东西我按自己的理解把它们的区别、调用顺序、典型场景、踩坑点全部捋一遍,包含可直接运行的验证代码,适合刚接触魔法方法的进阶玩家,也适合准备 Python 面试的开发者收藏。
先给一句总纲:__new__ 负责“造”出对象,__init__ 负责“修”好对象。造不出来,后面想修都没得修;造出来的东西不对类型,__init__ 甚至根本不会执行。后面所有细节都围绕这句话展开。
1. 先搞清楚这两个方法在对象生命周期里的位置
1.1 实例化过程不是你以为的“一步到位”
很多人写 obj = Foo() 的时候,脑子里默认这是“调用了 Foo 的构造方法”。其实这一步远没有这么简单。Python 里 Foo() 这个表达式,底层会先走 type.__call__(Foo, *args, **kwargs),然后在这个调用过程中依次触发 Foo.__new__ 和 Foo.__init__。换句话说,Foo() 是一个面向用户的入口,真正的“构造”分成了两个阶段。
为什么要拆成两半?因为有些对象根本不需要“初始化”,只需要“创建”。最典型的就是不可变对象,比如 int、str、tuple。你还记得 tuple 一旦创建就不能改吧?如果你先创建一个空对象再往里塞数据,那不可变就无法保证了。所以 tuple 这类对象必须在 __new__ 阶段就把数据定死,__init__ 干脆不做事。理解了这一点,你就理解了为什么 Python 要在 __init__ 之外单独设计一个 __new__。
用一个粗糙的生活类比:__new__ 相当于工厂里的“开模压胚”,把一个空壳子做出来;__init__ 相当于“组装零部件、贴标签”,往壳子里装东西。开模这一步只能做一次,装零件可以在不同阶段反复做,但必须先有壳子。这个顺序是硬性的,Python 解释器规定 __new__ 先执行,且必须返回一个实例,然后 __init__ 才会拿这个实例去初始化。
1.2 __new__ 是“造人”,__init__ 是“给身份证”
从职责上看,__new__ 的第一个参数是类本身,也就是 cls,它负责返回一个实例对象。你可以在里面做各种拦截,比如决定“这个类不直接生成新实例,而是返回之前缓存的实例”,或者“不允许直接实例化,抛异常”。这些操作都在对象诞生之前发生。
而 __init__ 的第一个参数是实例本身,按惯例叫 self,它的职责是给这个已经存在的实例设置属性。它不需要返回值,如果写了返回值,Python 会直接抛 TypeError。这一点和很多其他语言的构造函数完全不一样,其他语言里构造函数通常允许返回对象,但 Python 里 __init__ 必须返回 None。
从参数上也能看出分工:__new__(cls, ...) 接收的是类,__init__(self, ...) 接收的是实例。同一个 Foo(),传入的参数会分发给这两个方法。这个细节稍后会有代码演示。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心区别逐条拆解:参数、返回值、触发时机
2.1 参数传递的差异
写一个最普通的类:
python复制class Foo:
def __new__(cls, name, age):
print("__new__ 被调用,cls =", cls)
return super().__new__(cls)
def __init__(self, name, age):
print("__init__ 被调用,self =", self)
self.name = name
self.age = age
f = Foo("张三", 18)
print(f.name, f.age)
输出:
code复制__new__ 被调用,cls = <class '__main__.Foo'>
__init__ 被调用,self = <__main__.Foo object at 0x...>
张三 18
注意,Foo("张三", 18) 里的 "张三" 和 18 同时传给了 __new__ 和 __init__。所以如果你重写了 __new__,签名必须和 __init__ 兼容,否则会报参数错误。很多新手在这里翻车,只给 `new(cls, *args,
