Какой обязательный первый параметр метода `__new__` в Python?

Ответ

В Python обязательным первым параметром метода __new__ является cls. Этот параметр представляет собой ссылку на сам класс, для которого создается экземпляр. Он автоматически передается интерпретатором Python при вызове __new__.

Почему cls? Метод __new__ отвечает за создание и возврат нового экземпляра класса. Поскольку он вызывается до того, как экземпляр будет создан, ему нужна ссылка на класс, чтобы знать, какой тип объекта нужно создать. cls позволяет __new__ вызвать __new__ родительского класса (через super().__new__(cls)) или создать экземпляр другого класса, если это необходимо.

Пример использования __new__:

class Singleton:
    _instance = None

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

    def __init__(self, value):
        # __init__ вызывается каждый раз, даже для существующего экземпляра
        # Поэтому инициализацию, которая должна быть однократной, лучше делать в __new__
        if not hasattr(self, '_initialized'): # Проверяем, был ли уже инициализирован
            self.value = value
            self._initialized = True
            print(f"Инициализирован Singleton со значением: {self.value}")
        else:
            print(f"Singleton уже инициализирован. Текущее значение: {self.value}")

# Демонстрация
s1 = Singleton(10)
s2 = Singleton(20) # __init__ будет вызван, но _instance останется тем же

print(f"s1 is s2: {s1 is s2}")
print(f"s1.value: {s1.value}")
print(f"s2.value: {s2.value}")

Ключевые особенности __new__:

  • Вызов до __init__: __new__ вызывается первым и отвечает за создание самого объекта. __init__ затем инициализирует этот объект.
  • Возвращаемое значение: __new__ должен вернуть экземпляр класса (обычно cls или его подкласс). Если __new__ не возвращает экземпляр cls, то метод __init__ для этого экземпляра не будет вызван.
  • Статический характер: Хотя __new__ является методом класса, он не требует декоратора @classmethod или @staticmethod, так как его поведение как метода класса предопределено интерпретатором Python.

Ответ 18+ 🔞

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

А нахуя cls? __new__ — это тот самый пахан, который сначала лепит объект из глины, а уже потом __init__ его раскрашивает. И чтобы слепить, ему надо знать — из какой, сука, глины? Вот cls и говорит: «Вот из этой, епта!» Без него даже super().__new__(cls) не вызовешь — родительский класс не поймёт, какого хуя от него хотят.

Смотри, как это выглядит на практике:

class Singleton:
    _instance = None

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

    def __init__(self, value):
        # __init__ вызывается каждый раз, даже если экземпляр уже есть — вот же ж хитрая жопа!
        # Поэтому однократную инициализацию лучше делать в __new__, а тут проверять
        if not hasattr(self, '_initialized'):
            self.value = value
            self._initialized = True
            print(f"Инициализирован Singleton со значением: {self.value}")
        else:
            print(f"Singleton уже инициализирован. Текущее значение: {self.value}")

# Демонстрация
s1 = Singleton(10)
s2 = Singleton(20)  # __init__ вызовется, но _instance останется тем же — классика, блядь!

print(f"s1 is s2: {s1 is s2}")
print(f"s1.value: {s1.value}")
print(f"s2.value: {s2.value}")

Важные моменты, чтобы не обосраться:

  • Порядок вызова: Сначала __new__ — он создаёт объект, потом __init__ — он его наряжает. Если __new__ вернёт какую-то хуйню, а не экземпляр класса, то __init__ просто проигнорится — и правильно, нахуй так делать?
  • Что возвращать: __new__ обязан вернуть экземпляр класса (обычно cls). Может, конечно, и другой класс подсунуть — но это уже извращения, на свой страх и риск.
  • Статика: __new__ и так считается методом класса, не нужно ему никаких @classmethod вешать — питон сам всё понимает, ёпта.