3676 Stimmen

Was ist der Unterschied zwischen __str__ und __repr__?

Was ist der Unterschied zwischen __str__ y __repr__ in Python?

3467voto

moshez Punkte 33946

Alex gut zusammengefasst, war aber überraschenderweise zu knapp gehalten.

Lassen Sie mich zunächst noch einmal die wichtigsten Punkte aus Alex' Beitrag :

  • Die Standardimplementierung ist nutzlos (es ist schwer vorstellbar, dass dies nicht der Fall sein könnte, aber ja)
  • __repr__ Ziel ist es, unmissverständlich zu sein
  • __str__ Ziel ist es, lesbar zu sein
  • Die Container __str__ verwendet enthaltene Objekte". __repr__

Standardimplementierung ist nutzlos

Dies ist vor allem deshalb eine Überraschung, weil die Standardeinstellungen von Python in der Regel recht nützlich sind. In diesem Fall jedoch ist eine Voreinstellung für __repr__ was sich wie folgt verhalten würde:

return "%s(%r)" % (self.__class__, self.__dict__)

wäre zu gefährlich gewesen (z. B. kann man zu leicht in eine unendliche Rekursion geraten, wenn Objekte aufeinander verweisen). Also hält sich Python zurück. Beachten Sie, dass es eine Voreinstellung gibt, die wahr ist: wenn __repr__ definiert ist, und __str__ nicht ist, verhält sich das Objekt so, als ob __str__=__repr__ .

Das bedeutet einfach ausgedrückt: Fast jedes Objekt, das Sie implementieren, sollte eine funktionale __repr__ die für das Verständnis des Objekts verwendet werden kann. Implementierung von __str__ ist optional: tun Sie dies, wenn Sie eine "hübsche Druckfunktion" benötigen (z. B. von einem Berichtsgenerator verwendet).

Das Ziel der __repr__ ist eindeutig zu sein

Lassen Sie es mich ganz offen sagen - ich glaube nicht an Debugger. Ich weiß nicht wirklich, wie man einen Debugger benutzt, und habe noch nie einen ernsthaft benutzt. Außerdem glaube ich, dass der große Fehler in Debuggern ihre grundlegende Natur ist - die meisten Fehler, die ich debugge, sind vor langer, langer Zeit in einer weit entfernten Galaxie passiert. Das bedeutet, dass ich mit religiöser Inbrunst an die Protokollierung glaube. Logging ist das Lebenselixier eines jeden anständigen Fire-and-Forget-Serversystems. Python macht es einfach zu protokollieren: mit einigen projektspezifischen Wrappern braucht man nur ein

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)

Aber Sie müssen den letzten Schritt tun - stellen Sie sicher, dass jedes Objekt, das Sie implementieren, eine nützliche Repräsentation hat, damit Code wie dieser einfach funktionieren kann. Das ist der Grund, warum die "eval"-Sache auftaucht: wenn Sie genug Informationen haben, um eval(repr(c))==c Das bedeutet, dass Sie alles wissen, was es über die folgenden Themen zu wissen gibt c . Wenn das einfach genug ist, zumindest auf eine unscharfe Art und Weise, dann tun Sie es. Wenn nicht, stellen Sie sicher, dass Sie genügend Informationen über c sowieso. Ich verwende normalerweise ein eval-ähnliches Format: "MyClass(this=%r,that=%r)" % (self.this,self.that) . Es bedeutet nicht, dass Sie MyClass tatsächlich konstruieren können oder dass dies die richtigen Konstruktorargumente sind - aber es ist eine nützliche Form, um auszudrücken "das ist alles, was Sie über diese Instanz wissen müssen".

Anmerkung: Ich habe %r oben, nicht %s . Sie sollten immer Folgendes verwenden repr() [oder %r Formatierungszeichen, gleichwertig] innerhalb __repr__ Umsetzung, oder Sie verfehlen das Ziel der Repr. Sie wollen in der Lage sein, zu differenzieren MyClass(3) y MyClass("3") .

Das Ziel der __str__ soll lesbar sein

Insbesondere soll sie nicht eindeutig sein - beachten Sie, dass str(3)==str("3") . Wenn Sie eine IP-Abstraktion implementieren, ist es ebenfalls in Ordnung, wenn der Str-Wert wie 192.168.1.1 aussieht. Bei der Implementierung einer Datum/Uhrzeit-Abstraktion kann der str "2010/4/12 15:35:22" usw. lauten. Das Ziel ist es, sie so darzustellen, dass ein Benutzer, nicht ein Programmierer, sie lesen möchte. Schneiden Sie nutzlose Ziffern ab, geben Sie vor, eine andere Klasse zu sein - solange es die Lesbarkeit unterstützt, ist es eine Verbesserung.

Die Container __str__ verwendet enthaltene Objekte". __repr__

Das scheint überraschend, nicht wahr? Es ist ein wenig, aber wie lesbar wäre es, wenn es ihre __str__ ?

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

