Suche…


Bemerkungen

Ein Hinweis zum Implementieren beider Methoden

Wenn beide Methoden implementiert werden, ist es üblich, eine __str__ Methode zu verwenden, die eine menschenfreundliche Darstellung (z. B. "Ace of Spaces") und __repr__ eine eval freundliche Darstellung zurückgibt.

Tatsächlich beachten die Python-Dokumente für repr() genau das repr() :

Bei vielen Typen versucht diese Funktion, eine Zeichenfolge zurückzugeben, die ein Objekt mit demselben Wert ergibt, wenn sie an eval () übergeben wird. Andernfalls handelt es sich bei der Darstellung um eine in spitze Klammern eingeschlossene Zeichenfolge, die den Namen des Objekttyps enthält mit zusätzlichen Informationen, die häufig den Namen und die Adresse des Objekts enthalten.

Das bedeutet, dass __str__ so implementiert werden könnte, dass etwas wie "Ass der Räume" zurückgegeben wird. __repr__ könnte so implementiert werden, dass stattdessen Card('Spades', 1)

Diese Zeichenfolge könnte in einem " eval " direkt an eval :

object -> string -> object

Ein Beispiel für eine Implementierung einer solchen Methode könnte sein:

def __repr__(self):
    return "Card(%s, %d)" % (self.suit, self.pips)

Anmerkungen

[1] Diese Ausgabe ist implementierungsspezifisch. Die angezeigte Zeichenfolge stammt von cpython.

[2] Möglicherweise haben Sie das Ergebnis dieser str() / repr() str() bereits gesehen und nicht erkannt. Wenn Strings, die Sonderzeichen wie Backslashes enthalten, über str() in Strings konvertiert werden, erscheinen die Backslashes so, wie sie sind (sie erscheinen einmal). Wenn sie über repr() in Strings konvertiert werden (z. B. als Elemente einer angezeigten Liste), werden die Backslashes mit Escapezeichen versehen und erscheinen daher zweimal.

Motivation

Sie haben also gerade Ihre erste Klasse in Python erstellt, eine nette kleine Klasse, die eine Spielkarte enthält:

class Card:
    def __init__(self, suit, pips):
        self.suit = suit
        self.pips = pips

An anderer Stelle in Ihrem Code erstellen Sie einige Instanzen dieser Klasse:

ace_of_spades = Card('Spades', 1)
four_of_clubs = Card('Clubs',  4)
six_of_hearts = Card('Hearts', 6)

Sie haben sogar eine Liste von Karten erstellt, um eine "Hand" darzustellen:

my_hand = [ace_of_spades, four_of_clubs, six_of_hearts]

Beim Debuggen möchten Sie nun sehen, wie Ihre Hand aussieht, also tun Sie, was natürlich kommt und schreiben

print(my_hand)

Aber was Sie zurückbekommen, ist ein Haufen Kauderwelsch:

[<__main__.Card instance at 0x0000000002533788>, 
 <__main__.Card instance at 0x00000000025B95C8>, 
 <__main__.Card instance at 0x00000000025FF508>]

Verwirrt versuchen Sie, nur eine einzige Karte zu drucken:

print(ace_of_spades)

Und wieder erhalten Sie diese seltsame Ausgabe:

<__main__.Card instance at 0x0000000002533788>

Hab keine Angst. Wir sind dabei, das zu beheben.

Zunächst ist es jedoch wichtig zu verstehen, was hier vor sich geht. Wenn Sie schreibt print(ace_of_spades) sagten Sie Python man es will Informationen über die drucken Card Instanz Code ruft ace_of_spades . Und um fair zu sein, tat es.

Diese Ausgabe besteht aus zwei wichtigen Bits: dem type des Objekts und der id des Objekts. Allein der zweite Teil (die Hexadezimalzahl) reicht aus, um das Objekt zum Zeitpunkt des print eindeutig zu identifizieren. [1]

Was wirklich los war, war, dass Sie Python gebeten haben, die Essenz dieses Objekts "in Worte zu fassen" und es Ihnen dann anzuzeigen. Eine explizitere Version derselben Maschine könnte sein:

string_of_card = str(ace_of_spades)
print(string_of_card)

In der ersten Zeile versuchen Sie, Ihre Card in eine Zeichenfolge umzuwandeln, und in der zweiten zeigen Sie sie an.

Das Problem

Das Problem, auf das Sie stoßen, ist darauf zurückzuführen, dass Sie Python zwar alles erzählten, was Sie über die Card wissen mussten, um Karten zu erstellen , aber nicht , wie Sie Card in Zeichenketten konvertieren wollten.

Und da es nicht wusste, als Sie (implizit) str(ace_of_spades) , hat es Ihnen das gegeben, was Sie gesehen haben, eine generische Darstellung der Card Instanz.

Die Lösung (Teil 1)

