Ответ
Метод __new__ в Python отвечает за создание экземпляра класса, в то время как __init__ отвечает за его инициализацию. __new__ вызывается перед __init__ и должен вернуть новый экземпляр класса.
Это низкоуровневый метод, который используется редко, но необходим в следующих случаях:
- Реализация паттерна Singleton: Гарантирует, что у класса будет только один экземпляр.
- Наследование от неизменяемых (immutable) типов: При наследовании от таких типов, как
str,intилиtuple, необходимо переопределить__new__, так как изменить их после создания уже нельзя. - Создание объектов-заместителей: Когда нужно вернуть экземпляр другого класса в зависимости от переданных аргументов (реализация фабричного метода).
Пример реализации Singleton:
class DatabaseConnection:
_instance = None
def __new__(cls, *args, **kwargs):
# Если экземпляр еще не создан
if not cls._instance:
# Создаем новый экземпляр, вызывая __new__ родительского класса
cls._instance = super().__new__(cls)
# Возвращаем существующий или только что созданный экземпляр
return cls._instance
def __init__(self, dsn):
# Этот метод будет вызываться каждый раз,
# но на одном и том же экземпляре.
self.dsn = dsn
# Оба вызова вернут один и тот же объект
db1 = DatabaseConnection("user:pass@host1")
db2 = DatabaseConnection("user:pass@host2")
print(id(db1) == id(db2)) # Вывод: True
print(db1.dsn) # Вывод: user:pass@host2
Ключевое отличие от __init__:
__new__(cls, ...): Первый аргумент — классcls. Создает и возвращает экземпляр. Является статическим методом.__init__(self, ...): Первый аргумент — экземплярself. Ничего не возвращает. Инициализирует уже созданный экземпляр.
Ответ 18+ 🔞
Ах ты ж ёпта, ну и тема подъехала! Про __new__ и __init__. Слушай, это ж как в строительстве, блядь: один — прораб, который заливает фундамент и ставит коробку дома, а второй — дизайнер-отделочник, который потом обои клеит и унитаз прикручивает.
Вот __new__ — это тот самый прораб, мудак скрытный. Он вызывается первым, его задача — взять чертежи (класс) и вернуть тебе голый, сырой объект. Вообще нихуя не инициализированный, просто кусок памяти. Статический метод, чистая магия, блядь.
А __init__ — это уже наш отделочник. Он получает готовую коробку (self) и начинает её обустраивать: атрибуты навешивать, значения присваивать. Ничего не возвращает, только в подаренном пространстве хозяйничает.
Зачем этот __new__ вообще, спросишь? Ну, обычно он сам всё делает под капотом, тебе париться не надо. Но бывают моменты, когда без его переопределения — просто пиздец.
- Singleton (Один-на-хуй экземпляр). Чтоб класс как сука упрямая плодился только в одном экземпляре.
- Наследование от неизменяемых типов. Ну, типа
str,int,tuple. Их после создания не перепишешь, так что всю логику создания надо в__new__засунуть, иначе нихуя не выйдет. - Фабрика или подмена объекта. Когда в зависимости от аргументов нужно вернуть объект вообще другого класса. Хитрая жопа, но бывает.
Вот тебе пример, как сделать так, чтобы класс был один, как мой хуй в бане:
class DatabaseConnection:
_instance = None # Тут будем хранить нашу одну-единственную прелесть
def __new__(cls, *args, **kwargs):
# Если нашей прелести ещё нет...
if not cls._instance:
# То вызываем __new__ у родителя (object), чтобы создать эту самую прелесть
cls._instance = super().__new__(cls)
# А вот и она! Возвращаем её, даже если нас зовут сто раз.
return cls._instance
def __init__(self, dsn):
# ВНИМАНИЕ, ПОДВОХ! Этот __init__ будет вызываться КАЖДЫЙ РАЗ при "создании".
# Поэтому последний вызов перетрет dsn у нашего единственного экземпляра.
self.dsn = dsn
# Пытаемся создать два соединения
db1 = DatabaseConnection("user:pass@host1")
db2 = DatabaseConnection("user:pass@host2")
print(id(db1) == id(db2)) # True. Это один и тот же объект, ёбана!
print(db1.dsn) # Выведет "user:pass@host2". Потому что __init__ отработал на том же объекте второй раз и перезаписал dsn. Вот такая, блядь, особенность.
Итог, блядь:
__new__(cls, ...)— создаёт. Возвращает объект. Вызывается раз.__init__(self, ...)— настраивает. Ничего не возвращает. Может вызываться на уже существующем объекте (как в синглтоне).
В 95% случаев тебе хватит __init__. Но когда полезешь в метаклассы или в наследование от tuple — вспомни про этого скрытного прораба, __new__. Он тебе, сука, жизнь спасёт.