Nicht sehr. Insbesondere wäre es für die Strings in einem Container viel zu einfach, seine String-Darstellung zu stören. Denken Sie daran, dass Python angesichts von Mehrdeutigkeit der Versuchung widersteht, zu raten. Wenn Sie das oben beschriebene Verhalten beim Drucken einer Liste wünschen, brauchen Sie nur

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

(Sie können wahrscheinlich auch herausfinden, was mit Wörterbüchern zu tun ist.

Zusammenfassung

Umsetzung __repr__ für jede Klasse, die Sie implementieren. Das sollte selbstverständlich sein. Implementieren Sie __str__ wenn Sie glauben, dass es nützlich wäre, eine Zeichenkettenversion zu haben, die sich auf der Seite der Lesbarkeit befindet.

366 Stimmen

Ich stimme definitiv nicht mit Ihrer Meinung überein, dass die Fehlersuche nicht der richtige Weg ist. Für die Entwicklung verwenden Sie einen Debugger (und/oder Logging), für die Produktion verwenden Sie Logging. Mit einem Debugger haben Sie einen Überblick über alles, was schief gelaufen ist, als das Problem auftrat. Sie können das gesamte Bild sehen. Wenn Sie nicht ALLES protokollieren, ist das nicht möglich. Und wenn Sie alles protokollieren, müssen Sie sich durch Unmengen von Daten wühlen, um das zu bekommen, was Sie wollen.

42 Stimmen

Tolle Antwort (außer dem Teil über die Nichtverwendung von Debuggern). Ich möchte nur einen Link zu diesem Thema hinzufügen andere Q&A über str gegen Unicode in Python 3 die für diejenigen, die den Wechsel vollzogen haben, für die Diskussion relevant sein könnten.

0 Stimmen

@moshez erwähnenswert, dass Container (Listen, Dicts) immer die __repr__ und nicht die __str__ . Die print(str([today, today])) druckt [datetime.datetime(2019, 1, 8, 20, 5, 27, 24162), datetime.datetime(2019, 1, 8, 20, 5, 27, 24162)] .

795voto

Ned Batchelder Punkte 342778

Meine Faustregel: __repr__ ist für Entwickler, __str__ ist für Kunden.

25 Stimmen

Dies ist wahr, denn für obj = uuid.uuid1() ist obj.__str__() "2d7fc7f0-7706-11e9-94ae-0242ac110002" und obj.__repr__() ist "UUID('2d7fc7f0-7706-11e9-94ae-0242ac110002')". Entwickler brauchen (Wert + Ursprung), während Kunden einen Wert brauchen, und es ist ihnen egal, wie sie ihn bekommen haben!

14 Stimmen

Hier Kunde bedeutet nicht unbedingt Endverbraucher. Es ist der Kunde oder Benutzer des Objekts. Wenn es sich also um ein SDK handelt, verwenden die SDK-Entwickler __str__ damit normale Entwickler ein lesbares Objekt haben. Auf der anderen Seite, __repr__ ist für die SDK-Entwickler selbst bestimmt.

0 Stimmen

@NarenYellavula wenn Sie eine UUID für einen Kunden freigeben, machen Sie wahrscheinlich etwas falsch.

531voto

Alex Martelli Punkte 805329

Wenn Sie nicht ausdrücklich etwas dagegen unternehmen, haben die meisten Klassen auch keine hilfreichen Ergebnisse:

>>> class Sic(object): pass
... 
>>> print(str(Sic()))
<__main__.Sic object at 0x8b7d0>
>>> print(repr(Sic()))
<__main__.Sic object at 0x8b7d0>
>>> 

Wie Sie sehen, gibt es keinen Unterschied und keine weiteren Informationen über die Klasse und das Objekt id . Wenn Sie nur eine der beiden Möglichkeiten außer Kraft setzen...:

>>> class Sic(object): 
...   def __repr__(self): return 'foo'
... 
>>> print(str(Sic()))
foo
>>> print(repr(Sic()))
foo
>>> class Sic(object):
...   def __str__(self): return 'foo'
... 
>>> print(str(Sic()))
foo
>>> print(repr(Sic()))
<__main__.Sic object at 0x2617f0>
>>> 

wie Sie sehen, wenn Sie die __repr__ die AUCH verwendet wird für __str__ , aber nicht umgekehrt.

Weitere wichtige Informationen: __str__ auf einem aufgebauten Container verwendet die __repr__ , NICHT die __str__ für die darin enthaltenen Artikel. Und trotz der vielen Worte zu diesem Thema, die in den typischen Dokumenten zu finden sind, macht sich kaum jemand die Mühe, die __repr__ der Objekte eine Zeichenkette sein, die eval verwenden kann, um ein gleiches Objekt zu erstellen (es ist einfach zu schwierig, und wenn man nicht weiß, wie das betreffende Modul tatsächlich importiert wurde, ist es schlichtweg unmöglich).

Mein Rat: Konzentrieren Sie sich auf die Herstellung von __str__ einigermaßen von Menschen lesbar sein und __repr__ so eindeutig wie möglich sein, auch wenn das mit dem unscharfen, unerreichbaren Ziel kollidiert, die __repr__ Der zurückgegebene Wert kann als Eingabe für __eval__ !

53 Stimmen

In meinen Unit-Tests prüfe ich immer, dass eval(repr(foo)) wertet ein Objekt aus, das gleich ist mit foo . Sie haben Recht, dass es außerhalb meiner Testfälle nicht funktionieren wird, da ich nicht weiß, wie das Modul importiert wird, aber dies stellt zumindest sicher, dass es in einige vorhersehbaren Kontext. Ich denke, dies ist ein guter Weg, um zu bewerten, ob das Ergebnis von __repr__ ist eindeutig genug. Wenn Sie dies in einem Einheitstest tun, können Sie auch sicherstellen, dass __repr__ folgt Änderungen in der Klasse.

7 Stimmen

Ich versuche immer, sicherzustellen, dass entweder eval(repr(spam)) == spam (zumindest im richtigen Kontext), oder eval(repr(spam)) erhebt eine SyntaxError . Auf diese Weise vermeiden Sie Verwechslungen. (Und das ist fast true für die builtins und den größten Teil der stdlib, außer z. B. für rekursive Listen, bei denen a=[]; a.append(a); print(eval(repr(a))) gibt Ihnen [[Ellipses]] ) Natürlich tue ich das nicht, um tatsächlich verwenden. eval(repr(spam)) außer als Sicherheitsprüfung in Unit-Tests aber ich tun manchmal kopieren und einfügen repr(spam) zu einer interaktiven Sitzung.

1 Stimmen

Warum sollten Container (Listen, Tupel) nicht __str__ für jedes Element anstelle von __repr__ ? Scheint mir schlichtweg falsch zu sein, denn ich habe eine lesbare __str__ in meinem Objekt und wenn es Teil einer Liste ist, sehe ich die hässlichere __repr__ stattdessen.

216voto

bitoffdev Punkte 2894

Kurz gesagt, das Ziel von __repr__ soll eindeutig sein und __str__ ist zu sein lesbar sein.

Hier ist ein gutes Beispiel:

>>> 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)'

