В чем разница между методами __str__ и __repr__ в Python?

Ответ

__str__ и __repr__ — это методы для получения строкового представления объекта, но они предназначены для разной аудитории и целей.

Метод Целевая аудитория Основная цель Вызывается функциями Требования
__str__(self) Конечные пользователи, для удобочитаемого вывода. Предоставить красивое, неформальное, понятное строковое представление. print(), str(), f-строки (неявно). Читаемость и информативность для человека.
__repr__(self) Разработчики, для отладки и логгирования. Предоставить однозначное, формальное, максимально полное представление, желательно позволяющее воссоздать объект. repr(), вывод в интерактивной консоли, логгирование. Однозначность. Идеально, если eval(repr(obj)) == obj.

Простое правило: __repr__ — для разработчика (что это за объект?), __str__ — для пользователя (как это показать?).

Наглядный пример:

class Project:
    def __init__(self, name, language, version):
        self.name = name
        self.language = language
        self.version = version

    def __str__(self):
        # Удобочитаемо для пользователя
        return f"Project '{self.name}' (v{self.version})"

    def __repr__(self):
        # Точное и полное представление для разработчика
        # Обратите внимание на экранирование строки name!
        return f"Project(name='{self.name}', language='{self.language}', version={self.version})"

# Создаем объект
my_project = Project("Alpha", "Python", 1.5)

print("Using print() / str():")
print(my_project)           # Неявно вызывает __str__
print(str(my_project))      # Явный вызов __str__
# Вывод: Project 'Alpha' (v1.5)

print("nUsing repr() or in console:")
print(repr(my_project))     # Явный вызов __repr__
# В интерактивной консоли просто `my_project` выведет это же.
# Вывод: Project(name='Alpha', language='Python', version=1.5)

# Идеальное свойство repr: возможность воссоздания (не всегда достижимо)
try:
    # Для простых классов repr может быть исполняемым кодом
    copy_project = eval(repr(my_project))
    print(f"nCopy created: {copy_project.name}")
except:
    pass

Важные детали:

  1. Fallback: Если метод __str__ не определен, Python использует __repr__ в качестве запасного варианта (например, в print()). Поэтому всегда определяйте __repr__ — это хороший тон.
  2. Для коллекций: print(list) использует __repr__ для элементов списка, а не __str__.
  3. Стандартный __repr__ по умолчанию (<__main__.Project object at 0x7f8a1c2b4d90>) бесполезен для отладки. Всегда переопределяйте его.

Правило большого пальца: Сначала напишите информативный __repr__. Затем, если нужен особый формат для вывода пользователю, добавьте __str__.

Ответ 18+ 🔞

А, ну вот, опять эти два метода, которые всех в ступор вгоняют. Слушай, представь себе: ты написал какой-нибудь класс, там Project какой-нибудь, и он у тебя в коде как чёрный ящик лежит. А потом ты его в консоль выводишь, и тебе показывают какую-то хуйню вроде <__main__.Project object at 0x7f8a1c2b4d90>. Ну и чё это, блядь, такое? Это пиздец, а не информация. Вот для этого и нужны __str__ и __repr__ — чтобы этот ящик не таким страшным был.

Так, поехали по-простому. __str__ — это типа твоя парадная форма. Ты надеваешь её, когда идешь к людям, которые нихуя не шарят в коде. Чтобы им красиво и понятно показать: "Вот, смотрите, это проект 'Альфа', версия 1.5". Красиво, коротко, без лишних деталей. Его вызывает print() или str().

А __repr__ — это уже для своих, для таких же задротов, как ты. Это как техпаспорт на объект, или даже лучше — как инструкция по его сборке. Тут уже надо вывалить всё: и имя, и язык, и версию, да так, чтобы другой программист глянул и сразу понял, что за зверь перед ним. А в идеале — чтобы можно было скопировать этот текст, вставить в код, и получился бы такой же объект. Его вызывает repr() или просто пишешь имя объекта в консоли.

Вот смотри, как это в жизни выглядит, ёпта:

class Project:
    def __init__(self, name, language, version):
        self.name = name
        self.language = language
        self.version = version

    def __str__(self):
        # Это типа для красоты, для юзеров
        return f"Project '{self.name}' (v{self.version})"

    def __repr__(self):
        # А это уже по-взрослому, для отладки. Смотри, как всё подробно.
        return f"Project(name='{self.name}', language='{self.language}', version={self.version})"

# Ну и создаём объект, чего уж там
my_project = Project("Alpha", "Python", 1.5)

print("Смотри, что print() делает:")
print(my_project)  # Тут вызовется __str__
# Выведет: Project 'Alpha' (v1.5) — красиво и понятно.

print("nА теперь смотри, что repr() говорит:")
print(repr(my_project))  # А тут уже __repr__ в деле
# Выведет: Project(name='Alpha', language='Python', version=1.5) — всё по полочкам.

# И самое офигенное, что если repr написан хорошо, то можно даже так:
try:
    copy_project = eval(repr(my_project))  # Берем строку от repr и делаем из неё объект!
    print(f"nСмотри, клон создали: {copy_project.name}")
except:
    pass

Вот и вся магия. Но есть важные фишки, которые надо помнить, а то наебнёшься.

  1. Запасной парашют. Если ты такой хитрожопый и не написал __str__, то Python, когда его попросят напечатать твой объект, пойдёт и вызовет __repr__ вместо него. Поэтому всегда пиши __repr__, это как базовая гигиена. Иначе будешь получать тот самый дефолтный пиздец с адресом в памяти, от которого пользы — ноль ебать.
  2. Коллекции — они особенные. Ты думаешь, положил объекты в список, вывел список, и для каждого элемента вызовется __str__? Хуй там! print([obj1, obj2]) использует __repr__ для каждого элемента внутри. Так что если твой __repr__ кривой, то и в списке всё будет выглядеть как говно.
  3. Правило простое, как три копейки. Сначала всегда пиши нормальный, подробный __repr__. Потом, если тебе вдруг понадобится какая-то особая, красивая строка для логов или интерфейса — вот тогда уже добавляй __str__. А так — __repr__ твой главный друг на отладке.

Короче, запомни: __repr__ — чтобы понять, что за хуйня. __str__ — чтобы красиво показать, какая это хуйня.