← Назад к вопросам

Какой магический метод используется при инициализации класса?

1.0 Junior🔥 251 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Магические методы инициализации класса в Python

В Python есть два ключевых магических метода для инициализации класса: new и init. Они работают на разных уровнях и имеют разные назначения.

init — инициализация экземпляра

init — это основной метод инициализации, который используется в 99% случаев. Он вызывается после создания объекта и используется для установки начальных значений атрибутов.

class User:
    def __init__(self, name, email):
        # Инициализируем атрибуты экземпляра
        self.name = name
        self.email = email
        self.created_at = datetime.now()
        print(f"Инициализирован пользователь {name}")

# Использование
user = User("Alice", "alice@example.com")
# Вывод: Инициализирован пользователь Alice
print(user.name)  # Alice
print(user.email)  # alice@example.com

new — создание экземпляра

new — это метод, который вызывается перед init и отвечает за создание нового экземпляра класса. Он возвращает новый объект. Используется редко, для специальных случаев.

class User:
    def __new__(cls, name, email):
        # Создаём экземпляр класса
        print(f"Создаётся новый экземпляр класса {cls.__name__}")
        instance = super().__new__(cls)
        return instance
    
    def __init__(self, name, email):
        print(f"Инициализируется экземпляр")
        self.name = name
        self.email = email

# Использование
user = User("Alice", "alice@example.com")
# Вывод:
# Создаётся новый экземпляр класса User
# Инициализируется экземпляр

Порядок вызова

class Example:
    def __new__(cls):
        print("1. __new__ - создание объекта")
        return super().__new__(cls)
    
    def __init__(self):
        print("2. __init__ - инициализация объекта")

# obj = Example()
# Вывод:
# 1. __new__ - создание объекта
# 2. __init__ - инициализация объекта

Практические примеры

1. Использование init (стандартный случай)

class Product:
    def __init__(self, name, price, quantity=1):
        self.name = name
        self.price = price
        self.quantity = quantity
        self.total = price * quantity
    
    def __repr__(self):
        return f"Product({self.name}, {self.price})"

product = Product("Laptop", 1000, 2)
print(product.total)  # 2000

2. Использование new для синглтона

class Singleton:
    _instance = None
    
    def __new__(cls):
        # Если экземпляр уже существует, возвращаем его
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self):
        self.value = 42

# Использование
obj1 = Singleton()
obj2 = Singleton()
print(obj1 is obj2)  # True (один и тот же объект)

3. Использование new для контроля типа

class JSONValue:
    def __new__(cls, value):
        # Если значение число, возвращаем int/float
        if isinstance(value, int):
            return value
        # Если строка, возвращаем str
        if isinstance(value, str):
            return value
        # Иначе создаём объект класса
        instance = super().__new__(cls)
        return instance
    
    def __init__(self, value):
        # __init__ вызовется только если это объект нашего класса
        self.value = value

# Использование
val1 = JSONValue(42)      # Вернёт int, не объект класса
val2 = JSONValue("text")   # Вернёт str, не объект класса
val3 = JSONValue([1, 2])   # Вернёт объект JSONValue

print(type(val1))  # <class 'int'>
print(type(val2))  # <class 'str'>
print(type(val3))  # <class '__main__.JSONValue'>

4. Использование new с наследованием

class Base:
    def __init__(self, value):
        self.value = value

class Derived(Base):
    def __new__(cls, value):
        # Специальная логика при создании
        if value < 0:
            raise ValueError("Value must be positive")
        return super().__new__(cls)
    
    def __init__(self, value):
        super().__init__(value)
        self.processed = True

# Использование
try:
    obj = Derived(-5)  # Ошибка в __new__
except ValueError as e:
    print(f"Error: {e}")

obj = Derived(10)  # Успешно
print(obj.value)  # 10
print(obj.processed)  # True

5. Использование new для модификации параметров

class Rectangle:
    def __new__(cls, width, height):
        # Если это квадрат, создаём специальный объект
        if width == height:
            print("Создание квадрата")
            return super().__new__(cls)
        else:
            print("Создание прямоугольника")
            return super().__new__(cls)
    
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    @property
    def area(self):
        return self.width * self.height

# Использование
square = Rectangle(5, 5)      # Создание квадрата
rect = Rectangle(10, 5)        # Создание прямоугольника

Когда использовать new

Используй new:

  • Создание синглтонов
  • Кэширование экземпляров
  • Создание неизменяемых объектов (immutable)
  • Контроль типа возвращаемого значения
  • Специальные фабричные методы
class ImmutablePoint:
    _cache = {}
    
    def __new__(cls, x, y):
        # Кэшируем объекты чтобы не создавать дубликаты
        key = (x, y)
        if key not in cls._cache:
            instance = super().__new__(cls)
            instance.x = x
            instance.y = y
            cls._cache[key] = instance
        return cls._cache[key]

# Использование
p1 = ImmutablePoint(1, 2)
p2 = ImmutablePoint(1, 2)
print(p1 is p2)  # True (один и тот же объект)
print(id(p1) == id(p2))  # True

Когда использовать init

Используй init:

  • В 99% случаев для инициализации атрибутов
  • Установка начальных значений
  • Валидация параметров
  • Инициализация подсистем
class Database:
    def __init__(self, host, port, username, password):
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.connection = None
        self.connect()
    
    def connect(self):
        print(f"Подключение к {self.host}:{self.port}")
        self.connection = True

db = Database("localhost", 5432, "user", "pass")
# Вывод: Подключение к localhost:5432

Альтернатива: используй classmethod для фабрики

class User:
    def __init__(self, username, email, role="user"):
        self.username = username
        self.email = email
        self.role = role
    
    @classmethod
    def admin(cls, username, email):
        return cls(username, email, role="admin")
    
    @classmethod
    def from_dict(cls, data):
        return cls(data["username"], data["email"], data.get("role", "user"))

# Использование
admin = User.admin("alice", "alice@example.com")
print(admin.role)  # admin

user_dict = {"username": "bob", "email": "bob@example.com", "role": "moderator"}
user = User.from_dict(user_dict)
print(user.role)  # moderator

Заключение

init — это магический метод, который используется при инициализации класса (установка атрибутов). new создаёт сам объект, но используется редко. В реальной разработке:

  1. Используй init для 99% инициализаций
  2. Используй new только для синглтонов, кэширования или специальных фабрик
  3. Предпочитай @classmethod методы для альтернативных конструкторов
Какой магический метод используется при инициализации класса? | PrepBro