Какой обязательный первый параметр метода `__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.