在 Python 中,创建类的对象是一个两个步骤的过程:
__new__,它负责分配和返回类的新实例(对象)。__init__,它初始化已经创建的对象。__new__ — 创建(可以返回任何新的对象;必需参数是类)。__init__ — 初始化(与 self 一起工作,self 是已经分配的实例)。__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__ 返回相同的对象。因此,缓存在每次新调用时都坏了,因为创建了不同的对象。