Разница между __str__ и __repr__? - PullRequest
       165

Разница между __str__ и __repr__?

2417 голосов
/ 17 сентября 2009

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

Ответы [ 22 ]

2473 голосов
/ 13 апреля 2010

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

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

  • Реализация по умолчанию бесполезна (трудно придумать, чего бы не было, но да)
  • __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)

Но вы должны сделать последний шаг - убедиться, что у каждого объекта, который вы реализуете, есть полезное повторение, чтобы такой код мог просто работать. Вот почему возникает вопрос «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, иметь такую ​​строку, как 192.168.1.1, просто прекрасно. При реализации абстракции даты / времени str может быть «2010/4/12 15:35:22» и т. Д. Цель состоит в том, чтобы представить ее так, чтобы ее захотел прочитать пользователь, а не программист. Отрежьте ненужные цифры, притворитесь каким-то другим классом - пока он поддерживает читабельность, это улучшение.

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

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

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

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

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

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

Резюме

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

452 голосов
/ 17 сентября 2009

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

363 голосов
/ 17 сентября 2009

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

>>> 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__!

155 голосов
/ 17 сентября 2009

__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
134 голосов
/ 25 октября 2013

Короче говоря, цель __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(object='')

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

97 голосов
/ 25 января 2015

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

__str__ (читается как «строка dunder (двойное подчеркивание)») и __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__ ( C источник Python ) что-то вроде:

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 __repr__:

>>> datetime.datetime.now()
datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)

Если мы печатаем объект datetime, мы видим хороший читабельный (на самом деле, ISO) формат. Это реализовано в datetime's __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 source ). Это довольно сложно из-за всех атрибутов, необходимых для воспроизведения такого объекта:

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__, когда вам нужно его читаемое строковое представление.

25 голосов
/ 08 сентября 2016

Помимо всех приведенных ответов, я хотел бы добавить несколько пунктов: -

1) __repr__() вызывается, когда вы просто пишете имя объекта на интерактивной консоли python и нажимаете ввод.

2) __str__() вызывается при использовании объекта с оператором печати. ​​

3) Если отсутствует __str__, то print и любая функция, использующая str(), вызывает __repr__() объекта.

4) __str__() контейнеров, при вызове выполнит __repr__() метод содержащихся в нем элементов.

5) str(), вызываемый в пределах __str__(), потенциально может рекурсировать без базового случая и ошибки на максимальной глубине рекурсии.

6) __repr__() может вызвать repr(), который попытается автоматически избежать бесконечной рекурсии, заменив уже представленный объект на ....

17 голосов
/ 21 мая 2017

На странице 358 книги Сценарии Python для вычислительной науки Ганса Петтера Лангтангена ясно говорится, что

  • __repr__ стремится к полному строковому представлению объекта;
  • __str__ - вернуть красивую строку для печати. ​​

Итак, я предпочитаю понимать их как

  • repr = воспроизводить
  • str = строка (представление)

с точки зрения пользователя хотя это недоразумение я сделал, изучая питон.

Небольшой, но хороший пример также приведен на той же странице:

Пример * * одна тысяча тридцать одна 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'

12 голосов
/ 15 ноября 2012

Честно говоря, eval(repr(obj)) никогда не используется. Если вы обнаружите, что используете его, вам следует остановиться, потому что eval опасно, а строки - очень неэффективный способ сериализации ваших объектов (используйте pickle).

Поэтому я бы рекомендовал установить __repr__ = __str__. Причина в том, что str(list) вызывает repr для элементов (я считаю, что это один из самых больших недостатков Python в дизайне, который не был исправлен в Python 3). Фактический repr, вероятно, не будет очень полезен как вывод print [your, objects].

Для уточнения этого, по моему опыту, наиболее полезный вариант использования функции repr - это поместить строку в другую строку (используя форматирование строки). Таким образом, вам не нужно беспокоиться о том, чтобы избежать кавычек или чего-то еще. Но обратите внимание, что здесь не происходит eval.

11 голосов
/ 12 января 2016

Проще говоря:

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

__repr__ используется для отображения строкового представления объекта .

Допустим, я хочу создать класс Fraction, в котором строковое представление дроби равно '(1/2)', а объект (класс дроби) должен быть представлен как 'Дробь (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)
...