Lesen Sie diese Dokumentation für weitere Informationen:

repr(object)

Gibt eine Zeichenkette zurück, die eine druckbare Darstellung eines Objekts enthält. Dies ist derselbe Wert, der durch Konvertierungen (umgekehrte Anführungszeichen). Manchmal ist es nützlich, auf diese Operation als eine gewöhnliche Funktion. Bei vielen Typen versucht diese Funktion eine Zeichenkette zurückzugeben, die ein Objekt mit dem gleichen Wert ergibt, wenn sie übergeben an eval() , andernfalls ist die Darstellung eine Zeichenkette, die in spitzen Klammern, die den Namen des Objekttyps enthält zusammen mit zusätzlichen Informationen, die oft den Namen und die Adresse des Objekts. Eine Klasse kann kontrollieren, was diese Funktion zurückgibt für ihre Instanzen durch die Definition einer __repr__() Methode.

Hier ist die Dokumentation für str:

str(object='')

Gibt eine Zeichenkette zurück, die eine schön druckbare Darstellung eines Objekts enthält. Bei Zeichenketten wird die Zeichenkette selbst zurück. Der Unterschied zu repr(object) ist das str(object) nicht versucht nicht immer, eine Zeichenkette zurückzugeben, die für eval() ; sein Ziel ist es, eine druckbare Zeichenkette zurückzugeben. Wenn kein Argument angegeben wird, wird die leere Zeichenkette zurück, '' .

2 Stimmen

Welche Bedeutung hat hier die druckbare Zeichenfolge? Können Sie das bitte erklären?

2 Stimmen

Aufbauend auf dem obigen Beispiel von "bitoffdev" und @deadly können wir sehen, wie str ist für den Endbenutzer, weil es uns nur eine lesbare Zeichenkette liefert, während die repr. ist für Entwickler, weil es uns sowohl den Wert als auch den Typ gibt. Wenn Sie auf der Suche nach Antworten für ein Vorstellungsgespräch sind, dann wäre es perfekt.

215voto

__repr__ : Repräsentation eines Python-Objekts, das normalerweise von eval in dieses Objekt zurückgewandelt wird

__str__ : ist das, was Sie für das Objekt halten, in Textform

z.B..

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

3 Stimmen

__repr__() : wird verwendet, um einen "konstruktorähnlichen Ausdruck" in einer Zeichenkette zu erzeugen, so dass eval() ein Objekt aus dieser Zeichenkettenrepräsentation rekonstruieren kann __str__() : wird verwendet, um eine Zeichenkette zu erzeugen, die eine druckbare Repräsentation eines Objekts enthält

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X