Python __new__与__init__的区别及执行顺序详解

星野
星野
发布时间:2026-10-30| 5人浏览过
原创

深入详解 Python 中 __new__ 与 __init__ 的本质区别、调用执行顺序、参数传递机制及实际开发应用场景。通过完整的图解流程与可运行示例,彻底扫清面向对象实例化中的核心盲区。

分类:Python 核心机制 深度:中高级解析 阅读时长:约 12 分钟
在 Python 面向对象编程中,一个对象的诞生并非由单一方法完成,而是由 __new__ 和 __init__ 协同工作的双阶段过程。简而言之:__new__ 负责“构造”(分配内存并返回对象实例),属于静态方法;__init__ 负责“初始化”(为刚刚创建好的实例绑定属性),属于实例方法。调用顺序永远是 __new__ 在前、__init__ 在后。本文将深入剖析底层机制、参数流动、触发条件及实战场景。

一、为什么我们需要区分构造与初始化?

在很多初学者看来,Python 中的 __init__ 经常被称为“构造函数”(Constructor)。但在严格的语言规范中,这种称呼并不准确。__init__ 仅仅是初始化器(Initializer)。

当你在代码中写下 obj = MyClass("arg") 时,内存中此时还没有该对象的实体。在 __init__ 执行之前,必须先有某种机制在内存中为对象划分地址空间、建立对象类型指针。这个从无到有的“造物”工作,正是由 __new__ 独立承担的。

Python __new__与__init__的区别及执行顺序详解
图 1:Python 对象的完整实例化调用链与条件触发逻辑

二、核心区别全方位对比

为了清晰区分二者的技术边界,我们可以从方法签名、返回值、调用身份及典型应用四个维度进行对照:

对比维度 __new__(cls, *args, **kwargs) __init__(self, *args, **kwargs)
职责定位 对象的“创建者”(分配内存、构造底层结构) 对象的“装饰者”(初始化属性、配置状态)
首位参数 cls(当前所要创建的类本身) self(已经被 __new__ 创建出来的实例引用)
方法类型 静态方法(底层为 CPython 特化静态函数,无需加 @staticmethod) 常规实例方法
返回值要求 必须显式返回一个对象实例(通常为 super().__new__(cls)) 严禁返回值(必须返回 None,显式返回非 None 会抛 TypeError)
重写频率 极少重写(仅在元编程、单例模式或不可变类型派生时使用) 极高重写(日常面向对象开发中最常用的方法)
执行时间点 实例化阶段最先调用 在 __new__ 返回本类实例后紧随其后执行
Python __new__与__init__的区别及执行顺序详解
图 2:__new__ 与 __init__ 职责切分及对象实例移交模型

三、执行顺序代码验证:谁先谁后?

我们通过一段最直观的代码来观察二者的实际执行顺序和参数流动:

class Demo:
    def __new__(cls, *args, **kwargs):
        print(f"[1] __new__ 执行:cls 是 {cls},接收参数: {args}")
        instance = super().__new__(cls)
        print(f"[2] __new__ 生成实例引用: {id(instance)}")
        return instance

    def __init__(self, name):
        print(f"[3] __init__ 执行:self 实例引用: {id(self)}")
        self.name = name
        print(f"[4] __init__ 完成属性绑定: self.name = {self.name}")

# 触发实例化
d = Demo("Python工匠")

运行结果示例:

[1] __new__ 执行:cls 是 <class '__main__.Demo'>,接收参数: ('Python工匠',)
[2] __new__ 生成实例引用: 4329188432
[3] __init__ 执行:self 实例引用: 4329188432
[4] __init__ 完成属性绑定: self.name = Python工匠

从输出可以观察到两点极其关键的事实:

  1. 执行顺序严格递进:先打印 [1] 和 [2],后打印 [3] 和 [4]。
  2. 实例引用的连续性:__new__ 生成的 id(instance) 与传递进 __init__ 的 id(self) 完全一致,证实了 self 正是 __new__ 的返回值。

关键转折实验:如果 __new__ 返回了非本类对象?

这是 Python 试题与实战中最容易被忽视的机制。根据 Python 官方规范:只有当 __new__ 返回当前类 cls 的实例时,Python 才会调用 __init__。

class Other:
    pass

