Jaka jest różnica między __str__
i __repr__
w Pythonie?
źródło
Alex podsumował dobrze, ale, co zaskakujące, był zbyt zwięzły.
Po pierwsze, chciałbym powtórzyć główne punkty w poście Alexa :
__repr__
celem jest być jednoznacznym__str__
celem jest być czytelnym__str__
zastosowania zawarte obiekty__repr__
Domyślna implementacja jest bezużyteczna
Jest to głównie niespodzianka, ponieważ domyślne ustawienia Pythona są zwykle dość przydatne. Jednak w tym przypadku ustawienie domyślne, dla __repr__
którego działałoby jak:
return "%s(%r)" % (self.__class__, self.__dict__)
byłby zbyt niebezpieczny (na przykład zbyt łatwy do wejścia w nieskończoną rekurencję, jeśli obiekty się nawzajem odwołują). Python więc sobie radzi. Zauważ, że istnieje jedna wartość domyślna, która jest prawdziwa: jeśli __repr__
jest zdefiniowana i __str__
nie ma, obiekt będzie zachowywał się tak, jakby __str__=__repr__
.
W skrócie oznacza to: prawie każdy zaimplementowany obiekt powinien mieć funkcję __repr__
przydatną do zrozumienia obiektu. Wdrożenie __str__
jest opcjonalne: zrób to, jeśli potrzebujesz funkcji „ładnego wydruku” (na przykład używanej przez generator raportów).
Celem __repr__
jest być jednoznacznym
Pozwól mi od razu powiedzieć i powiedzieć - nie wierzę w debuggery. Naprawdę nie wiem, jak korzystać z jakiegokolwiek debuggera i nigdy nie użyłem go poważnie. Co więcej, uważam, że wielką wadą debuggerów jest ich podstawowa natura - większość błędów, które debugowałem, miały miejsce dawno temu, w odległej galaktyce. Oznacza to, że wierzę, z zapałem religijnym, w pozyskiwanie drewna. Rejestrowanie jest siłą napędową każdego przyzwoitego systemu serwerów typu „zapomnij o pożarze i zapomnieniu”. Python ułatwia logowanie: w przypadku niektórych opakowań specyficznych dla projektu wszystko, czego potrzebujesz, to
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)
Ale musisz zrobić ostatni krok - upewnij się, że każdy zaimplementowany obiekt ma przydatne rep, więc taki kod może po prostu działać. Oto dlaczego pojawia się „ewaluacja”: jeśli masz wystarczająco dużo informacji eval(repr(c))==c
, oznacza to, że wiesz wszystko, o czym można wiedzieć c
. Jeśli to wystarczająco łatwe, przynajmniej w niewyraźny sposób, zrób to. Jeśli nie, upewnij się, że i tak masz wystarczająco dużo informacji c
. Zwykle używam eval-Like formacie: "MyClass(this=%r,that=%r)" % (self.this,self.that)
. Nie oznacza to, że możesz zbudować MyClass, ani że są to odpowiednie argumenty konstruktora - ale jest to przydatna forma wyrażenia „to wszystko, co musisz wiedzieć o tym wystąpieniu”.
Uwaga: użyłem %r
powyżej, nie %s
. Zawsze chcesz użyć repr()
[lub %r
formatowania znaku, równoważnie] wewnątrz __repr__
implementacji, albo pokonujesz cel repr. Chcesz być w stanie różnicować MyClass(3)
i MyClass("3")
.
Celem __str__
jest bycie czytelnym
W szczególności nie jest to jednoznaczne - zauważ to str(3)==str("3")
. Podobnie, jeśli zaimplementujesz abstrakcję IP, posiadanie jej łańcucha wygląda tak, jakby 192.168.1.1 było w porządku. Podczas implementowania abstrakcji daty / godziny ciąg może mieć postać „2010/4/12 15:35:22” itp. Celem jest przedstawienie go w taki sposób, aby użytkownik, a nie programista, chciał go przeczytać. Odcinaj bezużyteczne cyfry, udawaj, że to inna klasa - o ile obsługuje czytelność, jest to poprawa.
Kontenera __str__
zastosowania zawarte obiekty__repr__
To wydaje się zaskakujące, prawda? To trochę, ale jak by to było czytelne, gdyby użył ich __str__
?
[moshe is, 3, hello
world, this is a list, oh I don't know, containing just 4 elements]
Nie bardzo. W szczególności łańcuchy w kontenerze zbyt łatwo zakłócałyby jego reprezentację łańcucha. Pamiętaj, że w obliczu niejasności Python opiera się pokusie zgadywania. Jeśli chcesz powyższe zachowanie podczas drukowania listy, po prostu
print "[" + ", ".join(l) + "]"
(prawdopodobnie możesz także dowiedzieć się, co zrobić z słownikami.
Podsumowanie
Implementuj __repr__
dla dowolnej implementowanej klasy. To powinna być druga natura. Zaimplementuj, __str__
jeśli uważasz, że przydałaby się wersja łańcuchowa, która popsuła się po stronie czytelności.
__repr__
że potrzebowałem do debugowania. Dziękuję za pomocMoja ogólna zasada:
__repr__
jest dla programistów,__str__
jest dla klientów.źródło
__str__
więc normalni programiści mają czytelny obiekt. Z drugiej strony__repr__
jest dla samych programistów SDK.O ile nie zdecydujesz się inaczej, większość klas nie przyniesie pomocnych rezultatów dla:
Jak widzisz - bez różnicy i bez informacji poza klasą i przedmiotem
id
. Jeśli zastąpisz tylko jeden z dwóch ...:jak widzisz, jeśli przesłonisz
__repr__
, to TAKŻE zostanie użyte__str__
, ale nie odwrotnie.Inne ważne ciekawostki, które warto wiedzieć:
__str__
na wbudowanym pojemniku używa__repr__
, a NIE__str__
, przedmiotów, które zawiera. I pomimo słów na ten temat znalezionych w typowych dokumentach, prawie nikt nie zadaje sobie trudu, aby__repr__
obiekty z były ciągiem, któryeval
może posłużyć do zbudowania równego obiektu (jest to po prostu zbyt trudne, ORAZ nie wiedząc, w jaki sposób dany moduł został zaimportowany, sprawia, że tak naprawdę całkowicie niemożliwe).Moja rada: skup się na tym, aby
__str__
czytelnie czytelny dla ludzi i__repr__
tak jednoznaczny, jak to tylko możliwe, nawet jeśli koliduje to z niewyraźnym, nieosiągalnym celem polegającym na tym__repr__
, aby zwracana wartość była akceptowalna jako wkład__eval__
!źródło
eval(repr(foo))
wartość jest równa obiektowifoo
. Masz rację, że nie będzie działać na zewnątrz z moich testów, ponieważ nie wiem jak moduł jest importowany, ale ten przynajmniej zapewnia, że pracuje w jakimś przewidywalnym kontekście. Myślę, że to dobry sposób oceny, czy wynik__repr__
jest wystarczająco wyraźny. Wykonanie tego w teście jednostkowym pomaga również upewnić się, że__repr__
następuje zmiana w klasie.eval(repr(spam)) == spam
(przynajmniej w odpowiednim kontekście), alboeval(repr(spam))
podnosiSyntaxError
. W ten sposób unikniesz zamieszania. (I to jest prawie prawdziwe dla wbudowanych i większości stdlib, z wyjątkiem np. List rekurencyjnych, gdziea=[]; a.append(a); print(eval(repr(a)))
daje ci[[Ellipses]]
…) Oczywiście, że nie robię tego, aby użyćeval(repr(spam))
, z wyjątkiem sprawdzania poczytalności w testach jednostkowych… ale ja czy czasami skopiować i wkleićrepr(spam)
w sesji interaktywnej.__str__
dla każdego elementu zamiast__repr__
? Wydaje mi się to zupełnie błędne, ponieważ zaimplementowałem obiekt czytelny__str__
w moim obiekcie, a gdy jest on częścią listy, widzę brzydszy__repr__
.eval(repr(x))
zawodzi nawet dla typów wbudowanych:class A(str, Enum): X = 'x'
podniesie SyntaxError naeval(repr(A.X))
. To smutne, ale zrozumiałe. BTW,eval(str(A.X))
faktycznie działa, ale oczywiście tylko wtedy, gdyclass A
jest objęty zakresem - więc prawdopodobnie nie jest bardzo przydatny.str
element użycia kontenera,repr
ponieważ[1, 2, 3]
! =["1", "2, 3"]
.__repr__
: reprezentacja obiektu python zwykle eval przekonwertuje go z powrotem na ten obiekt__str__
: to, co myślisz, to ten obiekt w formie tekstowejna przykład
źródło
Oto dobry przykład:
Przeczytaj tę dokumentację dla repr:
Oto dokumentacja dla str:
źródło
__str__
(czytane jako „ciąg dunder (podwójny podkreślnik)”) i__repr__
(czytane jako „dunder-repper” (dla „reprezentacji”)) są specjalnymi metodami zwracającymi ciągi znaków na podstawie stanu obiektu.__repr__
zapewnia zachowanie kopii zapasowej, jeśli__str__
brakuje.Dlatego najpierw należy napisać taki,
__repr__
który pozwala przywrócić równoważny obiekt z ciągu, który zwraca, np. Używająceval
lub wpisując go znak po znaku w powłoce Pythona.W dowolnym momencie można napisać
__str__
reprezentację instancji w postaci czytelnej dla użytkownika, jeśli uważa się to za konieczne.__str__
W przypadku drukowania obiektu, lub przekazać go do
format
,str.format
lubstr
, następnie, jeśli__str__
metoda jest zdefiniowana, że metoda będzie wywoływana, w przeciwnym razie__repr__
zostaną wykorzystane.__repr__
__repr__
Metoda jest wywoływana przez funkcję wbudowanegorepr
i to, co jest echem od powłoki Pythona gdy ocenia wyrażenie zwraca obiekt.Ponieważ stanowi kopię zapasową
__str__
, jeśli możesz tylko napisać jedną, zacznij od__repr__
Oto wbudowana pomoc dotycząca
repr
:To znaczy, dla większości obiektów, jeśli wpiszesz to, co jest drukowane
repr
, powinieneś być w stanie stworzyć równoważny obiekt. Ale to nie jest domyślna implementacja.Domyślna implementacja
__repr__
Domyślnym obiektem
__repr__
jest ( C Python source ) coś takiego:Oznacza to, że domyślnie wydrukujesz moduł, z którego pochodzi obiekt, nazwę klasy i szesnastkową reprezentację jego położenia w pamięci - na przykład:
Informacje te nie są zbyt przydatne, ale nie ma sposobu, aby wywnioskować, w jaki sposób można dokładnie stworzyć kanoniczną reprezentację dowolnej instancji, i jest to lepsze niż nic, przynajmniej mówi nam, jak możemy jednoznacznie zidentyfikować je w pamięci.
Jak może
__repr__
być przydatny?Zobaczmy, jak może być użyteczne, używając powłoki i
datetime
obiektów Pythona . Najpierw musimy zaimportowaćdatetime
moduł:Jeśli wywołamy
datetime.now
powłokę, zobaczymy wszystko, czego potrzebujemy do odtworzenia równoważnego obiektu datetime. Jest to tworzone przez datetime__repr__
:Jeśli wydrukujemy obiekt datetime, zobaczymy ładny format czytelny dla człowieka (w rzeczywistości ISO). Jest to realizowane przez datetime
__str__
:Odtworzenie utraconego obiektu jest proste, ponieważ nie przypisaliśmy go do zmiennej przez skopiowanie i wklejenie z
__repr__
wyjścia, a następnie wydrukowanie, i otrzymujemy to samo wyjście czytelne dla człowieka, co drugi obiekt:Jak je wdrożyć?
W miarę rozwoju będziesz chciał, jeśli to możliwe, być w stanie odtwarzać obiekty w tym samym stanie. Tak na przykład definiuje się obiekt datetime
__repr__
( źródło Python ). Jest dość złożony, ponieważ ma wszystkie atrybuty potrzebne do odtworzenia takiego obiektu:Jeśli chcesz, aby Twój obiekt miał bardziej czytelną dla człowieka reprezentację, możesz zaimplementować
__str__
następny. Oto jak implementuje obiekt datetime ( źródło Python )__str__
, co łatwo robi, ponieważ ma już funkcję wyświetlania go w formacie ISO:Set
__repr__ = __str__
?To jest krytyka innej odpowiedzi, która sugeruje ustawienie
__repr__ = __str__
.Ustawienie
__repr__ = __str__
jest głupie -__repr__
jest awarią,__str__
a__repr__
napisane dla deweloperów podczas debugowania powinno być napisane przed napisaniem__str__
.Potrzebujesz
__str__
tylko wtedy, gdy potrzebujesz tekstowej reprezentacji obiektu.Wniosek
Zdefiniuj
__repr__
obiekty, które piszesz, abyś ty i inni programiści mieli powtarzalny przykład, kiedy używasz go podczas tworzenia. Zdefiniuj,__str__
kiedy potrzebujesz czytelnej dla człowieka reprezentacji ciągu.źródło
type(obj).__qualname__
?Na stronie 358 książki „ Skrypty Pythona dla obliczeniowej nauki” Hansa Pettera Langtangena wyraźnie stwierdza się, że
__repr__
cele w kompletnej reprezentacji ciąg obiektu;__str__
Jest powrót piękny ciąg do drukowania.Wolę więc rozumieć je jako
z punktu widzenia użytkownika, chociaż jest to nieporozumienie, które popełniłem podczas nauki Pythona.
Mały, ale dobry przykład podano również na tej samej stronie w następujący sposób:
Przykład
źródło
repr
reprodukcją. Lepiej jest traktować to jako reprezentację.Oprócz wszystkich udzielonych odpowiedzi chciałbym dodać kilka punktów: -
1)
__repr__()
jest wywoływany, gdy po prostu napiszesz nazwę obiektu na interaktywnej konsoli python i naciśniesz enter.2)
__str__()
jest wywoływany, gdy używasz obiektu z instrukcją print.3) W przypadku, gdy go
__str__
brakuje, należy wydrukować i dowolną funkcję przy użyciustr()
wywołań__repr__()
obiektu.4)
__str__()
kontenerów, gdy zostanie wywołany, wykona__repr__()
metodę zawartych w nim elementów.5)
str()
wywołanie wewnątrz__str__()
może potencjalnie powtórzyć się bez przypadku podstawowego i błąd przy maksymalnej głębokości rekurencji.6)
__repr__()
może wywoływać,repr()
które spróbuje automatycznie uniknąć nieskończonej rekurencji, zastępując już reprezentowany obiekt na...
.źródło
Krótko mówiąc:
__str__
służy do pokazania reprezentacji ciągu obiektu, którą mogą odczytać inni.__repr__
służy do wyświetlania znaków reprezentujący w obiekcie.Powiedzmy, że chcę utworzyć
Fraction
klasę, w której ciąg reprezentujący ułamek to „(1/2)”, a obiekt (klasa ułamkowa) ma być reprezentowany jako „ułamek (1,2)”Możemy więc stworzyć prostą klasę frakcji:
źródło
Szczerze mówiąc,
eval(repr(obj))
nigdy nie jest używany. Jeśli okaże się, że go używasz, powinieneś przestać, ponieważeval
jest to niebezpieczne, a łańcuchy są bardzo nieefektywnym sposobem serializacji obiektów (użyjpickle
zamiast tego).Dlatego polecam ustawienie
__repr__ = __str__
. Powodem jest to, żestr(list)
wywołujerepr
elementy (uważam to za jedną z największych wad projektowych Pythona, która nie została rozwiązana przez Python 3). Rzeczywistarepr
prawdopodobnie nie będzie bardzo pomocna jako wynikprint [your, objects]
.Aby to zakwalifikować, z mojego doświadczenia, najbardziej użytecznym przypadkiem użycia
repr
funkcji jest umieszczenie łańcucha wewnątrz innego łańcucha (przy użyciu formatowania łańcucha). W ten sposób nie musisz się martwić o unikanie cudzysłowów lub czegokolwiek. Pamiętaj jednak, że tutaj się nieeval
dzieje.źródło
eval(repr(obj))
jest testem rozsądku i praktyczną zasadą - jeśli to poprawnie odtwarza oryginalny obiekt, masz przyzwoitą__repr__
implementację. Nie jest tak, że serializujesz obiekty w ten sposób.eval
nie jest z natury niebezpieczny. Nie jest bardziej niebezpieczne niżunlink
,open
lub pisząc na plikach. Czy powinniśmy przestać zapisywać w plikach, ponieważ być może złośliwy atak mógłby użyć dowolnej ścieżki do pliku, aby wprowadzić do niego zawartość? Wszystko jest niebezpieczne, jeśli głupio z niego korzystają. Idiotyzm jest niebezpieczny. Efekty Dunninga-Krugera są niebezpieczne.eval
to tylko funkcja.Z (Nieoficjalnej) Wiki Python Reference (kopia archiwalna) autorstwa effbot:
__str__
„ oblicza„ nieformalną ”reprezentację ciągu obiektu. Różni się__repr__
to tym, że nie musi to być prawidłowe wyrażenie w języku Python: zamiast tego można użyć wygodniejszej lub zwięzłej reprezentacji. ”źródło
__repr__
nie jest w żaden sposób wymagane do zwrócenia wyrażenia vaild Python.str
- Tworzy nowy obiekt ciągu z podanego obiektu.repr
- Zwraca kanoniczną reprezentację ciągu znaków obiektu.Różnice:
str ():
repr ():
źródło
Jeden aspekt, którego brakuje w innych odpowiedziach. Prawdą jest, że ogólnie wzór jest następujący:
__str__
: czytelny dla człowieka__repr__
: jednoznaczny, możliwy do odczytu maszynowego przezeval
Niestety, to zróżnicowanie jest wadliwe, ponieważ Python REPL, a także IPython używają
__repr__
do drukowania obiektów w konsoli REPL (zobacz powiązane pytania dotyczące Python i IPython ). Dlatego projekty ukierunkowane na interaktywną pracę konsoli (np. Numpy lub Pandy) zaczęły ignorować powyższe reguły i__repr__
zamiast tego zapewnić implementację czytelną dla człowieka .źródło
Z książki Fluent Python :
źródło
Doskonałe odpowiedzi już obejmują różnicę między
__str__
i__repr__
, co dla mnie sprowadza się do tego, że ten pierwszy jest czytelny nawet dla użytkownika końcowego, a ten drugi jest jak najbardziej użyteczny dla programistów. Biorąc to pod uwagę, uważam, że domyślna implementacja__repr__
często nie osiąga tego celu, ponieważ pomija informacje przydatne dla programistów.Z tego powodu, jeśli mam dość proste
__str__
, zazwyczaj staram się uzyskać to, co najlepsze z obu światów za pomocą czegoś takiego:źródło
Python opowiada się za jednoznacznością nad czytelnością ,
__str__
wywoływaniemtuple
wywoływanych obiektów__repr__
, „formalną” reprezentacją obiektu. Chociaż formalna reprezentacja jest trudniejsza do odczytania niż nieformalna, jest jednoznaczna i bardziej odporna na błędy.źródło
__repr__
gdy nie__str__
jest zdefiniowane ( )! Więc się mylisz.W skrócie:
źródło
Po
print()
wywołaniudecimal.Decimal(23) / decimal.Decimal("1.05")
drukowany jest wynik surowego numeru; ten wynik jest w postaci ciągu, który można osiągnąć za pomocą__str__()
. Jeśli po prostu wprowadzimy wyrażenie, otrzymamydecimal.Decimal
wynik - wynik ten ma postać reprezentacyjną, którą można osiągnąć za pomocą__repr__()
. Wszystkie obiekty Python mają dwie formy wyjściowe. Forma łańcucha jest zaprojektowana tak, aby była czytelna dla człowieka. Forma reprezentacyjna jest zaprojektowana do generowania danych wyjściowych, które po dostarczeniu do interpretera Pythona odtworzyłyby (o ile to możliwe) reprezentowany obiekt.źródło
__str__
może być wywołany na obiekcie przez wywołaniestr(obj)
i powinien zwrócić ciąg czytelny dla człowieka.__repr__
może być wywołany na obiekcie przez wywołanierepr(obj)
i powinien zwrócić obiekt wewnętrzny (pola / atrybuty obiektu)Ten przykład może pomóc:
źródło
Zrozumieć
__str__
i__repr__
intuicyjnie i trwale odróżnić je w ogóle.__str__
zwraca ciąg ukrytego ciała danego obiektu do odczytu dla oczu__repr__
zwraca rzeczywiste ciało danego obiektu (sam powrót) dla jednoznaczności w identyfikacji.Zobacz to w przykładzie
Co się tyczy
__repr__
Możemy
__repr__
wygodnie wykonywać operacje arytmetyczne na wynikach.jeśli zastosujesz operację na
__str__
Zwraca tylko błąd.
Inny przykład.
Mam nadzieję, że pomoże to w budowaniu konkretnych podstaw do odkrywania kolejnych odpowiedzi.
źródło
__str__
musi zwrócić obiekt ciągu, ale__repr__
może zwrócić dowolne wyrażenie python.__str__
brakuje implementacji,__repr__
funkcja jest używana jako rezerwowa. Nie ma rezerwy, jeśli__repr__
brakuje implementacji funkcji.__repr__
funkcja zwraca ciąg znaków reprezentujący obiekt, możemy pominąć implementację__str__
funkcji.Źródło: https://www.journaldev.com/22460/python-str-repr-functions
źródło
__repr__
jest używany wszędzie, z wyjątkiem metodprint
istr
(gdy__str__
zdefiniowano a!)źródło