文章目录

Python __init__和__new__的区别:什么时候需要重写__new__

发布于 2026-04-29 19:23:41 · 浏览 3 次 · 评论 0 条

Python 中 __init____new__ 的区别常常让初学者感到困惑。简单来说,__new__ 负责对象的创建(构造),而 __init__ 负责对象的初始化。理解这两者的分工是掌握 Python 类机制的关键。


1. 理解对象创建的生命周期

在 Python 中,当你调用一个类(例如 MyClass())来实例化对象时,幕后发生了两个步骤。

为了清晰地展示这个流程,请查看下面的流程图:

graph LR A["调用 MyClass()"] --> B["调用 __new__(cls, *args, **kwargs)"] B --> C{"返回值是否为 None?"} C -- 否 --> D["调用 __init__(self, *args, **kwargs)"] D --> E["返回实例对象"] C -- 是 --> F["返回 None"]

分析上述流程:

  1. Python 首先调用 __new__ 方法。这是一个静态方法,负责申请内存空间并创建对象。
  2. __new__ 方法必须返回一个实例对象。
  3. 如果 __new__ 返回的是该类的实例,Python 会自动调用 __init__ 方法,并将这个实例作为 self 传入。
  4. 如果 __new__ 返回了其他类的实例或者 None,则 __init__ 方法不会被调用。

2. 对比核心区别

为了更直观地理解二者的差异,请参考下表。

特性 __new__ __init__
职责 创建对象(分配内存) 初始化对象(设置属性)
性质 静态方法 实例方法
参数 第一个参数是 cls(类本身) 第一个参数是 self(实例对象)
返回值 必须返回一个实例对象 通常返回 None
执行顺序 先执行 后执行

3. 基础代码演示

大多数情况下,你只需要重写 __init__。因为 Python 默认的 __new__ 方法已经足够处理对象的创建。

编写以下代码,观察标准流程:

class Demo:
    def __new__(cls, *args, **kwargs):
        print("1. __new__ 被调用")
        # 必须调用父类的 __new__ 方法来创建对象
        instance = super().__new__(cls)
        return instance

    def __init__(self, name):
        print("2. __init__ 被调用")
        self.name = name

# 实例化
d = Demo("Test")

运行代码后,你会看到两个方法依次执行。此时 __new__ 只是简单地把创建工作交给了父类。


4. 什么时候需要重写 __new__

当你需要控制对象的创建过程,而不仅仅是初始化属性时,就需要重写 __new__。主要有以下三种常见场景。

场景一:实现单例模式

单例模式确保一个类只有一个实例。如果你只在 __init__ 中做判断,是无法阻止多次创建对象的,因为 __new__ 每次都会分配新的内存。你必须拦截 __new__ 的创建过程。

重写 __new__ 方法来实现单例:

class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        # 检查类属性 _instance 是否为空
        if cls._instance is None:
            # 如果为空,调用父类 __new__ 创建对象并缓存
            cls._instance = super().__new__(cls)
        # 无论是否第一次创建,都返回同一个对象
        return cls._instance

    def __init__(self, name):
        self.name = name

# 测试
s1 = Singleton("A")
s2 = Singleton("B")

print(s1 is s2)  # 输出: True
print(s1.name)   # 输出: B (注意:__init__ 会被调用两次,属性被覆盖)

注意:在这个例子中,虽然 s1s2 是同一个对象,但 __init__ 仍然被调用了两次。如果希望 __init__ 也只执行一次,你需要在 __new__ 中增加额外的判断逻辑。

场景二:继承不可变类型

如果你想要自定义一个继承自 strint 的类,并想在创建时修改它的值,你必须重写 __new__。因为不可变对象一旦创建就不能修改,而 __init__ 执行时对象已经存在,无法改变其值。

创建一个总是将文本转为大写的字符串类:

class UpperStr(str):
    def __new__(cls, string):
        # 在对象创建前修改数据
        new_string = string.upper()
        # 将修改后的数据传递给父类 __new__ 进行创建
        return super().__new__(cls, new_string)

# 测试
text = UpperStr("hello world")
print(text)  # 输出: HELLO WORLD

场景三:控制实例创建(例如工厂模式)

有时候,你希望根据传入的参数不同,返回不同类的实例,或者在某些条件下拒绝创建对象。

编写一个根据输入返回不同类型的示例:

class Animal:
    pass

class Dog(Animal):
    def speak(self):
        return "Woof"

class Cat(Animal):
    def speak(self):
        return "Meow"

class AnimalFactory:
    def __new__(cls, name):
        if name == "dog":
            return Dog()
        elif name == "cat":
            return Cat()
        else:
            return super().__new__(cls)

# 测试
a = AnimalFactory("dog")
print(a.speak())  # 输出: Woof
print(type(a))    # 输出: <class '__main__.Dog'>

在这个例子中,AnimalFactory("dog") 返回的并不是 AnimalFactory 的实例,而是 Dog 的实例。因此,AnimalFactory__init__ 不会被调用。


5. 常见错误与陷阱

在重写 __new__ 时,请务必遵守以下规则,否则会导致程序报错或行为异常。

  1. 必须返回实例__new__ 必须返回一个对象。如果你忘记写 return 或者返回了 None,Python 将不会调用 __init__,且你得到的变量值将是 None

    错误示例:

    class BadClass:
        def __new__(cls):
            super().__new__(cls) # 忘记 return
  2. 参数传递要一致__new__ 接收的参数(除了 cls)应该与 __init__ 接收的参数(除了 self)保持一致。因为实例化时传递的参数会同时传给这两个方法。

    class GoodClass:
        def __new__(cls, value):
            print(f"Creating with {value}")
            return super().__new__(cls)
    
        def __init__(self, value):
            print(f"Initializing with {value}")
            self.value = value
  3. 不要在 __new__ 中访问实例属性:在 __new__ 执行时,对象尚未完全构造完成,self 还不存在(虽然你可以拿到 cls),此时尝试给对象添加属性通常是不推荐的做法,属性初始化应全部放在 __init__ 中。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文