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.