Во-первых, позвольте мне повторить основные моменты из поста Алекса:
Реализация по умолчанию бесполезна (трудно придумать ту, которой не было бы, но да)
__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
Если вы специально не принимаете мер для обеспечения обратного, большинство классов не дают полезных результатов ни для того, ни для другого:
>>> classSic(object): pass ... >>> print(str(Sic())) <__main__.Sic object at 0x8b7d0> >>> print(repr(Sic())) <__main__.Sic object at 0x8b7d0>
Как вы видите - никакой разницы и никакой информации, кроме класса и объекта id. Если вы переопределите только один из двух:
Как вы видите, если вы переопределяете __repr__, это ТАКЖЕ используется для __str__, но не наоборот.
Другие важные сведения, которые следует знать: __str__ во встроенном контейнере для элементов, которые он содержит, используется __repr__, а НЕ __str__. И, несмотря на слова по этому поводу, встречающиеся в типичных документах, вряд ли кто-то беспокоится о том, чтобы сделать __repr__ из объектов строку, которую eval можно использовать для построения равного объекта (это просто слишком сложно, и незнание того, как на самом деле был импортирован соответствующий модуль, делает это фактически невозможным).
Итак, мой совет: сосредоточьтесь на том, чтобы сделать __str__ достаточно понятными для человека и __repr__ настолько однозначными, насколько это возможно, даже если это мешает нечеткой недостижимой цели сделать __repr__возвращаемое значение приемлемым в качестве входных данных для eval!
Ответ 4
Короче говоря, цель __repr__ - быть однозначным и __str__ быть читаемым.
Возвращает строку, содержащую печатное представление объекта. Это то же значение, которое получается преобразованиями (обратные кавычки). Иногда полезно иметь возможность обращаться к этой операции как к обычной функции. Для многих типов эта функция пытается вернуть строку, которая при передаче в eval() выдает объект с тем же значением, в противном случае представление представляет собой строку, заключенную в угловые скобки, которая содержит имя типа объекта вместе с дополнительной информацией, часто включающей имя и адрес объекта. Класс может управлять тем, что эта функция возвращает для своих экземпляров, определяя __repr__() метод.
Вот документация для str:
str(object='')
Возвращает строку, содержащую удобное для печати представление объекта. Для строк это возвращает саму строку. Разница с repr(object) заключается в том, что str(object) не всегда пытается вернуть строку, приемлемую для eval(); его цель - вернуть строку для печати. Если аргумент не указан, возвращается пустая строка, ''.