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-сценарий с ожидаемым поведением.
  • Называет хотя бы один риск: производительность, безопасность, транзакции, память или сопровождение.

Sources

Related topics