Wir können Python jedoch mitteilen, wie Instanzen unserer benutzerdefinierten Klassen in Strings konvertiert werden sollen. Und so machen wir das mit der __str__ __- __str__ "dunder" (für doppelten Unterstrich) oder "magic".

Immer, wenn Sie Python __str__ , eine Zeichenfolge aus einer Klasseninstanz zu erstellen, sucht es nach einer __str__ Methode für die Klasse und ruft sie auf.

Betrachten Sie die folgende aktualisierte Version unserer Card Klasse:

class Card:
    def __init__(self, suit, pips):
        self.suit = suit
        self.pips = pips

    def __str__(self):
        special_names = {1:'Ace', 11:'Jack', 12:'Queen', 13:'King'}

        card_name = special_names.get(self.pips, str(self.pips))

        return "%s of %s" % (card_name, self.suit)

Hier haben wir nun die __str__ Methode in unserer Card Klasse definiert, die nach einem einfachen Nachschlagen des Wörterbuchs für Bildkarten eine formatierte Zeichenfolge zurückgibt , je nachdem, wie wir uns entscheiden.

(Beachten Sie, dass "Returns" hier fett gedruckt ist, um zu str(ace_of_spades) , wie wichtig es ist, einen String zurückzugeben und nicht einfach nur zu drucken. Das Drucken scheint zwar zu funktionieren, aber Sie haben die Karte dann gedruckt, wenn Sie etwas wie str(ace_of_spades) , ohne dass in Ihrem Hauptprogramm sogar eine Funktion zum Drucken aufgerufen wird. __str__ sicher, dass __str__ eine Zeichenfolge zurückgibt.

Die __str__ Methode ist eine Methode. Das erste Argument ist also self und sollte weder zusätzliche Argumente annehmen noch übergeben.

Um auf das Problem zurückzukommen, die Karte benutzerfreundlicher darzustellen, wenn wir erneut ausführen:

ace_of_spades = Card('Spades', 1)
print(ace_of_spades)

Wir werden sehen, dass unsere Ausgabe viel besser ist:

Ace of Spades

So toll, wir sind fertig, richtig?

Um unsere Grundlagen zu verdeutlichen, überprüfen wir noch einmal, ob wir das erste Problem gelöst haben und die Liste der Card , der hand , gedruckt haben.

Also überprüfen wir den folgenden Code noch einmal:

my_hand = [ace_of_spades, four_of_clubs, six_of_hearts]
print(my_hand)

Und zu unserer Überraschung bekommen wir wieder diese lustigen Hex-Codes:

[<__main__.Card instance at 0x00000000026F95C8>, 
 <__main__.Card instance at 0x000000000273F4C8>, 
 <__main__.Card instance at 0x0000000002732E08>]

Was ist los? Wir erzählten Python, wie wir wollten, dass unsere Card angezeigt werden. Warum schien sie anscheinend zu vergessen?

Die Lösung (Teil 2)

Die Maschinerie hinter den Kulissen ist etwas anders, wenn Python die Zeichenfolgendarstellung von Elementen in einer Liste erhalten möchte. Es stellt sich heraus, dass sich Python für diesen Zweck nicht für __str__ .

Stattdessen sucht es nach einer anderen Methode, __repr__ , und wenn diese nicht gefunden wird, greift sie auf das "Hexadezimal-Ding" zurück. [2]

Sie sagen also, ich muss zwei Methoden entwickeln, um dasselbe zu tun? Eine, wenn ich meine Karte alleine print möchte, und eine andere, wenn sie sich in einem Container befindet?

Nein, aber zuerst schauen wir uns an, wie unsere Klasse aussehen würde , wenn wir beide __str__ und __repr__ Methoden implementieren __repr__ :

class Card:
    special_names = {1:'Ace', 11:'Jack', 12:'Queen', 13:'King'}

    def __init__(self, suit, pips):
        self.suit = suit
        self.pips = pips

    def __str__(self):
        card_name = Card.special_names.get(self.pips, str(self.pips))
        return "%s of %s (S)" % (card_name, self.suit)

    def __repr__(self):
        card_name = Card.special_names.get(self.pips, str(self.pips))
        return "%s of %s (R)" % (card_name, self.suit)

Hier ist die Implementierung der beiden Methoden __str__ und __repr__ genau gleich, mit der Ausnahme, dass zur Unterscheidung der beiden Methoden (S) zu den von __str__ Zeichenfolgen __str__ und (R) zu den von __repr__ Zeichenfolgen __repr__ .

Beachten Sie, dass genau wie unsere __str__ Methode, __repr__ keine Argumente akzeptiert und gibt einen String zurück.

Wir können jetzt sehen, welche Methode für jeden Fall verantwortlich ist:

ace_of_spades = Card('Spades', 1)
four_of_clubs = Card('Clubs',  4)
six_of_hearts = Card('Hearts', 6)

my_hand = [ace_of_spades, four_of_clubs, six_of_hearts]

print(my_hand)           # [Ace of Spades (R), 4 of Clubs (R), 6 of Hearts (R)]

print(ace_of_spades)     # Ace of Spades (S)

Wie bedeckt war, die __str__ wurde Methode aufgerufen , wenn wir unsere übergeben Card Instanz print und die __repr__ Methode aufgerufen wurde , wenn wir eine Liste unserer Instanzen weitergegeben zu print .

An dieser Stelle sei darauf hingewiesen, dass wir, genau wie zuvor mit str() explizit einen String aus einer benutzerdefinierten Klasseninstanz erstellen können, auch explizit eine String-Darstellung unserer Klasse mit einer eingebauten Funktion namens repr() erstellen können. repr() .

Zum Beispiel:

str_card = str(four_of_clubs)
print(str_card)                     # 4 of Clubs (S)

repr_card = repr(four_of_clubs)
print(repr_card)                    # 4 of Clubs (R)

Außerdem könnten wir, wenn definiert, die Methoden direkt aufrufen (obwohl dies etwas unklar und unnötig erscheint):

print(four_of_clubs.__str__())     # 4 of Clubs (S)

print(four_of_clubs.__repr__())    # 4 of Clubs (R)

Über diese duplizierten Funktionen ...

Python-Entwickler erkannten, dass in dem Fall, dass identische Zeichenfolgen von str() und repr() sollen, Methoden möglicherweise funktional dupliziert werden müssen - etwas, das niemandem gefällt.

Stattdessen gibt es einen Mechanismus, um die Notwendigkeit dafür zu beseitigen. Einer, an dem ich dich bis hierher geschlichen habe. Es stellt sich heraus, dass, wenn eine Klasse die __repr__ Methode aber nicht die __str__ __repr__ Methode implementiert und Sie eine Instanz dieser Klasse an str() (ob implizit oder explizit), Python auf Ihre __repr__ Implementierung __repr__ und diese verwendet.

Um klar zu sein, betrachten Sie die folgende Version der Card Klasse:

class Card:
    special_names = {1:'Ace', 11:'Jack', 12:'Queen', 13:'King'}

    def __init__(self, suit, pips):
        self.suit = suit
        self.pips = pips

    def __repr__(self):
        card_name = Card.special_names.get(self.pips, str(self.pips))
        return "%s of %s" % (card_name, self.suit)

Beachten Sie, dass diese Version nur die __repr__ Methode implementiert. Aufrufe von str() führen jedoch zu der benutzerfreundlichen Version:

print(six_of_hearts)            # 6 of Hearts  (implicit conversion)
print(str(six_of_hearts))       # 6 of Hearts  (explicit conversion)

wie Aufrufe von repr() :

print([six_of_hearts])          #[6 of Hearts] (implicit conversion)
print(repr(six_of_hearts))      # 6 of Hearts  (explicit conversion)

Zusammenfassung

Damit Sie Ihre Klasseninstanzen dazu befähigen können, sich auf benutzerfreundliche Weise "zu zeigen", sollten Sie mindestens die __repr__ Methode Ihrer Klasse __repr__ . Wenn das Gedächtnis während eines Gesprächs dient, sagte Raymond Hettinger, dass die Sicherstellung, dass __repr__ für Klassen implementiert wird, eines der ersten Dinge ist, nach denen er bei Python-Code-Reviews sucht, und jetzt sollte klar sein, warum. Die Menge an Informationen, die Sie zum Debuggen von Anweisungen, Absturzberichten oder Protokolldateien mit einer einfachen Methode hinzugefügt haben könnten , ist überwältigend, wenn Sie sie mit dem bloßen vergleichen, und oft weniger nützliche Informationen (Typ, ID), die standardmäßig bereitgestellt werden.

Wenn Sie unterschiedliche Repräsentationen für beispielsweise innerhalb eines Containers wünschen, sollten Sie sowohl die Methoden __repr__ als auch __str__ implementieren. (Mehr darüber, wie Sie diese beiden Methoden auf unterschiedliche Weise verwenden können).

Beide Methoden implementiert, Eval-Round-Trip-Stil __repr __ ()

class Card:
    special_names = {1:'Ace', 11:'Jack', 12:'Queen', 13:'King'}

    def __init__(self, suit, pips):
        self.suit = suit
        self.pips = pips

    # Called when instance is converted to a string via str()
    # Examples:
    #   print(card1)
    #   print(str(card1)
    def __str__(self):
        card_name = Card.special_names.get(self.pips, str(self.pips))
        return "%s of %s" % (card_name, self.suit)

    # Called when instance is converted to a string via repr()
    # Examples:
    #   print([card1, card2, card3])
    #   print(repr(card1))
    def __repr__(self):
        return "Card(%s, %d)" % (self.suit, self.pips)


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow