new vs init
一、分工:先建房,再装修
-
__new__(cls, ...):负责创建实例——分配内存、真正“造出”一个对象。它是个静态方法,必须return一个对象。 -
__init__(self, ...):负责初始化实例——给刚造好的对象设置属性。它不返回任何值。
生活化比喻:
__new__是“盖房子”——打地基、搭框架,返回一个空房子;__init__是“装修”——给房子配家具家电。先有房子(new 返回)才能装修(init),顺序不能反。
二、执行顺序(关键)
sequenceDiagram participant U as 调用 MyClass(*args) participant N as __new__(cls) participant I as __init__(self) U->>N: 1) 分配内存, 返回实例 obj N-->>U: obj U->>I: 2) 用 obj 初始化属性 I-->>U: 初始化完成
要点:
__new__先执行并返回实例后,Python 才会调用__init__。如果__new__返回的不是一个cls的实例,__init__压根不会被调用。
三、可运行代码示例
class Foo:
def __new__(cls, *args, **kwargs):
print("1) __new__ 创建对象")
obj = super().__new__(cls) # 真正分配内存
return obj # 必须返回实例
def __init__(self, x):
print("2) __init__ 初始化")
self.x = x
f = Foo(10) # 先打印 __new__,再打印 __init__
print(f.x) # 10
四、什么时候必须碰 __new__
-
继承不可变类型(int / str / tuple):它们的值在创建时就定死了,想“改值”只能在
__new__里动手。class UpperStr(str): def __new__(cls, s): return super().__new__(cls, s.upper()) # 创建时就转大写 print(UpperStr("hello")) # HELLO -
单例模式 Singleton:控制全局只创建一个实例。
class Singleton: _inst = None def __new__(cls, *args, **kwargs): if cls._inst is None: cls._inst = super().__new__(cls) return cls._inst a = Singleton(); b = Singleton() print(a is b) # True,永远是同一个对象 -
对象池 / 缓存:在
__new__里拦截,复用已有实例。
五、核心考点
| 对比点 | __new__ | __init__ |
|---|---|---|
| 调用时机 | 创建对象时(先) | 初始化时(后) |
是否需 return | ✅ 必须返回实例 | ❌ 不返回 |
| 首个参数 | cls | self |
| 典型用途 | 不可变类型、单例、对象池 | 设置实例属性 |
-
__new__是隐式的静态方法,定义时不用写@staticmethod。 -
绝大多数业务代码只需写
__init__,别滥用__new__。 -
若
__new__不返回cls的实例,__init__不会被调用(常见于返回其它类型时的元编程场景)。
速记卡(面试闪卡)
Q1:一句话讲清「new vs init」到底是什么?
A:new 负责创建实例(分配内存、必须返回对象),init 负责初始化实例(设属性、不返回)。
Q2:一、分工:先建房,再装修 —— 怎么理解?
A:像”盖房子再装修”:new 是打地基搭框架、返回一个空房子(分配内存);init 是配家具家电(设属性)。顺序不能反——先有房子才能装修。一句话:new 管”生”,init 管”养”。
Q3:二、执行顺序(关键) —— 怎么理解?
A:像”工厂流水线”:Python 先调 new 拿到实例外壳,再拿这个实例去调 init 装属性。关键坑:若 new 返回的不是 cls 的实例(如返回别的类型),Python 认为”壳不对”,init 压根不会被调用——元编程里常见隐蔽 bug。
Q4:四、什么时候必须碰 new —— 怎么理解?
A:三种”必须动手”的场景:① 继承不可变类型(int/str/tuple),值创建时就定死,只能在 new 里改(如 UpperStr 创建时转大写);② 单例模式,new 里判断复用已有实例;③ 对象池/缓存,new 里拦截复用省开销。
Q5:五、核心考点 —— 怎么理解?
A:记住”作弊条”:调用时机 new 先、init 后;是否 return —— new 必须返回实例、init 不返回;首个参数 new 是 cls(类)、init 是 self(实例);用途不可变/单例/对象池 vs 设属性。new 是隐式静态方法,不用写 @staticmethod。
Q6:核心速记主线有哪些?
-
new 创建实例(分配内存),init 初始化(设属性)
-
执行顺序:new 先返回,init 才被调用
-
返回非 cls 实例时 init 不调用
-
new 用于不可变类型、单例、对象池
口诀
A:new 造壳来 init 装,先生后养不能反;
返回非类 init 歇,壳不对就不装。
单例不可变碰 new,对象池里复用忙;
cls 生 self 养记心间,绝大多写 init 安。