В чем разница между __str__ и __repr__?
Переведено автоматически
Ответ 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()
; его цель - вернуть строку для печати. Если аргумент не указан, возвращается пустая строка,''
.