Python 中 __new__ 和 __init__ 有什么区别?
简化版
__new__ 负责创建对象,返回实例;__init__ 负责初始化对象,给已经创建好的实例设置属性。正常业务类主要重写 __init__,只有控制对象创建过程、不可变类型子类化、单例等场景才常重写 __new__。
详细版
对象创建大致分两步:
obj = ClassName(...)
背后会先调用:
obj = ClassName.__new__(ClassName, ...)
如果 __new__ 返回的是当前类的实例,再调用:
ClassName.__init__(obj, ...)
示例:
class User:
def __new__(cls, *args, **kwargs):
print("__new__")
return super().__new__(cls)
def __init__(self, name):
print("__init__")
self.name = name
u = User("Tom")
输出顺序是:
__new__
__init__
__new__ 是静态创建阶段方法,第一个参数通常叫 cls;__init__ 是实例初始化方法,第一个参数是 self。
完整版教学
一、对象不是 init 创建出来的
很多人以为 __init__ 是构造方法,负责创建对象。更准确地说,__init__ 是初始化方法,对象在进入 __init__ 前已经存在。
class User:
def __init__(self, name):
self.name = name
这里的 self 就是已经创建好的实例。__init__ 做的事情是给它挂属性、校验参数、准备初始状态。
真正控制对象创建的是 __new__:
class User:
def __new__(cls, *args, **kwargs):
return super().__new__(cls)
它必须返回一个对象。
二、调用流程怎么走
执行:
u = User("Tom")
可以粗略理解为:
tmp = User.__new__(User, "Tom")
if isinstance(tmp, User):
User.__init__(tmp, "Tom")
u = tmp
如果 __new__ 返回的不是当前类实例,__init__ 可能不会按普通流程执行。
例如:
class A:
def __new__(cls):
return "not an A"
def __init__(self):
print("init")
a = A()
print(a) # not an A
这里返回的是字符串,不是 A 的实例,所以 A.__init__ 不会用这个字符串当 self 去初始化。
三、为什么平时很少写 new
大多数业务类不需要控制对象分配过程,只需要初始化属性:
class Order:
def __init__(self, order_id, amount):
self.order_id = order_id
self.amount = amount
这种场景重写 __init__ 就够了。
随意重写 __new__ 会让对象创建过程变复杂,也容易影响继承链。
四、哪些场景会用 new
常见场景有三个。
第一,不可变类型子类化。比如继承 tuple、str、int,对象一旦创建就不能再改内部值,所以要在 __new__ 阶段决定值:
class UpperStr(str):
def __new__(cls, value):
return super().__new__(cls, value.upper())
s = UpperStr("python")
print(s) # PYTHON
第二,单例控制:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
第三,框架或底层库需要定制对象创建流程。
五、init 是否可以返回值
__init__ 不能返回非 None 值。
错误示例:
class User:
def __init__(self):
return self
这会报错,因为 __init__ 的职责是初始化,不是返回对象。对象已经由 __new__ 返回了。
六、继承中要注意 super
如果你重写 __new__,通常要调用父类的 __new__:
def __new__(cls, *args, **kwargs):
return super().__new__(cls)
如果你重写 __init__,也常需要调用父类初始化:
class Base:
def __init__(self, name):
self.name = name
class Child(Base):
def __init__(self, name, age):
super().__init__(name)
self.age = age
否则父类中定义的属性可能没有被初始化。
七、常见误区与追问
易错点:
__new__管“有没有对象、返回哪个对象”,__init__管“拿到对象后怎么填属性”。把这条线分清,单例、不可变对象、自定义创建流程都好解释。
- 误区:
__init__是构造函数,负责创建对象。 更准确地说,__new__创建并返回实例,__init__初始化这个已经创建好的实例;Python 里二者职责不同。 - 追问:
__init__能不能返回对象? 不能,__init__必须返回None;如果返回其他值会抛TypeError,因为对象已经由__new__返回了。 - 误区:所有类都应该重写
__new__。 大多数业务类只需要__init__;只有要控制实例创建、缓存实例、单例、不可变类型子类化时,才考虑__new__。 - 追问:为什么继承
tuple、str这类不可变类型时常用__new__? 不可变对象内容在创建时就要确定,等到__init__阶段已经无法原地改内部值,所以要在__new__里把值传给父类创建过程。 - 误区:
__new__一定会触发__init__。 如果__new__返回的不是当前类的实例,Python 不会按当前类继续调用__init__;这也是它能深度控制对象创建的原因。 - 追问:单例为什么常在
__new__里实现? 因为单例要控制“返回哪个实例”,这是创建阶段职责;但工程中还要注意线程安全和重复调用__init__造成状态被覆盖。
八、加强记忆
__new__ 创建对象,__init__ 初始化对象。平时写类主要写 __init__;只有要干预“对象怎么被造出来”时才考虑 __new__。判断两者最简单的方法是看返回值:__new__ 必须返回实例,__init__ 只能返回 None。