__new__返回实例类型必须与类一致,否则跳过__init__;__init__中不能重赋值self;单例需在__new__缓存并返回旧实例;不可变类型构造必须在__new__完成。

为什么__new__返回的实例类型必须和类一致
如果__new__返回了非当前类或其子类的实例(比如直接返回int(42)或object()),Python 会跳过__init__调用——这不是 bug,是语言设计:只有当__new__返回的是本类实例时,解释器才认为“构造完成”,进而触发__init__。常见错误是写成:
def __new__(cls, value):
return super().__new__(int) # 错!返回 int 实例,__init__ 不会被调用
正确做法是确保返回cls的实例:
def __new__(cls, value):
instance = super().__new__(cls)
return instance
如果你真要代理到其他类型(如单例、缓存、不可变封装),必须显式调用__init__或改用__call__在元类中控制。
__init__里不能给self重新赋值为新对象
__init__的self参数是__new__返回的对象引用,它已经确定了内存地址和类型。你不能在__init__里写self = SomeOtherClass()——这只会让局部变量self指向别处,原对象不变,且后续所有属性赋值都作用在旧对象上。
典型误用场景:
- 想“转换类型”:比如传入字符串就转成
int,但又想保留类接口 → 应该在__new__中决定返回哪个类的实例,或用工厂函数 - 想避免重复初始化:比如检查参数是否已存在对应实例 → 这属于对象创建逻辑,应在
__new__中做缓存/查表
记住:__init__只负责“填充”已有实例,不负责“生成”或“替换”。
单例模式下__new__必须缓存并返回旧实例
单例不是靠__init__控制的,因为每次调用类都会执行__new__→__init__流程。__init__反复执行会导致状态被覆盖。真正可靠的单例必须在__new__中拦截:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
注意两点:
- 缓存的是
cls._instance,不是self(self还没诞生) - 必须用
super().__new__(cls)而非object.__new__(cls),否则绕过父类__new__可能破坏继承链(比如某些框架类重写了__new__)
如果类有参数,还要在__new__中处理**kwargs或校验参数一致性,否则多次不同参数调用可能导致状态混乱。
不可变类型(如str、tuple子类)必须在__new__中完成构造
__init__无法修改不可变对象的值。例如自定义一个带校验的PositiveInt:
class PositiveInt(int):
def __new__(cls, value):
if value <p>下面这种写法是错的:</p><pre class="brush:php;toolbar:false;">def __init__(self, value): # ❌ int 实例一旦创建就不能改,__init__ 里 self = ... 无效
if value <p>原因:父类<code>int</code>的<code>__new__</code>已经把值固化进对象了。<code>__init__</code>只能设置额外属性(如<code>self.source = "user_input"</code>),不能动核心值。所以所有对值的校验、转换、截断等操作,必须放在<code>__new__</code>里完成。</p><p>容易被忽略的是:即使你没显式定义<code>__new__</code>,Python 也会用默认的<code>object.__new__</code>,而它不做任何值处理——所以一旦涉及不可变类型的定制,<code>__new__</code>不是可选项,是必选项。</p>Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











