Python 中 __init__ 和 __new__ 的区别常常让初学者感到困惑。简单来说,__new__ 负责对象的创建(构造),而 __init__ 负责对象的初始化。理解这两者的分工是掌握 Python 类机制的关键。
1. 理解对象创建的生命周期
在 Python 中,当你调用一个类(例如 MyClass())来实例化对象时,幕后发生了两个步骤。
为了清晰地展示这个流程,请查看下面的流程图:
分析上述流程:
- Python 首先调用
__new__方法。这是一个静态方法,负责申请内存空间并创建对象。 __new__方法必须返回一个实例对象。- 如果
__new__返回的是该类的实例,Python 会自动调用__init__方法,并将这个实例作为self传入。 - 如果
__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__ 会被调用两次,属性被覆盖)
注意:在这个例子中,虽然 s1 和 s2 是同一个对象,但 __init__ 仍然被调用了两次。如果希望 __init__ 也只执行一次,你需要在 __new__ 中增加额外的判断逻辑。
场景二:继承不可变类型
如果你想要自定义一个继承自 str 或 int 的类,并想在创建时修改它的值,你必须重写 __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__ 时,请务必遵守以下规则,否则会导致程序报错或行为异常。
-
必须返回实例:
__new__必须返回一个对象。如果你忘记写return或者返回了None,Python 将不会调用__init__,且你得到的变量值将是None。错误示例:
class BadClass: def __new__(cls): super().__new__(cls) # 忘记 return -
参数传递要一致:
__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 -
不要在
__new__中访问实例属性:在__new__执行时,对象尚未完全构造完成,self还不存在(虽然你可以拿到cls),此时尝试给对象添加属性通常是不推荐的做法,属性初始化应全部放在__init__中。

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