В Python создание объекта класса — двухэтапный процесс:
__new__, который отвечает за выделение и возврат нового экземпляра класса (инстанса).__init__, который инициализирует уже созданный объект.__new__ — создание (можно вернуть любой новый объект; обязательный параметр — класс).__init__ — инициализация (работает с self, уже выделенным экземпляром).__new__ переопределяется для наследников неизменяемых типов (tuple, str, int), создания синглтонов и паттерна "фабрика".class MyStr(str): def __new__(cls, value): print("__new__ called") instance = super().__new__(cls, value.upper()) # изменяем значение до создания return instance def __init__(self, value): print("__init__ called", value) s = MyStr('abc') # __new__ called -> __init__ called abc print(s) # 'ABC'
Можно ли инициализировать неизменяемый объект через __init__, если в классе не реализован __new__?
Нет! Например, для неизменяемых типов (как у 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__.
История
При реализации паттерна Singleton забыли реализовать логику возврата существующего инстанса в __new__: несколько вызовов класса по-прежнему создавали новые объекты, что приводило к фрагментации памяти.
История
В библиотеке для сериализации данных при кэшировании использовали класс с переопределённым __init__, но не уследили, что для кэширования нужен именно возврат одного и того же объекта через __new__. Из-за этого кэш ломался при каждом новом вызове, потому что создавались разные объекты.