编程高级 Python 开发者

__new__ 在 Python 中是如何工作的,和 __init__ 有何不同?何时以及为什么需要重写 __new__ 而不是 __init__?请提供详细的使用示例。

用 Hintsage AI 助手通过面试

答案。

在 Python 中,创建类的对象是一个两个步骤的过程:

  1. 首先调用静态方法 __new__,它负责分配和返回类的新实例(对象)。
  2. 然后调用 __init__,它初始化已经创建的对象。

区别:

  • __new__ — 创建(可以返回任何新的对象;必需参数是类)。
  • __init__ — 初始化(与 self 一起工作,self 是已经分配的实例)。
  • 通常为不可变类型的子类(tuple, str, int)重写 __new__,创建单例和“工厂”模式。

使用示例:

class MyStr(str): def __new__(cls, value): print("__new__ 被调用") instance = super().__new__(cls, value.upper()) # 在创建之前修改值 return instance def __init__(self, value): print("__init__ 被调用", value) s = MyStr('abc') # __new__ 被调用 -> __init__ 被调用 abc print(s) # 'ABC'

有陷阱的问题。

如果类中没有实现 __new__,能否通过 __init__ 初始化不可变对象?

示例答案:

不能!例如,对于不可变类型(如 str, int, tuple),对字段/值的任何更改必须在 __new__ 中进行,否则在 __init__ 中无法修改值。

class MyTuple(tuple): def __init__(self, items): print(f'__init__! {items}') def __new__(cls, items): print(f'__new__! {items}') return super().__new__(cls, map(str, items)) t = MyTuple([1, 2, 3]) print(t) # ('1', '2', '3')

由于对主题细微差别的无知而导致的实际错误示例


故事

在一个大型的 Django 项目中,实现了从 str 继承以存储特殊类型的字符串。尝试在 __init__ 中修改值——但结果没有改变,因为 str 是不可变的。只有在研究和重写 __new__ 后才修复。


故事

在实现单例模式时,忘记在 __new__ 中实现返回现有实例的逻辑:多次调用类仍然创建新的对象,导致内存碎片化。


故事

在用于数据序列化的库中,在缓存时使用了重写了 __init__ 的类,但没有注意到缓存需要通过 __new__ 返回相同的对象。因此,缓存在每次新调用时都坏了,因为创建了不同的对象。