Дандер-методы: __str__, __repr__, __len__, __add__ и другие
Магия Python: что скрывается за двойными подчёркиваниями? 🔮
В Python методы с двойными подчёркиваниями (__метод__) называют "магическими" или "дандер-методами" (от англ. dunder — double underscore). Они позволяют переопределять поведение объектов в различных ситуациях!
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other): # Переопределяем сложение!
return Vector(self.x + other.x, self.y + other.y)
__str__ vs __repr__: как Python представляет объекты
Эти методы отвечают за строковое представление объекта, но с разными целями:
__str__ — для человека 👨💻
Используется функциями print() и str(). Должен быть понятным и читаемым.
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def __str__(self):
return f"'{self.title}' by {self.author}"
book = Book("Python Tricks", "Dan Bader")
print(book) # Выведет: 'Python Tricks' by Dan Bader
__repr__ — для программиста 💻
Используется при выводе в консоли и функцией repr(). Должен быть однозначным и, желательно, позволяющим воссоздать объект.
def __repr__(self):
return f"Book(title='{self.title}', author='{self.author}')"
# В консоли:
>>> book
Book(title='Python Tricks', author='Dan Bader')
Золотое правило: если __str__ не определён, Python использует __repr__ как резервный вариант!
Измеряем и складываем: __len__ и __add__
__len__ — считаем элементы 📏
Вызывается функцией len():
class Playlist:
def __init__(self, songs):
self.songs = songs
def __len__(self):
return len(self.songs)
playlist = Playlist(["Song1", "Song2"])
print(len(playlist)) # 2
__add__ — создаём новые объекты ➕
Позволяет складывать объекты через оператор +:
class BankAccount:
def __init__(self, balance):
self.balance = balance
def __add__(self, other):
return BankAccount(self.balance + other.balance)
acc1 = BankAccount(100)
acc2 = BankAccount(200)
result = acc1 + acc2 # Новый счёт с балансом 300!
Продвинутые дандер-методы для особых случаев
__getitem__ — доступ по индексу 📌
Делает объект итерируемым:
class Library:
def __init__(self, books):
self.books = books
def __getitem__(self, index):
return self.books[index]
lib = Library(["Book1", "Book2"])
print(lib[1]) # 'Book2'
__call__ — объект как функция 📞
Позволяет вызывать экземпляр как функцию:
class Counter:
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
return self.count
counter = Counter()
print(counter()) # 1
print(counter()) # 2
Когда использовать дандер-методы?
- Когда стандартное поведение Python нужно адаптировать под ваш класс
- Для улучшения читаемости кода (например,
obj1 + obj2вместоobj1.add(obj2)) - Для интеграции с Python-экосистемой (работа с
len(),print()и т.д.)
Важно: не переусердствуйте! Иногда явный метод (например, .get_length()) лучше магического.
Практическая магия: пример класса "Таймер" ⏱️
class Timer:
def __init__(self, seconds):
self.seconds = seconds
def __add__(self, other):
return Timer(self.seconds + other.seconds)
def __str__(self):
mins, secs = divmod(self.seconds, 60)
return f"{mins:02d}:{secs:02d}"
def __repr__(self):
return f"Timer({self.seconds})"
t1 = Timer(65)
t2 = Timer(120)
print(t1 + t2) # 03:05
Этот пример объединяет несколько дандер-методов для создания интуитивно понятного интерфейса!
Куда двигаться дальше?
- Изучите другие дандер-методы:
__eq__,__lt__для сравнений - Посмотрите, как работают
__enter__/__exit__для контекстных менеджеров - Экспериментируйте! Создайте свой класс, который ведёт себя как встроенный тип Python
Для погружения в тему рекомендую видео Данилы Бежина про ООП в Python: https://www.youtube.com/@DanilaBezhin