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

Как вызываются магические (dunder) методы?

1.2 Junior🔥 191 комментариев
#Python Core

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

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

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

Что такое магические методы

Магические (dunder) методы — это специальные методы в Python, имена которых начинаются и заканчиваются двойным подчёркиванием (__method__). Они позволяют переопределять поведение встроенных операций для пользовательских объектов и делают классы более "pythonic".

Вызов магических методов

Магические методы вызываются не напрямую (хотя технически это возможно), а автоматически интерпретатором Python при выполнении определённых операций:

1. Инициализация и конструктор

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __new__(cls, x, y):
        # Вызывается перед __init__ для создания экземпляра
        return super().__new__(cls)

# Вызов через конструктор
p = Point(1, 2)  # __new__ → __init__

2. Арифметические операции

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __add__(self, other):  # v1 + v2
        return Vector(self.x + other.x, self.y + other.y)
    
    def __sub__(self, other):  # v1 - v2
        return Vector(self.x - other.x, self.y - other.y)
    
    def __mul__(self, scalar):  # v * 2
        return Vector(self.x * scalar, self.y * scalar)
    
    def __truediv__(self, scalar):  # v / 2
        return Vector(self.x / scalar, self.y / scalar)

v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2  # Вызывает __add__
v4 = v1 * 2   # Вызывает __mul__

3. Сравнение

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __eq__(self, other):  # ==
        return self.age == other.age
    
    def __lt__(self, other):  # <
        return self.age < other.age
    
    def __le__(self, other):  # <=
        return self.age <= other.age
    
    def __gt__(self, other):  # >
        return self.age > other.age
    
    def __ge__(self, other):  # >=
        return self.age >= other.age
    
    def __ne__(self, other):  # !=
        return self.age != other.age

p1 = Person("Alice", 30)
p2 = Person("Bob", 25)
result = p1 > p2  # Вызывает __gt__

4. Строковое представление

class Car:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model
    
    def __str__(self):  # str(), print()
        return f"{self.brand} {self.model}"
    
    def __repr__(self):  # repr(), интерактивный режим
        return f"Car('{self.brand}', '{self.model}')"

car = Car("Toyota", "Camry")
print(car)     # Вызывает __str__
print(repr(car))  # Вызывает __repr__

5. Доступ к элементам (индексация и слайсинг)

class CustomList:
    def __init__(self, items):
        self.items = items
    
    def __getitem__(self, index):  # obj[key]
        return self.items[index]
    
    def __setitem__(self, index, value):  # obj[key] = value
        self.items[index] = value
    
    def __delitem__(self, index):  # del obj[key]
        del self.items[index]
    
    def __len__(self):  # len()
        return len(self.items)

cl = CustomList([1, 2, 3])
value = cl[0]  # Вызывает __getitem__
cl[0] = 10     # Вызывает __setitem__
size = len(cl) # Вызывает __len__

6. Вызываемость объекта

class Multiplier:
    def __init__(self, factor):
        self.factor = factor
    
    def __call__(self, x):  # obj(args)
        return x * self.factor

mult = Multiplier(3)
result = mult(5)  # Вызывает __call__, возвращает 15

7. Контекстный менеджер

class FileManager:
    def __init__(self, filename):
        self.filename = filename
    
    def __enter__(self):  # with statement
        self.file = open(self.filename)
        return self.file
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()

with FileManager('data.txt') as f:
    # __enter__ вызывается здесь
    data = f.read()
# __exit__ вызывается здесь

8. Атрибуты

class DynamicAttributes:
    def __getattr__(self, name):  # obj.attr (если нет атрибута)
        return f"Атрибут {name} не найден"
    
    def __setattr__(self, name, value):  # obj.attr = value
        super().__setattr__(name, value)
    
    def __delattr__(self, name):  # del obj.attr
        super().__delattr__(name)
    
    def __getattribute__(self, name):  # Вызывается для ЛЮБОГО атрибута
        return super().__getattribute__(name)

obj = DynamicAttributes()
obj.x = 10  # Вызывает __setattr__
value = obj.x  # Вызывает __getattribute__

9. Итераторы и генераторы

class CountUp:
    def __init__(self, max):
        self.max = max
        self.current = 0
    
    def __iter__(self):  # iter()
        return self
    
    def __next__(self):  # next()
        if self.current < self.max:
            self.current += 1
            return self.current
        raise StopIteration

for num in CountUp(3):  # Вызывает __iter__ и __next__
    print(num)

10. Отправка (dispatch)

class Matrix:
    def __hash__(self):  # hash()
        return id(self)
    
    def __bool__(self):  # bool(), if obj
        return True
    
    def __sizeof__(self):  # sys.getsizeof()
        return 128

m = Matrix()
if m:  # Вызывает __bool__
    print("Объект истинен")

Ключевые моменты

  • Автоматический вызов: интерпретатор вызывает методы за кулисами, вы пишете операции как обычно
  • Не вызывайте напрямую: избегайте obj.__add__(other), используйте obj + other
  • Обратная совместимость: __radd__, __rsub__ для операций справа 2 * obj
  • Порядок вычислений: Python сначала ищет метод левого операнда, потом правого
  • Исключения: некоторые методы можно вызвать напрямую (__init__, __new__), но обычно это не требуется

Магические методы — это то, что делает Python таким выразительным и гибким языком!