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.