class Strange:
    def __new__(cls):
        print("Strange.__new__ 被调用")
        # 故意返回 Other 类的实例
        return Other()

    def __init__(self):
        print("Strange.__init__ 被调用(此行不会执行)")

s = Strange()
print("最终获得对象类型:", type(s))

运行结果示例:

Strange.__new__ 被调用
最终获得对象类型: <class '__main__.Other'>

可以看到,Strange.__init__ 根本没有被触发!这是因为 Python 解释器发现 __new__ 返回的对象不是 Strange 的实例,因此认为对其调用 Strange.__init__ 是不安全的。

四、__new__ 的两大经典实战场景

场景 1:继承不可变内置类型(int, str, tuple, float)

在 Python 中,int、str、tuple 等是不可变对象(Immutable Objects)。一旦创建完成,其底层的内存数值就无法更改。如果你希望自定义一个大写字符串类型,在 __init__ 中进行字符串转换是毫无意义的,因为字符串在传入 __init__ 之前就已经固化。

Python __new__与__init__的区别及执行顺序详解
图 3:不可变对象修改拦截机制与正确构造路径

实现自定义大写字符串的代码示例:

class UpperStr(str):
    def __new__(cls, value):
        # 在对象生成前将原始字符串转换为大写
        upper_val = str(value).upper()
        # 委托父类 str 生成不可变实例
        return super().__new__(cls, upper_val)

s = UpperStr("antigravity")
print(s)            # 输出: ANTIGRAVITY
print(type(s))      # 输出: <class '__main__.UpperStr'>

场景 2:实现单例模式(Singleton Pattern)

单例模式要求某个类在整个进程中只存在一个实例。如果由 __init__ 负责控制,由于 __init__ 被调用时对象早已在内存中创建完毕,根本无法阻止新对象的生成。

通过重写 __new__,我们可以在分配内存之前检查类属性中是否已存在缓存的实例。如果存在,则直接返回该缓存引用;只有在首次调用时才真正创建:

class DatabasePool:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            # 首次实例化:调用父类分配内存
            cls._instance = super().__new__(cls)
        # 后续实例化:直接返回同一实例引用
        return cls._instance

    def __init__(self, host="127.0.0.1"):
        self.host = host

p1 = DatabasePool("192.168.1.10")
p2 = DatabasePool("10.0.0.1")

print("是否为同一实例:", p1 is p2)  # 输出: True
print("p1 内存地址:", id(p1))
print("p2 内存地址:", id(p2))
单例模式下的关键隐患:__init__ 会被反复执行!
注意上面的代码:尽管 p1 is p2 确实为 True,但由于 __new__ 每次返回的都是 DatabasePool 的实例,Python 解释器在每次执行 DatabasePool(...) 时都会再次调用 __init__,导致 self.host 被后来的参数覆盖!解决办法是在类内部增加一个 _initialized 标志,在 __init__ 中判断若已初始化则直接 return。

五、开发中极易踩坑的三大注意事项

  • 注意 1:__init__ 严禁返回任何非 None 值
    写 def __init__(self): return self 会直接触发 TypeError: __init__() should return None, not '...'。因为 __init__ 的职责就是静默填充属性,不需要也不允许向调用方传递新对象。
  • 注意 2:object.__new__ 的参数陷阱
    在继承自 object(Python 3 中的所有新式类)时,如果不重写 __init__,object.__new__(cls, *args, **kwargs) 会报错拒绝接收额外参数。因此在自定义 __new__ 中通常只写 super().__new__(cls),而不把 *args, **kwargs 往下传给 object.__new__。
  • 注意 3:不要混淆类方法与静态方法
    __new__ 在形式上接收 cls,很像 @classmethod,但在底层 CPython 中它是作为特殊静态方法(static method with special lookup)处理的,无需在方法前添加 @staticmethod 或 @classmethod 装饰器。
Python __new__与__init__的区别及执行顺序详解
图 4:方法选择决策树与核心防坑速查图

六、总结

__new__ 和 __init__ 共同构成了 Python 优雅而完备的对象构建体系:

在绝大多数日常业务开发中,我们只需要使用 __init__ 完成常规的状态挂载与依赖注入;但在需要精细控制对象构造、实现高性能单例或扩展不可变核心类型的底层库开发中,深刻理解 __new__ 的运行机制与执行时机,将赋予我们直接操纵对象创生阶段的强大能力。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。