Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Метод new в Python
new — это специальный метод, который создаёт новый экземпляр класса ДО того, как вызовется init. Это метод более низкого уровня, чем init.
Разница между new и init
class MyClass:
def __new__(cls, value):
"""Создаёт объект (до инициализации)"""
print(f"1. __new__ вызван с cls={cls}, value={value}")
instance = super().__new__(cls) # Создаём объект
return instance
def __init__(self, value):
"""Инициализирует объект (после создания)"""
print(f"2. __init__ вызван с self={self}, value={value}")
self.value = value
obj = MyClass(42)
# Вывод:
# 1. __new__ вызван с cls=<class MyClass>, value=42
# 2. __init__ вызван с self=<__main__.MyClass object>, value=42
Порядок вызова:
- new создаёт пустой объект
- init инициализирует его атрибуты
Практическое применение new
1. Singleton паттерн (одиночка)
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
print("Создаём единственный объект")
cls._instance = super().__new__(cls)
else:
print("Возвращаем существующий объект")
return cls._instance
def __init__(self):
self.name = "Singleton"
obj1 = Singleton() # "Создаём единственный объект"
obj2 = Singleton() # "Возвращаем существующий объект"
print(obj1 is obj2) # True — один и тот же объект!
2. Контроль типа возвращаемого объекта
class Shape:
def __new__(cls, shape_type):
if shape_type == 'circle':
return Circle()
elif shape_type == 'square':
return Square()
else:
raise ValueError(f"Unknown shape: {shape_type}")
class Circle:
def draw(self):
return "Drawing circle"
class Square:
def draw(self):
return "Drawing square"
shape = Shape('circle')
print(shape.draw()) # "Drawing circle"
print(type(shape)) # <class Square> или <class Circle>
3. Иммутабельные объекты (tuple, str, int)
class ImmutablePoint:
def __new__(cls, x, y):
"""Создаём иммутабельный объект с замороженными значениями"""
instance = super().__new__(cls)
instance.x = x
instance.y = y
return instance
def __init__(self, x, y):
pass # __init__ не меняет атрибуты, они уже созданы в __new__
def __setattr__(self, name, value):
"""Запретить изменение атрибутов"""
if hasattr(self, name):
raise AttributeError(f"Cannot modify {name}")
super().__setattr__(name, value)
point = ImmutablePoint(1, 2)
print(point.x, point.y) # 1 2
# point.x = 5 # AttributeError: Cannot modify x
4. Кэширование объектов (например, для числовых типов)
class CachedNumber:
_cache = {}
def __new__(cls, value):
"""Кэшируем объекты для часто используемых значений"""
if value in cls._cache:
print(f"Возвращаем кэшированный объект для {value}")
return cls._cache[value]
print(f"Создаём новый объект для {value}")
instance = super().__new__(cls)
cls._cache[value] = instance
return instance
def __init__(self, value):
self.value = value
n1 = CachedNumber(1) # "Создаём новый объект для 1"
n2 = CachedNumber(1) # "Возвращаем кэшированный объект для 1"
print(n1 is n2) # True!
5. Переопределение типа возвращаемого значения
class PythonInt(int):
"""Расширенный int, который удваивает значение"""
def __new__(cls, value):
# Для immutable типов (int, str, tuple) нужно передать значение в super().__new__
instance = super().__new__(cls, value * 2)
return instance
n = PythonInt(5)
print(n) # 10 — удвоено в __new__!
print(type(n)) # <class PythonInt>
new vs init
| Аспект | new | init |
|---|---|---|
| Что делает | Создаёт объект | Инициализирует объект |
| Возвращает | Новый экземпляр | None |
| Вызывается | До init | После new |
| Использует super() | super().new(cls) | super().init() |
| Когда использовать | Контроль создания | Инициализация атрибутов |
| С immutable типами | ОБЯЗАТЕЛЕН | Опционален |
Важные детали
1. new ВСЕГДА возвращает экземпляр (или None для отмены)
class NoInstance:
def __new__(cls):
print("__new__ вызван")
return None # Отменяем создание
def __init__(self):
print("__init__ вызван")
obj = NoInstance()
print(obj) # None (и __init__ НЕ вызовется!)
2. new СТАТИЧЕСКИЙ метод (по сути)
class MyClass:
def __new__(cls): # cls — это ключевой параметр, не self!
return super().__new__(cls)
3. Если new вернёт другой класс, init не вызовется
class Parent:
def __new__(cls):
print(f"__new__ создаёт {cls}")
return super().__new__(cls)
def __init__(self):
print("__init__ инициализирует Parent")
class Child(Parent):
pass
class Other:
def __init__(self):
print("__init__ инициализирует Other")
class Tricky(Parent):
def __new__(cls):
return Other() # Возвращаем OTHER тип!
def __init__(self):
print("__init__ Tricky (не вызовется)")
obj = Tricky()
# Вывод:
# __new__ создаёт Tricky
# __init__ инициализирует Parent (вызывается, но на объекте Other!)
Когда использовать new
✅ Используй new для:
- Singleton паттерна
- Контроля типа возвращаемого объекта
- Иммутабельных классов
- Кэширования объектов
- Работы с immutable типами (int, str, tuple)
❌ НЕ используй new для:
- Обычной инициализации атрибутов (используй init)
- Простых операций (усложняет код)
- Если не уверен (это низкоуровневая магия)
Реальный пример: собственный класс конфига
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
if not hasattr(self, 'loaded'):
self.loaded = True
self.db_url = "postgresql://localhost:5432/mydb"
self.debug = True
config1 = Config()
config2 = Config()
print(config1 is config2) # True — один объект на весь приложение
Итог
new — низкоуровневый метод для контроля СОЗДАНИЯ объектов:
- Вызывается ДО init
- Возвращает экземпляр класса
- Нужен для singleton, иммутабельных типов, кэширования
- В 95% случаев достаточно init — используй new только если точно знаешь зачем