PythonJuniorTechnical
Объясните разницу между __str__ и __repr__.
__str__ возвращает читаемое строковое представление объекта для конечного пользователя, __repr__ — однозначное техническое представление для разработчика, которое в идеале позволяет воссоздать объект через eval().
__str__ и __repr__: назначение и различия
В Python каждый объект может определить два строковых представления:
- __repr__ — «официальное» представление для разработчиков. Должно быть однозначным и, по возможности, позволять воссоздать объект через
eval(). Вызывается функциейrepr(), а также в интерактивной оболочке при выводе объекта. - __str__ — «неформальное» читаемое представление для пользователя. Вызывается функцией
str(),print()и форматированием строк (f-строки,format()).
Если __str__ не определён, Python откатывается к __repr__. Обратное неверно: отсутствие __repr__ даёт дефолтный вывод вроде <MyClass object at 0x...>.
Пример
from datetime import datetime
class Event:
def __init__(self, title: str, timestamp: datetime) -> None:
self.title = title
self.timestamp = timestamp
def __repr__(self) -> str:
# Цель: eval(repr(obj)) == obj (при доступном конструкторе)
return f"Event(title={self.title!r}, timestamp={self.timestamp!r})"
def __str__(self) -> str:
# Цель: удобочитаемость
return f"{self.title} в {self.timestamp:%Y-%m-%d %H:%M}"
e = Event("Релиз", datetime(2024, 6, 1, 12, 0))
print(repr(e)) # Event(title='Релиз', timestamp=datetime.datetime(2024, 6, 1, 12, 0))
print(str(e)) # Релиз в 2024-06-01 12:00
print(e) # Релиз в 2024-06-01 12:00 (использует __str__)
# В интерактивной оболочке или логах repr() полезнее:
events = [e]
print(events) # [Event(title='Релиз', timestamp=...)]
Когда что использовать
- Всегда реализуйте
__repr__— это базовый минимум для любого класса. - Добавляйте
__str__, когда объект показывается пользователю (UI, письма, отчёты). - В dataclasses
__repr__генерируется автоматически; можно отключить параметромrepr=False. - Для логирования используйте
repr()— он показывает тип и значения, что помогает при отладке.
Связь с форматированием строк
e = Event("Деплой", datetime(2024, 7, 15, 9, 30))
# !r вызывает repr(), !s — str()
print(f"{e!r}") # Event(title='Деплой', ...)
print(f"{e!s}") # Деплой в 2024-07-15 09:30
print(f"{e}") # Деплой в 2024-07-15 09:30 (по умолчанию __str__)
Подводные камни
- Если
__repr__не определён, контейнеры (list, dict) покажут адрес памяти вместо содержимого — трудно дебажить. - Не путайте
!rи!sв f-строках:f"{name!r}"добавляет кавычки вокруг строки, что может сломать вывод. - Рекурсивные структуры в
__repr__вызовутRecursionError— добавляйте защиту через флаг илиreprlib.repr(). - В dataclass поле с
repr=Falseисчезает из авто-__repr__, но не из__str__(если вы его реализовали вручную). - Метод
__str__в родительском классе перекрывается в дочернем, только если дочерний явно его переопределяет — неожиданное наследование. - Длинный
__repr__при логировании больших объектов может засорить логи; используйтеreprlib.repr()для усечения.
Common mistakes
- Описывать str vs repr только как термин и не показывать механизм на минимальном примере.
- Игнорировать ошибки, пустые данные, конкурентный доступ или границы транзакции.
- Не связывать поведение с официальным контрактом Python и реальной эксплуатацией.
What the interviewer is testing
- Объясняет str vs repr через последовательность действий, а не через набор ключевых слов.
- Приводит короткий кодовый пример или production-сценарий с ожидаемым поведением.
- Называет хотя бы один риск: производительность, безопасность, транзакции, память или сопровождение.