Вопрос-Ответ

What is the difference between __str__ and __repr__?

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

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

Переведено автоматически
Ответ 1

Алекс Мартелли хорошо резюмировал, но, на удивление, был слишком краток.

Во-первых, позвольте мне повторить основные моменты из поста Алекса:


  • Реализация по умолчанию бесполезна (трудно придумать ту, которой не было бы, но да)

  • __repr__ цель - быть однозначным

  • __str__ цель - быть читабельным

  • Контейнер __str__ использует содержащиеся объекты’ __repr__

Реализация по умолчанию бесполезна

В основном это сюрприз, потому что значения по умолчанию в Python, как правило, довольно полезны. Однако в этом случае значение по умолчанию для __repr__, которое будет действовать как:

return "%s(%r)" % (self.__class__, self.__dict__)

было бы слишком опасно (например, слишком легко перейти к бесконечной рекурсии, если объекты ссылаются друг на друга). Так что Python справляется. Обратите внимание, что есть одно значение по умолчанию, которое является true: если __repr__ определено, а __str__ нет, объект будет вести себя так, как если бы __str__=__repr__.

Проще говоря, это означает: почти каждый реализуемый вами объект должен иметь функционал, __repr__ который можно использовать для понимания объекта. Реализация __str__ необязательна: сделайте это, если вам нужна функциональность “красивой печати” (например, используемая генератором отчетов).

Цель __repr__ - быть однозначным

Позвольте мне прямо сказать — я не верю в отладчики. Я действительно не знаю, как использовать какой-либо отладчик, и никогда не использовал его всерьез. Более того, я считаю, что большой ошибкой отладчиков является их основная природа — большинство сбоев, которые я отлаживаю, произошли давным-давно, в далекой-далекой галактике. Это означает, что я с религиозным рвением верю в ведение журнала. Ведение журнала - это жизненная сила любой приличной серверной системы типа "запустить и забыть". Python упрощает ведение журнала: возможно, с некоторыми специфичными для проекта оболочками, все, что вам нужно, это

log(INFO, "I am in the weird function and a is", a, "and b is", b, "but I got a null C — using default", default_c)

Но вы должны сделать последний шаг — убедиться, что каждый реализуемый вами объект имеет полезный repr, чтобы подобный код мог просто работать. Вот почему возникает проблема “eval”: если у вас достаточно информации so eval(repr(c))==c, это означает, что вы знаете все, о чем нужно знать c. Если это достаточно просто, хотя бы в размытом виде, сделайте это. Если нет, убедитесь, что у вас все равно достаточно информации о c. Обычно я использую формат, подобный eval: "MyClass(this=%r,that=%r)" % (self.this,self.that). Это не означает, что вы действительно можете создать MyClass или что это правильные аргументы конструктора - но это полезная форма для выражения “это все, что вам нужно знать об этом экземпляре”.

Примечание: я использовал %r выше, а не %s. Вы всегда хотите использовать repr() [или %r символ форматирования, что эквивалентно] внутри __repr__ реализации, или вы нарушаете цель repr. Вы хотите иметь возможность различать MyClass(3) и MyClass("3").

Цель __str__ - быть читаемым

В частности, это не должно быть однозначным — обратите на это внимание str(3)==str("3"). Аналогично, если вы реализуете абстракцию IP, то ее str будет выглядеть как 192.168.1.1. это просто прекрасно. При реализации абстракции даты / времени str может быть "2010/4/12 15:35:22" и т.д. Цель состоит в том, чтобы представить его таким образом, чтобы пользователь, а не программист, захотел бы его прочитать. Отрежьте бесполезные цифры, притворитесь каким—нибудь другим классом - пока он поддерживает читаемость, это улучшение.

Контейнер __str__ использует содержащиеся объекты’ __repr__

Это кажется удивительным, не так ли? Это немного, но насколько читабельным это было бы, если бы он использовал их __str__?

[moshe is, 3, hello
world, this is a list, oh I don't know, containing just 4 elements]

Не очень. В частности, для строк в контейнере было бы слишком легко нарушить его строковое представление. Помните, что перед лицом двусмысленности Python сопротивляется искушению угадать. Если вам нужно описанное выше поведение при печати списка, просто

print("[" + ", ".join(lst) + "]")

(вероятно, вы также можете выяснить, что делать со словарями).

Краткие сведения

Реализуйте __repr__ для любого класса, который вы реализуете. Это должно быть вашей второй натурой. Реализуйте, __str__ если вы считаете, что было бы полезно иметь строковую версию, которая допускает ошибки в части удобочитаемости.

Ответ 2

Мое эмпирическое правило: __repr__ для разработчиков, __str__ для клиентов.

Ответ 3

Если вы специально не принимаете мер для обеспечения обратного, большинство классов не дают полезных результатов ни для того, ни для другого:

>>> class Sic(object): pass
...
>>> print(str(Sic()))
<__main__.Sic object at 0x8b7d0>
>>> print(repr(Sic()))
<__main__.Sic object at 0x8b7d0>

Как вы видите - никакой разницы и никакой информации, кроме класса и объекта id. Если вы переопределите только один из двух:

>>> class Sic(object): 
... def __repr__(self): return 'foo'
...
>>> print(str(Sic()))
foo
>>> print(repr(Sic()))
foo
>>> class Sic(object):
... def __str__(self): return 'foo'
...
>>> print(str(Sic()))
foo
>>> print(repr(Sic()))
<__main__.Sic object at 0x2617f0>

Как вы видите, если вы переопределяете __repr__, это ТАКЖЕ используется для __str__, но не наоборот.

Другие важные сведения, которые следует знать: __str__ во встроенном контейнере для элементов, которые он содержит, используется __repr__, а НЕ __str__. И, несмотря на слова по этому поводу, встречающиеся в типичных документах, вряд ли кто-то беспокоится о том, чтобы сделать __repr__ из объектов строку, которую eval можно использовать для построения равного объекта (это просто слишком сложно, и незнание того, как на самом деле был импортирован соответствующий модуль, делает это фактически невозможным).

Итак, мой совет: сосредоточьтесь на том, чтобы сделать __str__ достаточно понятными для человека и __repr__ настолько однозначными, насколько это возможно, даже если это мешает нечеткой недостижимой цели сделать __repr__возвращаемое значение приемлемым в качестве входных данных для eval!

Ответ 4

Короче говоря, цель __repr__ - быть однозначным и __str__ быть читаемым.


Вот хороший пример:

>>> import datetime
>>> today = datetime.datetime.now()
>>> str(today)
'2012-03-14 09:21:58.130922'
>>> repr(today)
'datetime.datetime(2012, 3, 14, 9, 21, 58, 130922)'

Прочитайте эту документацию для repr:


repr(object)


Возвращает строку, содержащую печатное представление объекта. Это то же значение, которое получается преобразованиями (обратные кавычки). Иногда полезно иметь возможность обращаться к этой операции как к обычной функции. Для многих типов эта функция пытается вернуть строку, которая при передаче в eval() выдает объект с тем же значением, в противном случае представление представляет собой строку, заключенную в угловые скобки, которая содержит имя типа объекта вместе с дополнительной информацией, часто включающей имя и адрес объекта. Класс может управлять тем, что эта функция возвращает для своих экземпляров, определяя __repr__() метод.


Вот документация для str:


str(object='')


Возвращает строку, содержащую удобное для печати представление объекта. Для строк это возвращает саму строку. Разница с repr(object) заключается в том, что str(object) не всегда пытается вернуть строку, приемлемую для eval(); его цель - вернуть строку для печати. Если аргумент не указан, возвращается пустая строка, ''.


2024-01-29 18:08 python