В чем разница между __str__
и __repr__
в Python?
В чем разница между __str__ и __repr__?
Ответы (27)
Алекс хорошо резюмировал, но, на удивление, оказался слишком лаконичным.
Во-первых, позвольте мне повторить основные моменты в сообщении Алекса:
- Реализация по умолчанию бесполезна (трудно придумать вариант, который бы не был, но да)
-
__repr__
цель - быть недвусмысленной -
__str__
цель должна быть читаемой - Контейнер
__str__
использует содержащиеся объекты '__repr__
Реализация по умолчанию бесполезна
Это в основном сюрприз, потому что настройки Python по умолчанию довольно полезны. Однако в этом случае, имея значение по умолчанию для __repr__
, которое будет действовать следующим образом:
return "%s(%r)" % (self.__class__, self.__dict__)
было бы слишком опасно (например, слишком легко попасть в бесконечную рекурсию, если объекты ссылаются друг на друга). Итак, Python уходит. Обратите внимание, что есть одно значение по умолчанию, которое истинно: если __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)
Но вам нужно сделать последний шаг - убедиться, что каждый реализуемый вами объект имеет полезную реплику, чтобы такой код мог просто работать. Вот почему возникает «eval»: если у вас достаточно информации, например, 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(l) + "]"
(вы, вероятно, также сможете выяснить, что делать со словарями.
Резюме
Реализуйте __repr__
для любого класса, который вы реализуете. Это должно быть второй натурой. Реализуйте __str__
, если считаете, что было бы полезно иметь строковую версию, которая ошибается с точки зрения удобочитаемости.
В основном __str__
или str() используется для создания вывода, который удобочитаем и должен быть предназначен для конечных пользователей.
С другой стороны, repr() или __repr__
в основном возвращает каноническое строковое представление объектов, которое служит для отладки и разработки, помогает программистам.
Программисты с предыдущим опытом работы с языками с методом
toString
склонны реализовывать__str__
, а не__repr__
. Если вы реализуете только один из этих специальных методов в Python, выберите__repr__
.
Из книги Fluent Python Рамальо Лучано.
Вы можете получить некоторое представление об этом коде:
class Foo():
def __repr__(self):
return("repr")
def __str__(self):
return("str")
foo = Foo()
foo #repr
print(foo) #str
Каждый объект наследует __repr__
от базового класса, созданного всеми объектами.
class Person:
pass
p=Person()
, если вы позвоните по номеру repr(p)
, вы получите по умолчанию:
<__main__.Person object at 0x7fb2604f03a0>
Но если вы позвоните по телефону str(p)
, вы получите тот же результат. это потому, что когда __str__
не существует, Python вызывает __repr__
Давайте реализуем наш собственный __str__
class Person:
def __init__(self,name,age):
self.name=name
self.age=age
def __repr__(self):
print("__repr__ called")
return f"Person(name='{self.name}',age={self.age})"
p=Person("ali",20)
print(p)
и str(p)
вернут
__repr__ called
Person(name='ali',age=20)
добавим __str__()
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
print('__repr__ called')
return f"Person(name='{self.name}, age=self.age')"
def __str__(self):
print('__str__ called')
return self.name
p=Person("ali",20)
, если мы вызовем print(p)
и str (p), он вызовет __str__()
, поэтому он вернет
__str__ called
ali
repr(p)
вернет
представитель позвонил "Человек (имя = 'ali, age = self.age')"
Давайте опустим __repr__
и просто реализуем __str__
.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
print('__str__ called')
return self.name
p=Person('ali',20)
print(p)
будет искать __str__
и вернет:
__str__ called
ali
ПРИМЕЧАНИЕ = если бы у нас были определены __repr__
и __str__
, f'name is {p}'
вызвал бы __str__
Вопрос: В чем разница между __str__()
и __repr__()
?
ДЛИННЫЙ
Этот вопрос существует давно, и существует множество ответов, большинство из которых верны (не говоря уже о нескольких легендах сообщества Python [!]). Однако, когда дело доходит до мелочей, этот вопрос аналогичен вопросу о разнице между встроенными функциями str()
и repr()
. Я собираюсь описать различия своими словами (что означает, что я, возможно, вольно «заимствую» из Core Python Programming , так что, пожалуйста, простите меня).
И str()
, и repr()
выполняют одну и ту же базовую задачу: их цель - вернуть строковое представление объекта Python. тип строкового представления - вот что их отличает.
-
str()
&__str__()
возвращает печатаемое строковое представление объект ... что-то удобочитаемое / для потребления человеком -
repr()
&__repr__()
возвращает строковое представление объекта, который является действительный объект Python , что-то, что вы можете передать вeval()
или ввести в оболочку Python без получения ошибки.
Например, давайте назначим строку для x
и int
для y
и просто покажем удобочитаемые строковые версии каждого:
>>> x, y = 'foo', 123
>>> str(x), str(y)
('foo', '123')
Можем ли мы взять то, что находится внутри кавычек в обоих случаях, и ввести их дословно в интерпретатор Python? Давайте попробуем:
>>> 123
123
>>> foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined
Очевидно, вы можете для int
, но не обязательно для str
. Хотя я могу передать '123'
в eval()
, это не работает для 'foo'
:
>>> eval('123')
123
>>> eval('foo')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'foo' is not defined
Теперь попробуем repr()
; снова выгрузите то, что в паре кавычек для каждой строки:
>>> repr(x), repr(y)
("'foo'", '123')
>>> 123
123
>>> 'foo'
'foo'
Вау, они оба работают? Это потому, что 'foo'
, хотя и является печатаемым строковым представлением этой строки, оно не оцениваемо, а "'foo'"
можно. 123
- допустимый Python int
, вызываемый либо str()
, либо repr()
. Что произойдет, если мы позвоним по номеру eval()
с этим?
>>> eval('123')
123
>>> eval("'foo'")
'foo'
Это работает, потому что 123
и 'foo'
являются допустимыми объектами Python. Еще один важный вывод: иногда оба возвращают одно и то же (одно и то же строковое представление), но это не всегда так. (И да, да, я могу создать переменную foo
, где работает eval()
, но дело не в этом.)
Дополнительные фактоиды по обеим парам
- Иногда
str()
иrepr()
называются неявно , то есть они вызываются от имени пользователей: когда пользователи выполняютprint
или Py вызовprint()
(Py3 +), даже если пользователи не вызываютstr()
явно, такой вызов выполняется от их имени до отображения объекта. - В оболочке Python (интерактивный интерпретатор), если вы вводите переменную в приглашении
>>>
и нажимаете RETURN, интерпретатор отображает результатыrepr()
, неявно вызванного для этого объекта. - Чтобы соединить
str()
иrepr()
с__str__()
и__repr__()
, осознайте, что вызовы встроенных функций, то естьstr(x)
илиrepr(y)
, приводят к вызову соответствующих специальных методов их объекта: <116134765 > илиy.__repr()__
- Реализуя
__str__()
и__repr__()
для ваших классов Python, вы перегружаете встроенные функции (str()
иrepr()
), позволяя передавать экземпляры ваших классов вstr()
иrepr()
. Когда такие вызовы сделаны, они поворачиваются и вызывают классы '__str__()
и__repr__()
(по # 3).
__str__
может быть вызван для объекта путем вызова str(obj)
и должен возвращать удобочитаемую строку.
__repr__
может быть вызван для объекта путем вызова repr(obj)
и должен возвращать внутренний объект (поля / атрибуты объекта)
Этот пример может помочь:
class C1:pass
class C2:
def __str__(self):
return str(f"{self.__class__.__name__} class str ")
class C3:
def __repr__(self):
return str(f"{self.__class__.__name__} class repr")
class C4:
def __str__(self):
return str(f"{self.__class__.__name__} class str ")
def __repr__(self):
return str(f"{self.__class__.__name__} class repr")
ci1 = C1()
ci2 = C2()
ci3 = C3()
ci4 = C4()
print(ci1) #<__main__.C1 object at 0x0000024C44A80C18>
print(str(ci1)) #<__main__.C1 object at 0x0000024C44A80C18>
print(repr(ci1)) #<__main__.C1 object at 0x0000024C44A80C18>
print(ci2) #C2 class str
print(str(ci2)) #C2 class str
print(repr(ci2)) #<__main__.C2 object at 0x0000024C44AE12E8>
print(ci3) #C3 class repr
print(str(ci3)) #C3 class repr
print(repr(ci3)) #C3 class repr
print(ci4) #C4 class str
print(str(ci4)) #C4 class str
print(repr(ci4)) #C4 class repr
-
__str__
должен возвращать строковый объект, тогда как__repr__
может возвращать любое выражение Python. - Если реализация
__str__
отсутствует, то функция__repr__
используется в качестве резервной. Если реализация функции__repr__
отсутствует, отката нет. - Если функция
__repr__
возвращает строковое представление объекта, мы можем пропустить реализацию функции__str__
.
Источник: https://www.journaldev.com/22460/python-str-repr-functions
__repr__
используется везде, кроме методов print
и str
(когда определен __str__
!)
В двух словах:
class Demo:
def __repr__(self):
return 'repr'
def __str__(self):
return 'str'
demo = Demo()
print(demo) # use __str__, output 'str' to stdout
s = str(demo) # __str__ is used, return 'str'
r = repr(demo) # __repr__ is used, return 'repr'
import logging
logger = logging.getLogger(logging.INFO)
logger.info(demo) # use __str__, output 'str' to stdout
from pprint import pprint, pformat
pprint(demo) # use __repr__, output 'repr' to stdout
result = pformat(demo) # use __repr__, result is string which value is 'str'
Поймите __str__
и __repr__
интуитивно и навсегда различайте их.
__str__
вернуть строку замаскированного тела данного объекта для чтения глазами
__repr__
вернуть реальное тело данного объекта (вернуть себя) для однозначности для идентификации.
См. Пример
In [30]: str(datetime.datetime.now())
Out[30]: '2017-12-07 15:41:14.002752'
Disguised in string form
Что касается __repr__
In [32]: datetime.datetime.now()
Out[32]: datetime.datetime(2017, 12, 7, 15, 43, 27, 297769)
Presence in real body which allows to be manipulated directly.
Мы можем удобно выполнять арифметические операции с результатами __repr__
.
In [33]: datetime.datetime.now()
Out[33]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521)
In [34]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521) - datetime.datetime(2
...: 017, 12, 7, 15, 43, 27, 297769)
Out[34]: datetime.timedelta(0, 222, 443752)
если применить операцию к __str__
In [35]: '2017-12-07 15:43:14.002752' - '2017-12-07 15:41:14.002752'
TypeError: unsupported operand type(s) for -: 'str' and 'str'
Не возвращает ничего, кроме ошибки.
Другой пример.
In [36]: str('string_body')
Out[36]: 'string_body' # in string form
In [37]: repr('real_body')
Out[37]: "'real_body'" #its real body hide inside
Надеюсь, это поможет вам построить бетонную основу для поиска дополнительных ответов.
Один аспект, которого не хватает в других ответах. Верно, что в целом шаблон таков:
- Цель
__str__
: удобочитаемый - Цель
__repr__
: однозначно, возможно, машиночитаемое черезeval
К сожалению, это различие некорректно, поскольку Python REPL и IPython используют __repr__
для печати объектов в консоли REPL (см. Связанные вопросы для Python и IPython). Таким образом, проекты, предназначенные для работы с интерактивной консолью (например, Numpy или Pandas), начали игнорировать вышеуказанные правила и вместо этого предоставлять удобочитаемую реализацию __repr__
.
На странице 358 книги Сценарии Python для вычислительной науки Ханса Петтера Лангтангена четко говорится, что
-
__repr__
направлен на полное строковое представление объекта; -
__str__
возвращает удобную строку для печати.
Итак, я предпочитаю понимать их как
- repr = воспроизводить
- str = строка (представление)
с точки зрения пользователя хотя это недоразумение я сделал при изучении python.
Небольшой, но хороший пример также приведен на той же странице:
Пример
In [38]: str('s')
Out[38]: 's'
In [39]: repr('s')
Out[39]: "'s'"
In [40]: eval(str('s'))
Traceback (most recent call last):
File "<ipython-input-40-abd46c0c43e7>", line 1, in <module>
eval(str('s'))
File "<string>", line 1, in <module>
NameError: name 's' is not defined
In [41]: eval(repr('s'))
Out[41]: 's'
Это на стр. №351.
Называть repr
воспроизведением - это своего рода заблуждение. Лучше думать об этом как о себе.
Из книги Свободный Python :
Основным требованием к объекту Python является предоставление пригодного для использования строковое представление самого себя, которое используется для отладки и ведение журнала, еще один для представления конечным пользователям. Вот почему
в модели данных существуют специальные методы__repr__
и__str__
.
str
- создает новый строковый объект из данного объекта.
repr
- возвращает каноническое строковое представление объекта.
Отличия:
str():
- делает объект доступным для чтения
- генерирует вывод для конечного пользователя
repr():
- требуется код, воспроизводящий объект
- генерирует вывод для разработчика
Помимо всех приведенных ответов, я хотел бы добавить несколько моментов: -
1) __repr__()
вызывается, когда вы просто вводите имя объекта в интерактивной консоли Python и нажимаете клавишу ввода.
2) __str__()
вызывается, когда вы используете объект с оператором печати.
3) В случае, если __str__
отсутствует, то print и любая функция, использующая str()
, вызывает __repr__()
объекта.
4) __str__()
контейнеров, при вызове будет выполняться метод __repr__()
содержащихся в нем элементов.
5) str()
, вызванный в пределах __str__()
, потенциально может рекурсивно выполняться без базового случая и ошибки максимальной глубины рекурсии.
6) __repr__()
может вызывать repr()
, который автоматически пытается избежать бесконечной рекурсии, заменяя уже представленный объект на ...
.
Проще говоря:
__str__
используется, чтобы показать строковое представление вашего объекта , чтобы его могли легко прочитать другие.
__repr__
используется для отображения строкового представления объекта .
Допустим, я хочу создать класс Fraction
, в котором строковое представление дроби - '(1/2)', а объект (класс Fraction) должен быть представлен как 'Fraction (1,2)'
Итак, мы можем создать простой класс Fraction:
class Fraction:
def __init__(self, num, den):
self.__num = num
self.__den = den
def __str__(self):
return '(' + str(self.__num) + '/' + str(self.__den) + ')'
def __repr__(self):
return 'Fraction (' + str(self.__num) + ',' + str(self.__den) + ')'
f = Fraction(1,2)
print('I want to represent the Fraction STRING as ' + str(f)) # (1/2)
print('I want to represent the Fraction OBJECT as ', repr(f)) # Fraction (1,2)
Важно помнить, что контейнер
__str__
использует содержащиеся объекты__repr__
.
>>> from datetime import datetime
>>> from decimal import Decimal
>>> print (Decimal('52'), datetime.now())
(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 51, 26, 185000))
>>> str((Decimal('52'), datetime.now()))
"(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 52, 22, 176000))"
Python отдает предпочтение однозначности перед удобочитаемостью , вызов __str__
из tuple
вызывает __repr__
содержащихся объектов, «формальное представление» <958034> . Хотя формальное представление труднее читать, чем неформальное, оно недвусмысленно и более устойчиво к ошибкам.
Он использует __repr__
, когда он (__str__
) не определен! Значит, ты ошибаешься.
Отличные ответы уже охватывают разницу между __str__
и __repr__
, которая для меня сводится к тому, что первый из них читается даже конечным пользователем, а второй максимально полезен для разработчиков. Учитывая это, я обнаружил, что реализация по умолчанию __repr__
часто не позволяет достичь этой цели, потому что в ней опускается информация, полезная для разработчиков.
По этой причине, если у меня достаточно простой __str__
, я обычно просто пытаюсь получить лучшее из обоих миров с помощью чего-то вроде:
def __repr__(self):
return '{0} ({1})'.format(object.__repr__(self), str(self))
>>> print(decimal.Decimal(23) / decimal.Decimal("1.05"))
21.90476190476190476190476190
>>> decimal.Decimal(23) / decimal.Decimal("1.05")
Decimal('21.90476190476190476190476190')
Когда print()
вызывается для результата decimal.Decimal(23) / decimal.Decimal("1.05")
, печатается необработанное число; этот вывод находится в строковой форме , которая может быть достигнута с помощью __str__()
. Если мы просто введем выражение, мы получим вывод decimal.Decimal
- этот вывод находится в репрезентативной форме , что может быть достигнуто с помощью __repr__()
. Все объекты Python имеют две формы вывода. Строковая форма предназначена для удобства чтения человеком. Репрезентативная форма предназначена для вывода результатов, которые, если их передать интерпретатору Python, будут (когда это возможно) воспроизвести представленный объект.
В чем разница между
__str__
и__repr__
в Python?
__str__
(читается как «строка с двойным подчеркиванием») и __repr__
(читается как «dunder-repper» (для «представления»)) - оба специальных метода, которые возвращают строки в зависимости от состояния предмет.
__repr__
обеспечивает поведение резервного копирования, если __str__
отсутствует.
Итак, сначала следует написать __repr__
, который позволит вам восстановить эквивалентный объект из строки, которую он возвращает, например. используя eval
или вводя его символ в символ в оболочке Python.
В любое время позже можно написать __str__
для читаемого пользователем строкового представления экземпляра, если он считает это необходимым.
__str__
Если вы распечатываете объект или передаете его в format
, str.format
или str
, то, если определен метод __str__
, этот метод будет вызван, в противном случае будет использоваться __repr__
.
__repr__
Метод __repr__
вызывается встроенной функцией repr
, и это то, что отображается в вашей оболочке python, когда она оценивает выражение, возвращающее объект.
Поскольку он обеспечивает резервную копию для __str__
, если вы можете записать только один, начните с __repr__
Вот встроенная справка по repr
:
repr(...)
repr(object) -> string
Return the canonical string representation of the object.
For most object types, eval(repr(object)) == object.
То есть для большинства объектов, если вы вводите то, что печатается с помощью repr
, вы сможете создать эквивалентный объект. Но это не реализация по умолчанию.
Реализация по умолчанию __repr__
Объект по умолчанию __repr__
- (исходный код Python на C) примерно так:
def __repr__(self):
return '<{0}.{1} object at {2}>'.format(
self.__module__, type(self).__name__, hex(id(self)))
Это означает, что по умолчанию вы распечатываете модуль, из которого принадлежит объект, имя класса и шестнадцатеричное представление его местоположения в памяти - например:
<__main__.Foo object at 0x7f80665abdd0>
Эта информация не очень полезна, но нет способа выяснить, как можно точно создать каноническое представление любого данного экземпляра, и это лучше, чем ничего, по крайней мере, рассказать нам, как мы можем однозначно идентифицировать его в памяти.
Чем __repr__
может быть полезным?
Давайте посмотрим, насколько это может быть полезно, используя оболочку Python и объекты datetime
. Сначала нам нужно импортировать модуль datetime
:
import datetime
Если мы вызовем datetime.now
в оболочке, мы увидим все, что нам нужно для воссоздания эквивалентного объекта datetime. Это создается datetime __repr__
:
>>> datetime.datetime.now()
datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
Если мы печатаем объект datetime, мы видим удобный для чтения формат (фактически, ISO). Это реализовано с помощью datetime __str__
:
>>> print(datetime.datetime.now())
2015-01-24 20:05:44.977951
Легко воссоздать объект, который мы потеряли, потому что мы не присвоили его переменной, скопировав и вставив из вывода __repr__
, а затем распечатав его, и мы получили его в том же удобочитаемом для человека выводе. как другой объект:
>>> the_past = datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
>>> print(the_past)
2015-01-24 20:05:36.491180
Как их реализовать?
В процессе разработки вы захотите иметь возможность воспроизводить объекты в том же состоянии, если это возможно. Так, например, объект datetime определяет __repr__
(исходный код Python). Это довольно сложно из-за всех атрибутов, необходимых для воспроизведения такого объекта:
def __repr__(self):
"""Convert to formal string, for repr()."""
L = [self._year, self._month, self._day, # These are never zero
self._hour, self._minute, self._second, self._microsecond]
if L[-1] == 0:
del L[-1]
if L[-1] == 0:
del L[-1]
s = "%s.%s(%s)" % (self.__class__.__module__,
self.__class__.__qualname__,
", ".join(map(str, L)))
if self._tzinfo is not None:
assert s[-1:] == ")"
s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
if self._fold:
assert s[-1:] == ")"
s = s[:-1] + ", fold=1)"
return s
Если вы хотите, чтобы ваш объект имел более удобочитаемое представление, вы можете реализовать __str__
следующим образом. Вот как объект datetime (исходный код Python) реализует __str__
, что он легко делает, потому что у него уже есть функция для его отображения в формате ISO:
def __str__(self):
"Convert to string, for str()."
return self.isoformat(sep=' ')
Установить __repr__ = __str__
?
Это критика другого ответа здесь, предлагающего установить __repr__ = __str__
.
Настройка __repr__ = __str__
глупая - __repr__
является запасным вариантом для __str__
, и __repr__
, написанный для использования разработчиками при отладке, должен быть написан до того, как вы напишете __str__
.
Вам нужен __str__
только тогда, когда вам нужно текстовое представление объекта.
Заключение
Определите __repr__
для объектов, которые вы пишете, чтобы у вас и других разработчиков был воспроизводимый пример при его использовании в процессе разработки. Определите __str__
, когда вам нужно его строковое представление в удобочитаемой форме.
Разве это не должно быть что-то вроде type(obj).__qualname__
?
@SolomonUcko да, в Python 3, похоже, так оно и есть - я искал исходный код, в котором это реализовано, и обновлю свой ответ этой информацией, когда соберу ее.
Короче говоря, цель
__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(object)
Возвращает строку, содержащую печатное представление объекта. Это то же значение, что и при преобразованиях (обратное цитаты). Иногда бывает полезно иметь доступ к этой операции как обычная функция. Для многих типов эта функция пытается чтобы вернуть строку, которая даст объект с тем же значением, когда передается в
eval()
, в противном случае представление представляет собой строку, заключенную в угловые скобки, содержащие название типа объекта вместе с дополнительной информацией, часто включающей имя и адрес объекта. Класс может контролировать, что возвращает эта функция для его экземпляров, определив метод__repr__()
.
Вот документация для str:
str(object='')
Вернуть строку, содержащую хорошо печатаемый представление объекта. Для строк это возвращает строку сам. Разница с
repr(object)
в том, чтоstr(object)
не всегда пытайтесь вернуть строку, приемлемую дляeval()
; это цель - вернуть печатаемую строку. Если аргумент не указан, возвращает пустая строка,''
.
Что здесь означает печатаемая строка? Вы можете объяснить это, пожалуйста?
основываясь на приведенном выше примере с помощью "bitoffdev" и @deadly, мы можем увидеть, как str предназначен для конечного пользователя, потому что он дает нам только читаемую строку, где as repr предназначен для разработчиков, потому что он дает нам значение, а также тип. Если вы ищете ответы на собеседование, это было бы идеально.
Честно говоря, eval(repr(obj))
никогда не используется. Если вы обнаружите, что используете его, вам следует остановиться, потому что eval
опасен, а строки - очень неэффективный способ сериализации ваших объектов (вместо этого используйте pickle
).
Поэтому я бы рекомендовал установить __repr__ = __str__
. Причина в том, что str(list)
вызывает repr
для элементов (я считаю, что это один из самых больших недостатков дизайна Python, который не был исправлен Python 3). Фактический repr
, вероятно, не будет очень полезным в качестве вывода print [your, objects]
.
Чтобы уточнить это, по моему опыту, наиболее полезным вариантом использования функции repr
является помещение строки внутри другой строки (с использованием форматирования строки). Таким образом, вам не нужно беспокоиться об экранировании кавычек или чего-то еще. Но учтите, что здесь не происходит eval
.
Я думаю, это упускает суть. Использование eval(repr(obj))
- это проверка работоспособности и практическое правило - если это правильно воссоздает исходный объект, то у вас есть достойная реализация __repr__
. Не предполагается, что вы на самом деле сериализуете объекты таким образом.
eval
по своей сути не опасен. Не более опасен, чем unlink
, open
или запись в файлы. Должны ли мы прекратить запись в файлы, потому что, возможно, злонамеренная атака может использовать произвольный путь к файлу для помещения содержимого внутрь? Все опасно, если глупые люди тупо используют. Идиотизм опасен. Эффекты Даннинга-Крюгера опасны. eval
- это просто функция.
Мое практическое правило: __repr__
для разработчиков, __str__
для клиентов.
Это верно, потому что для obj = uuid.uuid1 () obj .__ str __ () имеет значение «2d7fc7f0-7706-11e9-94ae-0242ac110002», а obj .__ repr __ () - «UUID ('2d7fc7f0-7706-11e9-940002 ') ". Разработчикам нужна (ценность + происхождение), тогда как клиентам нужна ценность, и им все равно, как они ее получили!
Здесь клиент не обязательно означает конечного пользователя. Это клиент или пользователь объекта. Поэтому, если это SDK, разработчики SDK будут использовать __str__
, поэтому у обычных разработчиков есть читаемый объект. С другой стороны, __repr__
предназначен для самих разработчиков SDK.
Если вы специально не принимаете меры, чтобы гарантировать иное, большинство классов не дают полезных результатов ни для того, ни другого:
>>> 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__(object): return 'foo'
...
>>> print str(Sic())
foo
>>> print repr(Sic())
foo
>>> class Sic(object):
... def __str__(object): 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__
!
В своих модульных тестах я всегда проверяю, что eval(repr(foo))
оценивается как объект, равный foo
. Вы правы, что это не будет работать вне моих тестовых примеров, поскольку я не знаю, как модуль импортируется, но это, по крайней мере, гарантирует, что он работает в некотором предсказуемом контексте. Я думаю, что это хороший способ оценить, достаточно ли явен результат __repr__
. Выполнение этого в модульном тесте также помогает гарантировать, что __repr__
следует за изменениями в классе.
Я всегда стараюсь убедиться, что либо eval(repr(spam)) == spam
(по крайней мере, в правильном контексте), либо eval(repr(spam))
вызывает SyntaxError
. Так вы избежите путаницы. (И это почти верно для встроенных функций и большей части stdlib, за исключением, например, рекурсивных списков, где a=[]; a.append(a); print(eval(repr(a)))
дает вам [[Ellipses]]
…) Конечно, я не делаю этого, чтобы на самом деле используйте eval(repr(spam))
, за исключением проверки работоспособности в модульных тестах ... но я делаю иногда копирую и вставляю repr(spam)
в интерактивный сеанс.
Почему бы контейнерам (спискам, кортежам) не использовать __str__
для каждого элемента вместо __repr__
? Мне это кажется совершенно неправильным, поскольку я реализовал читаемый __str__
в своем объекте, а когда он является частью списка, я вместо этого вижу более уродливый __repr__
.
Просто столкнулся с досадной ошибкой, связанной с тем, что eval(repr(x))
не работает даже для встроенных типов: class A(str, Enum): X = 'x'
вызовет SyntaxError на eval(repr(A.X))
. Это печально, но понятно. Кстати, eval(str(A.X))
действительно работает, но, конечно, только если class A
входит в область видимости - так что это, вероятно, не очень полезно.
@SuperGeo Другие ответы охватывают это: контейнер str
используйте элемент repr
, потому что [1, 2, 3]
! = ["1", "2, 3"]
.
@abarnert: для пользовательского class Spam
, eval(repr(spam)) == spam
также потребуется реализовать Spam.__eq__
, верно? По умолчанию object.__eq__
использует is
(docs).
__repr__
: представление объекта python обычно eval преобразует его обратно в этот объект
__str__
: это то, что вы думаете, это объект в текстовой форме
например,
>>> s="""w'o"w"""
>>> repr(s)
'\'w\\\'o"w\''
>>> str(s)
'w\'o"w'
>>> eval(str(s))==s
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
w'o"w
^
SyntaxError: EOL while scanning single-quoted string
>>> eval(repr(s))==s
True
Из (неофициальной) справочной вики по Python (архивная копия) от effbot:
__str__
« вычисляет« неформальное »строковое представление объекта. Оно отличается от __repr__
тем, что оно не обязательно должно быть действительным выражением Python: вместо него можно использовать более удобное или краткое представление . "
__repr__
ни в коем случае не требуется для возврата действительного выражения Python.
Определенно не согласен с вашим мнением, что отладка - это не лучший вариант. Для разработки используйте отладчик (и / или ведение журнала), для производства используйте ведение журнала. С отладчиком вы можете видеть все, что пошло не так, когда возникла проблема. Вы можете увидеть полную картину. Если вы не регистрируете ВСЕ, вы не сможете этого получить. Кроме того, если вы регистрируете все, что вам нужно, вам придется пролезть через тонны данных, чтобы получить то, что вы хотите.
Отличный ответ (за исключением того, что не используют отладчики). Я просто хотел бы добавить ссылку на этот другие вопросы и ответы о str и unicode в Python 3, которые могут иметь отношение к обсуждению для людей, которые сделали выключатель.
plus1 для отладчиков бесполезны и не масштабируются ни копейки. Вместо этого увеличьте пропускную способность журналирования. И да, это был хорошо написанный пост. Оказалось, что
__repr__
- это то, что мне нужно для отладки. Спасибо за помощь.не считая вашего глупого отладчика, я выучил% r, и это все равно стоит голоса
@moshez стоит упомянуть, что контейнеры (списки, словари) всегда используют
__repr__
, а не__str__
.print(str([today, today]))
печатает[datetime.datetime(2019, 1, 8, 20, 5, 27, 24162), datetime.datetime(2019, 1, 8, 20, 5, 27, 24162)]
.Безопасно ли
eval(repr())
использовать, скажем, в модуле API, который хранит пароли пользователей?Я слышал, что переменная
l
(строчная буква «L») будет синтаксической ошибкой в python 4.0;)об отладчике и без отладчика: не получайте таких устоявшихся мнений. В некоторых приложениях отладка нереалистична, как правило, когда задействовано в реальном времени или когда ваш код выполняется только удаленно на платформе с ограниченным доступом или без консоли. В большинстве других случаев будет намного быстрее остановиться на исключении для расследования или установить точку останова, потому что вам не нужно проходить тысячи строк журнала (что загромождает ваш диск и замедляет работу приложения). Наконец, не всегда можно вести журнал, например, на встроенных устройствах, там отладчик тоже ваш друг.
Что касается отладки и ведения журнала, они оба полезны. Если ошибка воспроизводима, отладка упрощается. Если ошибка случайная, необходимо вести журнал.
@RedGlyph "когда ваш код выполняется только удаленно на платформе" - это запах кода. Он неявно не имеет модульных тестов и т. Д. Лучшим примером может быть отладка редких стохастических (недетерминированных) ошибок, ошибок IE, которые трудно воспроизвести. Акцент на проблеме, требующей удаленной платформы, а не кода.
@PhilipCouling Обязательно прочтите внимательно и используйте правильный словарный запас, прежде чем сокращать ответы и комментарии других. Мы используем «запах кода», когда при чтении кода обнаруживается что-то, что требует рефакторинга, потому что он скрывает потенциальную проблему, которая может привести к возникновению трудных для поиска ошибок позже или создать плохую общую структуру. Наличие или отсутствие модульного теста - это тоже не то, что вы можете предположить на этом этапе (и это определенно не «неявно»). Наконец, ваше утверждение о «лучшем примере» не имеет никаких оснований, я действительно использовал отладчик для решения тех проблем, которые часто не соответствовали ведению журнала.
@RedGlyph Я знаю, что это , что означает. Код, который может быть выполнен только удаленно, действительно затрудняет тестирование, он подразумевает необходимость в каком-то удаленном ресурсе. Модульное тестирование часто имитирует или заглушает такие ресурсы, что приводит к тому, «почему код не может быть выполнен локально». Если код действительно не может подключиться к отладчику, я бы обеспокоился, какие ошибки были скрыты в результате плохого тестирования. Я действительно считаю, что истинный код, предназначенный только для удаленного доступа, не рекомендуется. Вы говорите, что использовали отладчик для «ошибок, которые трудно воспроизвести» . Это смелое заявление.
«Я не верю в отладчики. Я действительно не знаю, как использовать какой-либо отладчик» - это меня взбесило, напоминает мне старый эпизод в Южном парке, где парень никогда не пробовал наркотики, поэтому он уверен, что они злые. Полагаю, мы все боимся неизвестного.
Небольшое обновление для последних версий Python 3: вы можете использовать f-строки и по-прежнему вызывать
__repr__
встраиваемых данных, добавив!r
: вы можете заменить"MyClass(this=%r,that=%r)" % (self.this,self.that)
наf"MyClass(this={self.this!r},that={self.that!r})"
. В противном случае спасибо за этот отличный пост!