Как в Python отменить или изменить создание экземпляра класса в методе __new__

Ответ

Да, метод __new__ в Python позволяет контролировать процесс создания экземпляра. В отличие от __init__, который инициализирует уже созданный объект, __new__ его создает и возвращает.

Чтобы отменить или изменить создание, __new__ может вернуть:

  • Существующий экземпляр: Это основной механизм для реализации паттерна Singleton.
  • Экземпляр другого класса: Можно перенаправить создание на другой класс.
  • None: Технически возможно, но это приведет к ошибке TypeError, так как последующий вызов __init__ получит None вместо ожидаемого экземпляра.

Важно: Метод __init__ вызывается только в том случае, если __new__ возвращает экземпляр того же класса (cls).

Пример (Singleton):

class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        # Если экземпляр еще не создан
        if cls._instance is None:
            print("Создание нового экземпляра")
            # Создаем его с помощью родительского __new__
            cls._instance = super().__new__(cls)
        else:
            print("Возврат существующего экземпляра")

        # Всегда возвращаем единственный экземпляр
        return cls._instance

    def __init__(self):
        # Этот код будет выполняться при каждом вызове Singleton(),
        # даже если возвращается старый экземпляр.
        print("Вызов __init__")

s1 = Singleton()
# Вывод:
# Создание нового экземпляра
# Вызов __init__

s2 = Singleton()
# Вывод:
# Возврат существующего экземпляра
# Вызов __init__

print(s1 is s2) # True

Ответ 18+ 🔞

Слушай, а вот этот ваш __new__ в Python — это вообще магия какая-то, блядь! Ну, типа, все знают про __init__, который как бы «инициализирует» объект, а этот гадёныш __new__ — он его вообще создаёт, сука, с нуля! И может такое вытворять, что мама не горюй.

Вот смотри, если совсем по-простому: __new__ может вернуть тебе три варианта, и от этого зависит, будет ли вообще объект или нет.

  1. Вернуть уже существующий объект. Это классика, блядь, для этих ваших синглтонов, когда нужно, чтобы класс был один, как перст, на весь проект. Хули плодить сущности?
  2. Вернуть объект другого класса. Ну, подменил, сука, как в детстве конфету на камушек. Запросили MyClass, а получили OtherClass. Сюрприз, пиздец!
  3. Вернуть None. Ну, это уже совсем жесть. Технически можно, но потом __init__ получит в руки этот None и обосрётся с ошибкой TypeError. Так что не рекомендую, если только специально не хотите всё сломать, блядь.

И главное, запомни, ёпта: __init__ вызовется только если __new__ вернул объект ТОГО ЖЕ самого класса (cls). Если вернул что-то другое — иди нахуй со своей инициализацией, её не будет.

Вот, смотри пример, как сделать эту самую «одиночку» (Singleton), чтобы все твои вызовы класса возвращали один и тот же объект, а не плодили клонов, как сука тараканов:

class Singleton:
    _instance = None  # Тут будем хранить наш единственный экземпляр, блядь

    def __new__(cls, *args, **kwargs):
        # Если экземпляра ещё нет — создаём
        if cls._instance is None:
            print("Создание нового экземпляра")
            # Вот тут магия: вызываем __new__ у родительского класса (обычно object)
            cls._instance = super().__new__(cls)
        else:
            print("Возврат существующего экземпляра")

        # И всегда, сука, всегда возвращаем один и тот же объект
        return cls._instance

    def __init__(self):
        # Внимание! Этот __init__ будет вызываться КАЖДЫЙ РАЗ, когда ты пишешь Singleton(),
        # даже если объект старый. Так что тут осторожнее, а то наинациализируешься.
        print("Вызов __init__")

# Пробуем
s1 = Singleton()
# Вывод:
# Создание нового экземпляра
# Вызов __init__

s2 = Singleton()
# Вывод:
# Возврат существующего экземпляра
# Вызов __init__

print(s1 is s2) # True, блядь! Это один и тот же объект!

Вот и вся магия, ёпта. __new__ — это как прораб на стройке объекта: он решает, строить новый дом или выдать ключи от уже готового. А __init__ — это уже дизайнер интерьеров, которого пускают внутрь, только если прораб сказал «да».