Встречается на 33% собеседований по Python

Чем 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__ будет вызываться при каждом создании "нового" объекта, что может привести к повторной инициализации.

Пример

python
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__ не вызывается.
как отвечать на вопрос
пример собеседования
фреймворки на собеседовании
типичные вопросы junior
интервью вопросы и ответы