Ответы:
Алекс подвела итог, но, на удивление, была слишком лаконична.
Во-первых, позвольте мне повторить основные моменты в посте Алекса :
__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)
Но вы должны сделать последний шаг - убедиться, что у каждого объекта, который вы реализуете, есть полезный repr, чтобы подобный код мог просто работать. Вот почему возникает вопрос «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__
Это кажется удивительным, не так ли? Это немного, но насколько читабельно было бы, если бы они использовали их __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__если вы считаете, что было бы полезно иметь строковую версию с ошибками на стороне читабельности.
__repr__было то, что мне нужно для отладки. Спасибо за помощь.
Мое эмпирическое правило: __repr__для разработчиков, __str__для клиентов.
__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находится в области видимости - так что, вероятно, это не очень полезно.
strэлемент использования контейнера, reprпотому что [1, 2, 3]! = ["1", "2, 3"].
__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
Короче говоря, цель
__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(object='')Вернуть строку, содержащую хорошо печатаемое представление объекта. Для строк это возвращает саму строку. Разница с тем
repr(object), чтоstr(object)не всегда пытается вернуть строку, которая является приемлемой дляeval(); его цель - вернуть строку для печати. Если аргумент не указан, возвращает пустую строку''.
В чем разница между
__str__и__repr__в Python?
__str__(читается как «строка dunder (двойное подчеркивание)») и __repr__(читается как «dunder-repper» (для «представления»)) оба являются специальными методами, которые возвращают строки, основанные на состоянии объекта.
__repr__обеспечивает резервное копирование, если __str__отсутствует.
Поэтому сначала нужно написать a, __repr__который позволит вам восстановить экземпляр эквивалентного объекта из строки, которую он возвращает, например, используя evalили вводя его символ за символом в оболочке Python.
В любое время позже можно написать __str__для читаемого пользователем строкового представления экземпляра, когда он считает это необходимым.
__str__Если вы печатаете объект, или передать его format, str.formatили str, то если __str__метод определен, что метод будет вызван, в противном случае, __repr__будет использоваться.
__repr____repr__Метод вызывается функция встроена reprи то , что находит отражение в вашем питоне оболочке , когда он вычисляет выражение , которое возвращает объект.
Поскольку он обеспечивает резервное копирование __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__быть полезно?Давайте посмотрим, насколько это может быть полезно, используя оболочку и datetimeобъекты Python . Сначала нам нужно импортировать 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__и a __repr__, написанный для разработчиков при отладке, должен быть написан до того, как вы напишите a __str__.
Вам нужно __str__только тогда, когда вам нужно текстовое представление объекта.
Определите __repr__для объектов, которые вы пишете, чтобы вы и другие разработчики имели воспроизводимый пример при использовании его в процессе разработки. Определите, __str__когда вам нужно удобочитаемое представление строки.
type(obj).__qualname__?
На странице 358 книги Ханса Петтера Лангтангена «Создание сценариев Python для вычислительной науки » четко сказано, что
__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'
reprвоспроизводить. Лучше думать об этом как о представлении.
Помимо всех ответов, я хотел бы добавить несколько моментов:
1) __repr__()вызывается, когда вы просто пишете имя объекта на интерактивной консоли Python и нажимаете ввод.
2) __str__()вызывается при использовании объекта с оператором печати.
3) В случае, если __str__отсутствует, то печатать и любую функцию, используя str()вызовы __repr__()объекта.
4) __str__()из контейнеров, при вызове будет выполнять __repr__()метод содержащихся в нем элементов.
5) str()вызов внутри __str__()мог потенциально рекурсировать без базового случая и ошибки на максимальной глубине рекурсии.
6) __repr__()может вызвать, repr()который попытается автоматически избежать бесконечной рекурсии, заменив уже представленный объект на ....
Проще говоря:
__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)
Честно говоря, eval(repr(obj))никогда не используется. Если вы обнаружите, что используете его, вам следует остановиться, потому что evalэто опасно, а строки - очень неэффективный способ сериализации ваших объектов (используйте pickleвместо этого).
Поэтому я бы порекомендовал настройку __repr__ = __str__. Причина заключается в том, что str(list)вызовы reprна элементах (я считаю , что это один из самых больших недостатков конструкции Питона , который не был адресован на Python 3). Фактическое repr, вероятно, не будет очень полезным, как результат print [your, objects].
Чтобы это уточнить, по моему опыту, наиболее полезный вариант использования reprфункции - поместить строку в другую строку (используя форматирование строки). Таким образом, вам не нужно беспокоиться о том, чтобы избежать кавычек или чего-то еще. Но учтите, что здесь ничего не evalпроисходит.
eval(repr(obj))- это проверка работоспособности и практическое правило - если это правильно воссоздает исходный объект, то у вас есть достойная __repr__реализация. Это не означает, что вы действительно сериализуете объекты таким образом.
evalне является опасным по своей природе. Не более опасно , чем unlink, openили записи файлов. Должны ли мы прекратить запись в файлы, потому что, возможно, злонамеренная атака может использовать произвольный путь к файлу для помещения содержимого внутрь? Все опасно, если тупо используется немыми людьми. Идиотизм опасен. Эффекты Даннинг-Крюгера опасны. evalэто просто функция.
Из (неофициальной) справочной вики по Python (архивная копия) от effbot:
__str__« вычисляет« неформальное »строковое представление объекта. Это отличается от того, __repr__что оно не обязательно должно быть допустимым выражением Python: вместо него можно использовать более удобное или краткое представление ».
__repr__ни в коем случае не требуется возвращать пустое выражение Python.
str - Создает новый строковый объект из данного объекта.
repr - Возвращает каноническое строковое представление объекта.
Различия:
ул ():
магнезии ():
Один аспект, который отсутствует в других ответах. Это правда, что в целом картина такова:
__str__: человекочитаемый__repr__: однозначное, возможно машиночитаемое черезevalК сожалению, это различие некорректно, поскольку Python REPL, а также IPython используют __repr__для печати объектов в консоли REPL (см. Связанные вопросы по Python и IPython ). Таким образом, проекты, предназначенные для работы с интерактивной консолью (например, Numpy или Pandas), начали игнорировать вышеприведенные правила и __repr__вместо этого предоставляют удобочитаемую реализацию.
Из книги Свободный Питон :
Основным требованием для объекта Python является предоставление пригодных для использования строковых представлений, одно из которых используется для отладки и ведения журнала, другое - для представления конечным пользователям. Именно поэтому
специальные методы__repr__и__str__существуют в модели данных.
Отличные ответы уже охватывают разницу между __str__и __repr__, которая для меня сводится к тому, что первое читается даже конечным пользователем, а второе максимально полезно для разработчиков. Учитывая это, я считаю, что реализация по умолчанию __repr__часто не может достичь этой цели, потому что в ней отсутствует информация, полезная для разработчиков.
По этой причине, если у меня все достаточно просто __str__, я, как правило, просто пытаюсь получить лучшее из обоих миров с чем-то вроде:
def __repr__(self):
return '{0} ({1})'.format(object.__repr__(self), str(self))
Важно помнить, что контейнер
__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__, «формальное» представление объекта. Хотя формальное представление труднее читать, чем неформальное, оно однозначно и более устойчиво к ошибкам.
__repr__ когда оно ( __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'
>>> 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__может быть вызван для объекта путем вызова 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__интуитивно и постоянно различать их вообще.
__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__может возвращать любое выражение Python.__str__реализация отсутствует, __repr__функция используется как запасной вариант. Нет возврата, если __repr__реализация функции отсутствует.__repr__функция возвращает строковое представление объекта, мы можем пропустить реализацию __str__функции.Источник: https://www.journaldev.com/22460/python-str-repr-functions