Чем init() отличается от new()
В Python __new__ и __init__ — это два специальных метода, которые управляют жизненным циклом объекта: __new__ отвечает за создание нового экземпляра, а __init__ — за его инициализацию. Они вызываются автоматически при создании объекта, но выполняют разные задачи и вызываются в строгой последовательности: сначала __new__, затем __init__.
Как это работает
__new__ — это статический метод (хотя он не объявляется с декоратором @staticmethod), который принимает первым аргументом класс, а не экземпляр. Он вызывается при вызове класса, например MyClass(), и должен вернуть новый экземпляр класса. Обычно это делается через вызов super().__new__(cls), но можно вернуть и объект другого класса, если это необходимо.
__init__ — это обычный метод экземпляра, который вызывается после __new__ и получает уже созданный объект в качестве аргумента self. Его задача — установить начальное состояние объекта, например, присвоить значения атрибутам. __init__ не должен возвращать ничего, кроме None (иначе возникнет ошибка TypeError).
Важно понимать: __init__ вызывается для каждого объекта, который возвращает __new__, даже если это один и тот же объект. Например, если вы реализуете синглтон и возвращаете один и тот же экземпляр из __new__, __init__ будет вызываться при каждом создании "нового" объекта, что может привести к повторной инициализации.
Пример
class Singleton:
_instance = None
def __new__(cls):
# Если экземпляр ещё не создан, создаём его
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, value):
# Этот метод будет вызываться каждый раз при Singleton(value)
self.value = value
# Создаём два "разных" объекта
s1 = Singleton(10)
s2 = Singleton(20)
print(s1 is s2) # True — это один и тот же объект
print(s1.value) # 20 — значение перезаписано вторым вызовомВ этом примере __new__ гарантирует, что всегда возвращается один и тот же экземпляр, но __init__ всё равно вызывается каждый раз, обновляя атрибут value.
Подводные камни
- Если
__new__возвращает объект, который не является экземпляром класса, то__init__не вызывается. Это может быть полезно для кэширования, но требует осторожности. - При наследовании важно правильно вызывать
super().__new__(cls), чтобы не нарушить цепочку создания объектов. - Переопределение
__new__без вызоваsuper().__new__может привести к тому, что объект не будет создан, и__init__не вызовется. - В Python 3
__new__автоматически становится статическим методом, поэтому не нужно добавлять@staticmethod.
Когда использовать
__new__ обычно используется в редких случаях, когда нужно контролировать процесс создания объекта: реализация синглтона, создание объектов неизменяемых типов (например, подклассов tuple или int), или возврат кэшированного экземпляра. В большинстве повседневных задач достаточно переопределить __init__.
Коротко
__new__создаёт объект,__init__его инициализирует.- Порядок вызова: сначала
__new__, потом__init__. __init__вызывается всегда, когда__new__возвращает экземпляр класса, даже если это один и тот же объект.- Если
__new__возвращает объект другого типа,__init__не вызывается.