Respostas:
Alex resumiu bem, mas, surpreendentemente, foi muito sucinto.
Primeiro, deixe-me reiterar os pontos principais no post de Alex :
__repr__
objetivo é ser inequívoco__str__
objetivo é ser legível__str__
usa objetos contidos '__repr__
A implementação padrão é inútil
Isso é principalmente uma surpresa, porque os padrões do Python tendem a ser bastante úteis. No entanto, nesse caso, ter um padrão para o __repr__
qual agiria como:
return "%s(%r)" % (self.__class__, self.__dict__)
teria sido muito perigoso (por exemplo, muito fácil entrar em recursão infinita se objetos se referirem uns aos outros). Então, o Python aguenta. Observe que há um padrão que é verdadeiro: se __repr__
for definido e __str__
não, o objeto se comportará como se __str__=__repr__
.
Isso significa, em termos simples: quase todos os objetos que você implementa devem ter um funcional __repr__
que seja útil para entender o objeto. Implementando__str__
é opcional: faça isso se você precisar de uma funcionalidade de "impressão bonita" (por exemplo, usada por um gerador de relatórios).
O objetivo __repr__
é ser inequívoco
Deixe-me falar e dizer: não acredito em depuradores. Realmente não sei como usar nenhum depurador e nunca o usei seriamente. Além disso, acredito que a grande falha nos depuradores é sua natureza básica - a maioria das falhas depuradoras aconteceu há muito tempo, em uma galáxia muito distante. Isso significa que eu acredito, com fervor religioso, na exploração madeireira. O registro é a força vital de qualquer sistema de servidor decente de ignorar. O Python facilita o log: com talvez alguns wrappers específicos do projeto, tudo o que você precisa é de um
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)
Mas você precisa executar o último passo - verifique se todos os objetos implementados têm uma repr útil, para que um código como esse possa funcionar. É por isso que a coisa “eval” surge: se você tem informações suficientes eval(repr(c))==c
, significa que você sabe tudo o que há para saber c
. Se isso é fácil o suficiente, pelo menos de uma maneira difusa, faça-o. Caso contrário, verifique se você tem informações suficientes c
. Eu costumo usar um eval-like formato: "MyClass(this=%r,that=%r)" % (self.this,self.that)
. Isso não significa que você pode realmente criar MyClass, ou que esses são os argumentos corretos do construtor - mas é uma forma útil de expressar “isso é tudo o que você precisa saber sobre essa instância”.
Nota: Eu usei %r
acima, não %s
. Você sempre deseja usar repr()
[ou %r
formatar caracteres, de forma equivalente] dentro da __repr__
implementação, ou está derrotando o objetivo de repr. Você quer ser capaz de diferenciar MyClass(3)
e MyClass("3")
.
O objetivo __str__
é ser legível
Especificamente, não se destina a ser inequívoco - observe isso str(3)==str("3")
. Da mesma forma, se você implementar uma abstração de IP, ter o str dela parecido com 192.168.1.1 é bom. Ao implementar uma abstração de data / hora, o str pode ser "2010/4/12 15:35:22" etc. O objetivo é representá-lo de uma maneira que um usuário, não um programador, deseje lê-lo. Pique dígitos inúteis, finja ser de outra classe - desde que apóie a legibilidade, é uma melhoria.
O contêiner __str__
usa objetos contidos '__repr__
Isso parece surpreendente, não é? É um pouco, mas quão legível seria se usasse o deles __str__
?
[moshe is, 3, hello
world, this is a list, oh I don't know, containing just 4 elements]
Não muito. Especificamente, as seqüências de caracteres em um contêiner acham muito fácil perturbar sua representação. Diante da ambiguidade, lembre-se, Python resiste à tentação de adivinhar. Se você deseja o comportamento acima ao imprimir uma lista, basta
print "[" + ", ".join(l) + "]"
(você provavelmente também pode descobrir o que fazer com os dicionários.
Sumário
Implemente __repr__
para qualquer classe que você implemente. Isso deve ser uma segunda natureza. Implemente __str__
se você acha que seria útil ter uma versão de string que erre no lado da legibilidade.
__repr__
era o que eu precisava para depurar. Obrigado pela ajuda.
Minha regra de ouro: __repr__
é para desenvolvedores, __str__
é para clientes.
__str__
para que os desenvolvedores normais tenham um objeto legível. Por outro lado, __repr__
é para os próprios desenvolvedores do SDK.
A menos que você aja especificamente para garantir o contrário, a maioria das classes não terá resultados úteis para:
>>> class Sic(object): pass
...
>>> print str(Sic())
<__main__.Sic object at 0x8b7d0>
>>> print repr(Sic())
<__main__.Sic object at 0x8b7d0>
>>>
Como você vê - nenhuma diferença e nenhuma informação além da classe e do objeto id
. Se você substituir apenas um dos dois ...:
>>> 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>
>>>
como você vê, se você substituir __repr__
, isso também é usado __str__
, mas não vice-versa.
Outros boatos cruciais a saber: __str__
em um contêiner embutido, usa __repr__
NÃO __str__
, para os itens que ele contém. E, apesar das palavras sobre o assunto encontradas nos documentos típicos, quase ninguém se incomoda em tornar os __repr__
objetos uma string que eval
pode ser usada para criar um objeto igual (é muito difícil e NÃO saber como o módulo relevante foi realmente importado o torna realmente impossível).
Então, meu conselho: concentre-se em tornar __str__
razoavelmente legível para o ser humano e o __repr__
mais inequívoco possível, mesmo que isso interfira no objetivo impreciso e impreciso de tornar __repr__
o valor retornado aceitável como entrada __eval__
!
eval(repr(foo))
se o objeto é igual a foo
. Você está certo de que não funcionará fora dos meus casos de teste, pois não sei como o módulo é importado, mas isso pelo menos garante que funcione em algum contexto previsível. Eu acho que é uma boa maneira de avaliar se o resultado de__repr__
é suficientemente explícito. Fazer isso em um teste de unidade também ajuda a garantir que as __repr__
alterações sejam seguidas na classe.
eval(repr(spam)) == spam
(pelo menos no contexto certo) ou eval(repr(spam))
gera um SyntaxError
. Dessa forma, você evita confusão. (E isso é quase verdade para os builtins e a maioria dos stdlib, exceto, por exemplo, listas recursivas, onde a=[]; a.append(a); print(eval(repr(a)))
você fornece [[Ellipses]]
...) É claro que eu não faço isso para realmente usá-lo eval(repr(spam))
, exceto como verificação de sanidade nos testes de unidade ... mas eu que , por vezes, copiar e colar repr(spam)
em uma sessão interativa.
__str__
para cada elemento em vez de __repr__
? Parece totalmente errado para mim, pois implementei um legível __str__
no meu objeto e, quando faz parte de uma lista, vejo o mais feio __repr__
.
eval(repr(x))
falha mesmo para os tipos internos: class A(str, Enum): X = 'x'
aumentará o SyntaxError eval(repr(A.X))
. É triste, mas compreensível. BTW, eval(str(A.X))
na verdade funciona, mas é claro apenas se class A
estiver no escopo - portanto, provavelmente não é muito útil.
str
elemento de uso de contêiner repr
porque [1, 2, 3]
! = ["1", "2, 3"]
.
__repr__
: a representação do objeto python geralmente eval o converterá novamente nesse objeto
__str__
: é o que você acha que é esse objeto em forma de texto
por exemplo
>>> 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
Em resumo, o objetivo de
__repr__
é ser inequívoco e__str__
legível.
Aqui está um bom exemplo:
>>> 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)'
Leia esta documentação para repr:
repr(object)
Retorne uma string contendo uma representação imprimível de um objeto. Esse é o mesmo valor gerado pelas conversões (aspas reversas). Às vezes, é útil poder acessar esta operação como uma função comum. Para muitos tipos, essa função tenta retornar uma sequência que daria origem a um objeto com o mesmo valor quando transmitida
eval()
; caso contrário, a representação é uma sequência entre colchetes angulares que contém o nome do tipo do objeto juntamente com informações adicionais geralmente incluindo o nome e o endereço do objeto. Uma classe pode controlar o que essa função retorna para suas instâncias, definindo um__repr__()
método.
Aqui está a documentação para str:
str(object='')
Retorna uma string contendo uma representação bem imprimível de um objeto. Para cadeias, isso retorna a própria cadeia. A diferença
repr(object)
é questr(object)
nem sempre tente retornar uma string aceitáveleval()
; seu objetivo é retornar uma sequência imprimível. Se nenhum argumento for fornecido, retornará a string vazia''
,.
Qual é a diferença entre
__str__
e__repr__
no Python?
__str__
(lidos como "dunder (sublinhado duplo)") e __repr__
(lidos como "dunder-repper" (para "representação")) são métodos especiais que retornam seqüências de caracteres com base no estado do objeto.
__repr__
fornece comportamento de backup se __str__
estiver ausente.
Portanto, deve-se primeiro escrever um __repr__
que permita restabelecer um objeto equivalente a partir da string que ele retorna, por exemplo, usando eval
ou digitando caractere por caractere em um shell Python.
A qualquer momento, pode-se escrever um __str__
para uma representação de string legível pelo usuário da instância, quando julgar necessário.
__str__
Se você imprimir um objeto ou passá-lo para format
,, str.format
ou str
, se um __str__
método for definido, esse método será chamado, caso contrário, __repr__
será usado.
__repr__
O __repr__
método é chamado pela função embutida repr
e é o que é ecoado no shell python quando avalia uma expressão que retorna um objeto.
Como ele fornece um backup __str__
, se você puder escrever apenas um, comece com__repr__
Aqui está a ajuda integrada sobre repr
:
repr(...)
repr(object) -> string
Return the canonical string representation of the object.
For most object types, eval(repr(object)) == object.
Ou seja, para a maioria dos objetos, se você digitar o que é impresso repr
, poderá criar um objeto equivalente. Mas essa não é a implementação padrão.
__repr__
O objeto padrão __repr__
é ( fonte C Python ) algo como:
def __repr__(self):
return '<{0}.{1} object at {2}>'.format(
self.__module__, type(self).__name__, hex(id(self)))
Isso significa que, por padrão, você imprimirá o módulo do qual o objeto é, o nome da classe e a representação hexadecimal de sua localização na memória - por exemplo:
<__main__.Foo object at 0x7f80665abdd0>
Essas informações não são muito úteis, mas não há como derivar como se pode criar com precisão uma representação canônica de qualquer instância, e é melhor que nada, pelo menos nos dizendo como podemos identificá-las exclusivamente na memória.
__repr__
ser útil?Vamos ver como isso pode ser útil, usando o shell e os datetime
objetos Python . Primeiro, precisamos importar o datetime
módulo:
import datetime
Se chamarmos datetime.now
o shell, veremos tudo o que precisamos para recriar um objeto datetime equivalente. Isso é criado pela data __repr__
e hora :
>>> datetime.datetime.now()
datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
Se imprimirmos um objeto datetime, veremos um bom formato legível por humanos (de fato, ISO). Isso é implementado pelo datetime __str__
:
>>> print(datetime.datetime.now())
2015-01-24 20:05:44.977951
É simples recriar o objeto que perdemos porque não o atribuímos a uma variável, copiando e colando da __repr__
saída, imprimindo-a e obtendo-a na mesma saída legível por humanos que o outro objeto:
>>> the_past = datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
>>> print(the_past)
2015-01-24 20:05:36.491180
Enquanto você está desenvolvendo, você poderá reproduzir objetos no mesmo estado, se possível. Por exemplo, é assim que o objeto datetime define __repr__
( fonte Python ). É bastante complexo, devido a todos os atributos necessários para reproduzir esse objeto:
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
Se você deseja que seu objeto tenha uma representação legível por humanos, você pode implementar a __str__
seguir. Veja como o objeto datetime ( fonte Python ) implementa __str__
, o que é fácil porque ele já tem uma função para exibi-lo no formato ISO:
def __str__(self):
"Convert to string, for str()."
return self.isoformat(sep=' ')
__repr__ = __str__
?Esta é uma crítica de outra resposta aqui que sugere configuração __repr__ = __str__
.
A configuração __repr__ = __str__
é boba - __repr__
é um substituto para __str__
e um __repr__
, escrito para uso dos desenvolvedores na depuração, deve ser escrito antes de você escrever um __str__
.
Você precisa de um __str__
somente quando precisar de uma representação textual do objeto.
Defina os __repr__
objetos que você escreve para que você e outros desenvolvedores tenham um exemplo reproduzível ao usá-lo à medida que desenvolve. Defina __str__
quando você precisa de uma representação de sequência legível por humanos.
type(obj).__qualname__
?
Na página 358 do livro Python scripting for science computational, de Hans Petter Langtangen, afirma claramente que
__repr__
objetivo é uma representação completa da string do objeto;__str__
é retornar uma boa string para impressão.Então, eu prefiro entendê-los como
do ponto de vista do usuário, embora esse seja um mal-entendido que eu fiz ao aprender python.
Um pequeno mas bom exemplo também é dado na mesma página da seguinte maneira:
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
reprodução. É melhor pensar nisso como representar.
Além de todas as respostas dadas, gostaria de acrescentar alguns pontos: -
1) __repr__()
é chamado quando você simplesmente escreve o nome do objeto no console python interativo e pressiona enter.
2) __str__()
é invocado quando você usa o objeto com a instrução print.
3) Caso __str__
esteja faltando, imprima e qualquer função usando str()
invoca__repr__()
de objeto.
4) __str__()
de contêineres, quando invocados, executarão o __repr__()
método de seus elementos contidos.
5) str()
chamado dentro __str__()
poderia recorrer sem um caso base e erro na profundidade máxima da recursão.
6) __repr__()
pode chamar o repr()
que tentará evitar a recursão infinita automaticamente, substituindo um objeto já representado por ...
.
Simplificando:
__str__
é usado para mostrar uma representação de string do seu objeto para ser lida facilmente por outras pessoas.
__repr__
é utilizado para mostrar uma representação em cadeia do objecto.
Digamos que eu queira criar uma Fraction
classe em que a representação de string de uma fração seja '(1/2)' e o objeto (classe Fraction) seja representado como 'Fraction (1,2)'
Assim, podemos criar uma classe Fraction simples:
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)
Com toda a honestidade, eval(repr(obj))
nunca é usado. Se você se encontra usando, deve parar, porque eval
é perigoso, e as cordas são uma maneira muito ineficiente de serializar seus objetos (usepickle
vez disso).
Portanto, eu recomendaria a configuração __repr__ = __str__
. A razão é que str(list)
chama repr
os elementos (considero que essa é uma das maiores falhas de design do Python que não foi abordada pelo Python 3). Um real repr
provavelmente não será muito útil como a saída de print [your, objects]
.
Para qualificar isso, na minha experiência, o caso de uso mais útil da repr
função é colocar uma string dentro de outra (usando a formatação da string). Dessa forma, você não precisa se preocupar com escapar aspas ou algo assim. Mas observe que não há nada eval
acontecendo aqui.
eval(repr(obj))
é um teste de sanidade e uma regra de ouro - se isso recriar o objeto original corretamente, você terá uma __repr__
implementação decente . Não se pretende que você serialize objetos dessa maneira.
eval
não é inerentemente perigoso. Não é mais perigoso do que unlink
, open
ou a gravar arquivos. Devemos parar de gravar em arquivos, porque talvez um ataque malicioso possa usar um caminho arbitrário para inserir conteúdo? Tudo é perigoso se usado de maneira burra por pessoas burras. A idiotice é perigosa. Os efeitos de Dunning-Kruger são perigosos. eval
é apenas uma função.
De um Wiki de referência do Python (não oficial) (cópia de arquivo) por effbot:
__str__
" calcula a representação de string" informal "de um objeto. Isso difere de __repr__
que não precisa ser uma expressão válida do Python: uma representação mais conveniente ou concisa pode ser usada. "
__repr__
não é de forma alguma necessário para retornar uma expressão Python vaild.
Um aspecto que está faltando em outras respostas. É verdade que, em geral, o padrão é:
__str__
: legível por humanos__repr__
: inequívoco, possivelmente legível por máquina viaeval
Infelizmente, essa diferenciação é falha, porque o Python REPL e também o IPython usam __repr__
para imprimir objetos em um console REPL (consulte as perguntas relacionadas para Python e IPython ). Assim, projetos direcionados ao trabalho de console interativo (por exemplo, Numpy ou Pandas) começaram a ignorar as regras acima e, em vez disso, fornecem uma __repr__
implementação legível por humanos .
Do livro Fluent Python :
Um requisito básico para um objeto Python é fornecer representações de string utilizáveis em si, uma usada para depuração e registro e outra para apresentação aos usuários finais. É por isso que os
métodos especiais__repr__
e__str__
existem no modelo de dados.
Excelentes respostas já cobrem a diferença entre __str__
e __repr__
, o que para mim se resume ao fato de o primeiro ser legível até mesmo por um usuário final, e o último ser o mais útil possível para os desenvolvedores. Dado isso, acho que a implementação padrão __repr__
geralmente falha em atingir esse objetivo porque omite informações úteis para os desenvolvedores.
Por esse motivo, se eu tiver um simples __str__
, geralmente apenas tento obter o melhor dos dois mundos com algo como:
def __repr__(self):
return '{0} ({1})'.format(object.__repr__(self), str(self))
Uma coisa importante a ter em mente é que o contêiner
__str__
usa objetos contidos '__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))"
O Python favorece a ambiguidade sobre a legibilidade , a __str__
chamada de a tuple
chama de objetos contidos __repr__
, a representação "formal" de um objeto. Embora a representação formal seja mais difícil de ler do que a informal, ela é inequívoca e mais robusta contra bugs.
__repr__
quando ( __str__
) não está definido! Então, você está errado.
Em poucas palavras:
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')
Quando print()
é chamado, o resultado do decimal.Decimal(23) / decimal.Decimal("1.05")
número bruto é impresso; essa saída está na forma de string que pode ser obtida com __str__()
. Se simplesmente inserirmos a expressão, obtemos uma decimal.Decimal
saída - essa saída está na forma representacional que pode ser obtida com __repr__()
. Todos os objetos Python têm duas formas de saída. O formato da string foi projetado para ser legível por humanos. A forma representacional é projetada para produzir resultados que, se alimentados a um intérprete Python, (quando possível) reproduziriam o objeto representado.
__str__
pode ser chamado em um objeto chamando str(obj)
e deve retornar uma sequência legível por humanos.
__repr__
pode ser chamado em um objeto chamando repr(obj)
e deve retornar um objeto interno (campos / atributos do objeto)
Este exemplo pode ajudar:
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
Compreender __str__
e__repr__
distinga-os intuitivamente e permanentemente.
__str__
retorna o corpo disfarçado de um determinado objeto para legível dos olhos
__repr__
possam ser retornar o corpo real de carne de um determinado objeto (retornar em si) para que a ambiguidade seja identificada.
Veja em um exemplo
In [30]: str(datetime.datetime.now())
Out[30]: '2017-12-07 15:41:14.002752'
Disguised in string form
Quanto a __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.
Podemos realizar operações aritméticas de forma __repr__
conveniente.
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)
se aplicar a operação em __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'
Retorna nada além de erro.
Outro exemplo.
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
Espero que isso ajude a construir bases concretas para explorar mais respostas.
__str__
deve retornar o objeto string enquanto __repr__
pode retornar qualquer expressão python.__str__
implementação estiver ausente, a __repr__
função será usada como fallback. Não há fallback se a __repr__
implementação da função estiver ausente.__repr__
função estiver retornando a representação String do objeto, podemos pular a implementação da __str__
função.Fonte: https://www.journaldev.com/22460/python-str-repr-functions