PythonJuniorTechnical

Чем отличаются __str__ и __repr__? Когда какой метод определять?

__str__ — человекочитаемое представление для пользователя, __repr__ — однозначное техническое представление для разработчика, попадающее в логи, REPL и списки. Если есть только __repr__, он работает и за __str__.

Назначение __str__ и __repr__

  • __str__ вызывает str(obj), print(obj), f"{obj}", format(obj) и %s — это человекочитаемый вид для пользователя.
  • __repr__ вызывает repr(obj), REPL-вывод, f"{obj!r}", %r, отображение элементов в list/dict, логирование через %r и logger.debug("%r", obj).
  • Если определён только __repr__, он используется как fallback для str. Обратное неверно.

Контракты

  • __repr__ должен быть однозначным и, по возможности, валидным Python-выражением: User(id=1, email='a@b.c'). Это сильно облегчает отладку.
  • __str__ может быть кратким и форматированным под UI.
  • Оба метода должны возвращать str и не падать. Падение в __repr__ ломает логирование и отладчики.

Пример

class User:
    def __init__(self, user_id: int, email: str, token: str) -> None:
        self.user_id = user_id
        self.email = email
        self._token = token

    def __str__(self) -> str:
        return self.email

    def __repr__(self) -> str:
        return f"User(user_id={self.user_id!r}, email={self.email!r})"

u = User(1, "ada@example.com", "secret-token")
print(str(u))           # ada@example.com
print(repr(u))          # User(user_id=1, email='ada@example.com')
print([u])              # [User(user_id=1, email='ada@example.com')]
print(f"{u} / {u!r}")   # ada@example.com / User(...)

В dataclasses

@dataclass автоматически генерирует __repr__ по полям. Используйте field(repr=False), чтобы спрятать секреты:

from dataclasses import dataclass, field

@dataclass
class Credentials:
    user: str
    password: str = field(repr=False)

print(Credentials("ada", "hunter2"))   # Credentials(user='ada')

Подводные камни

  • В __repr__ печатать пароли, токены, JWT, ключи — попадают в логи и трейсы.
  • Определить только __str__: в списках/словарях видите <User object at 0x...> и отлаживать невозможно.
  • Возвращать не-str (например, bytes) — TypeError: __repr__ returned non-string.
  • Падать в __repr__ при недогруженном поле (например, lazy ORM-атрибут) — ломает logger.exception.
  • Обещать eval(repr(obj)) == obj для сложных объектов и завязывать на это сериализацию — это про debug, не про persistence.
  • Использовать repr вместо нормальной сериализации (json.dumps/pickle) в межпроцессном обмене.
  • Забыть про !r в логах: f"value={x}" теряет кавычки вокруг строк и скрывает невидимые символы вроде \n.

Common mistakes

  • Говорить, что методы отличаются только форматированием.
  • Не упоминать fallback repr для str.
  • Игнорировать безопасность логов.

What the interviewer is testing

  • Понимает аудиторию каждого метода.
  • Может написать полезный repr.
  • Знает !r в f-string.

Sources

Related topics