Специальные методы: __repr__, __str__, __eq__, __len__
Содержание курса
Правила диспетчеризации: когда Python вызывает repr и str
Разобравшись с тем, для кого предназначен каждый метод, разберём точные правила: в каком контексте Python выбирает тот или иной.
Таблица вызовов — print(obj) и str(obj) → вызывают __str__; если его нет — используется __repr__
repr(obj)→ вызывает только__repr__;__str__здесь не используетсяf'{obj}'→ вызывает__format__(obj, '')с пустой спецификацией формата; стандартная реализация__format__делегирует__str__, поэтому для большинства классов результат совпадает сstr(obj). Но если класс переопределяет__format__, поведение f-строки может отличаться отstr()— это не синонимы- Интерактивная сессия (вводишь
objи нажимаешь Enter) → вызывает__repr__ - Объект внутри контейнера (
[obj],{obj},(obj,)) → каждый элемент выводится через__repr__
Последний пункт часто удивляет. Если у тебя список объектов и ты делаешь print(my_list), Python выведет не __str__ каждого элемента, а именно __repr__. Это сделано намеренно: внутри контейнера важна диагностическая информация.
Отдельно про f-строки и format
Вот конкретный пример, где f'{obj}' и str(obj) дают разный результат:
class Temperature:
def __init__(self, celsius):
self.celsius = celsius
def __str__(self):
return f"{self.celsius}°C"
def __format__(self, spec):
if spec == 'f':
return f"{self.celsius:.1f}°C"
return f"[{self.celsius}°C]" # переопределяем даже пустую спецификацию
t = Temperature(20)
str(t) # '20°C' — вызывает __str__
f'{t}' # '[20°C]' — вызывает __format__ с пустой строкой
f'{t:f}' # '20.0°C' — вызывает __format__ с 'f'
Для учебного Playlist мы __format__ не переопределяем, поэтому f'{p}' и str(p) дадут одинаковый результат. Но на продвинутом уровне важно понимать, что это разные точки входа.
Что происходит, если одного метода нет
Если определён только __repr__, стандартные print() и str() используют его как запасной вариант; интерактивная сессия и контейнеры тоже показывают __repr__.
Если определён только __str__, print() и str() используют его, но repr(obj) и интерактивная сессия всё равно выдают дефолтный <...>-вывод.
class OnlyStr:
def __str__(self):
return "я понятен пользователю"
obj = OnlyStr()
print(obj) # я понятен пользователю
repr(obj) # '<__main__.OnlyStr object at 0x...>'
class OnlyRepr:
def __repr__(self):
return "OnlyRepr()"
obj = OnlyRepr()
print(obj) # OnlyRepr()
repr(obj) # 'OnlyRepr()'
Если реализуешь только один из двух методов, обычно полезнее начать с информативного __repr__.
