Какова роль и в чем разница между методами __new__ и __init__ в Python

Ответ

Методы __new__ и __init__ — это два ключевых этапа в жизненном цикле создания объекта в Python, но они выполняют принципиально разные задачи.

Ключевое различие: __new__ создает экземпляр, а __init__ его инициализирует (настраивает).

__new__(cls, ...)

  • Роль: Создание (construction) объекта. Отвечает за выделение памяти под новый экземпляр.
  • Вызывается: Первым, до __init__.
  • Тип: Статический метод (хотя и не требует декоратора @staticmethod), первым аргументом принимает класс cls.
  • Возвращаемое значение: Должен вернуть новый экземпляр класса. Если он этого не сделает, __init__ не будет вызван.

__init__(self, ...)

  • Роль: Инициализация (initialization) объекта. Заполняет атрибуты уже созданного экземпляра.
  • Вызывается: Вторым, после __new__.
  • Тип: Метод экземпляра, первым аргументом принимает созданный экземпляр self.
  • Возвращаемое значение: Ничего не возвращает (None).

Пример, демонстрирующий порядок вызова:

class MyClass:
    def __new__(cls, *args, **kwargs):
        print(f"1. Вызов __new__ для класса {cls.__name__}")
        # Делегируем создание экземпляра родительскому классу object
        instance = super().__new__(cls)
        return instance

    def __init__(self, value):
        print(f"2. Вызов __init__ для экземпляра {id(self)}")
        self.value = value

obj = MyClass(10)
print(f"3. Объект создан: {obj.value}")

Вывод:

1. Вызов __new__ для класса MyClass
2. Вызов __init__ для экземпляра 140701234567890
3. Объект создан: 10

Когда использовать __new__?

Переопределение __new__ требуется редко. Основные случаи — реализация паттерна Singleton или создание неизменяемых (immutable) типов, наследуемых от str, int, tuple.

Ответ 18+ 🔞

А, ну вот, смотри, объясняю как для дебила, но ты не обижайся, я сам когда-то нихуя не понимал. Есть в Питоне два таких метода, __new__ и __init__, которые постоянно путают, как близнецов-уродов. А разница-то, блядь, принципиальная, как между родить ребёнка и потом ему имя дать.

Вот представь, __new__ — это как сперматозоид с яйцеклеткой встречается, нихуя себе, зачатие, создание новой сущности, выделение памяти под неё, вся эта техническая магия. А __init__ — это ты уже родившемуся младенцу памперс надеваешь, имя записываешь и в кроватку кладёшь. Инициализация, ёпта! Без первого — второго просто не будет, потому что инициализировать-то нечего, пиздец.

__new__(cls, ...) — Создатель, Архитектор, Отец родной

  • Чем занимается: Создаёт объект, внатуре. Выделяет память, колдует, материализует экземпляр из пустоты. Это статический метод, ему похуй на self, он работает с классом (cls).
  • Когда вызывается: Самый первый, ещё до всех этих ваших инициализаций.
  • Что должен сделать: Вернуть, блядь, новый экземпляр. Если он вернёт None или какую-то хуйню — __init__ даже не чихнёт в его сторону. Всё, накрылся медным тазом.

__init__(self, ...) — Отдел кадров, Дизайнер интерьеров, Нянька

  • Чем занимается: Настраивает уже готовый объект. Вот тебе self — держи, это твоё новое тело. Наполняй его атрибутами, настраивай, делай красиво.
  • Когда вызывается: Сразу после __new__, если тот не обосрался.
  • Что должен сделать: Ничего не возвращает. Просто поработал с self и всё, доволен как слон.

Вот смотри, живой пример, чтоб вообще всё стало ясно, как божий день:

class MyClass:
    def __new__(cls, *args, **kwargs):
        print(f"1. Вызов __new__ для класса {cls.__name__}")
        # Тут мы, сука, просим у родительского object: 'дай, блядь, памяти!'
        instance = super().__new__(cls)
        return instance

    def __init__(self, value):
        print(f"2. Вызов __init__ для экземпляра {id(self)}")
        self.value = value

obj = MyClass(10)
print(f"3. Объект создан: {obj.value}")

Что на экране будет:

1. Вызов __new__ для класса MyClass
2. Вызов __init__ для экземпляра 140701234567890
3. Объект создан: 10

Видишь порядок? Сначала __new__ материализовал объект, потом __init__ его причесал и дал ему value = 10.

А зачем вообще __new__ трогать, если он такой скрытный?

Обычно — да, него не трогают, он сам справляется. Но есть моменты, когда без него — никуда. Например, если ты хочешь сделать Singleton (чтоб класс был один, как ядрёна вошь), или если ты наследуешься от встроенных неизменяемых типов вроде str или tuple — там без переопределения __new__ нихуя не получится, потому что создание таких объектов — это особая, ебаная магия.

Короче, запомни: __new__ — рожает, __init__ — воспитывает. И не путай их, а то получится, как в той поговорке: начал за здравие, а кончил за ебись-колотись.