Zoeken…


Opmerkingen

Een opmerking over het implementeren van beide methoden

Wanneer beide werkwijzen worden uitgevoerd, is het gebruikelijk om een enigszins hebben __str__ methode die terugkeert mens ingestelde representatie (bijvoorbeeld "Ace of Spaces") en __repr__ retourneren een eval -vriendelijk representatie.

In feite noteren de Python-documenten voor repr() precies dit:

Voor veel typen probeert deze functie een tekenreeks te retourneren die een object met dezelfde waarde oplevert wanneer deze wordt doorgegeven aan eval (), anders is de weergave een tekenreeks tussen punthaken die de naam van het type van het object samen bevat met aanvullende informatie, waaronder vaak de naam en het adres van het object.

Wat dat betekent is dat __str__ kan worden om zoiets als "Aas van spaties" terug te geven, zoals eerder getoond, __repr__ kan worden geïmplementeerd om in plaats daarvan Card('Spades', 1) terug te geven Card('Spades', 1)

Deze string kan direct worden teruggegeven in eval in enigszins van een "round-trip":

object -> string -> object

Een voorbeeld van een implementatie van een dergelijke methode kan zijn:

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

Notes

[1] Deze output is implementatiespecifiek. De weergegeven string is van cpython.

[2] Misschien heb je het resultaat van deze str() / repr() delen al gezien en niet geweten. Wanneer tekenreeksen met speciale tekens zoals backslashes worden geconverteerd naar strings via str() de backslashes weergegeven zoals ze zijn (ze verschijnen eenmaal). Wanneer ze worden geconverteerd naar tekenreeksen via repr() (bijvoorbeeld omdat elementen van een lijst worden weergegeven), worden de backslashes ontsnapt en verschijnen ze dus twee keer.

Motivatie

Dus je hebt zojuist je eerste klas in Python gemaakt, een nette kleine klas die een speelkaart omvat:

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

Elders in uw code maakt u enkele instanties van deze klasse:

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

U hebt zelfs een lijst met kaarten gemaakt om een "hand" te vertegenwoordigen:

my_hand = [ace_of_spades, four_of_clubs, six_of_hearts]

Nu, tijdens het debuggen, wilt u zien hoe uw hand eruit ziet, dus u doet wat van nature komt en schrijft:

print(my_hand)

Maar wat je terugkrijgt is een hoop gebrabbel:

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

Verward probeert u slechts één kaart af te drukken:

print(ace_of_spades)

En nogmaals, je krijgt deze rare output:

<__main__.Card instance at 0x0000000002533788>

Geen schrik hebben. We gaan dit oplossen.

Eerst is het echter belangrijk om te begrijpen wat hier aan de hand is. Wanneer u schreef print(ace_of_spades) zei je Python je het wilde om informatie over het afdrukken van Card bijvoorbeeld uw code roept ace_of_spades . En om eerlijk te zijn, deed het dat.

Die uitvoer bestaat uit twee belangrijke bits: het type object en de id van het object. Het tweede deel alleen (het hexadecimaal getal) voldoende om het object uniek te identificeren op het moment dat de print oproep. [1]

Wat er echt gebeurde, was dat je Python vroeg om de essentie van dat object te 'verwoorden' en het vervolgens aan je te tonen. Een meer expliciete versie van dezelfde machine kan zijn:

string_of_card = str(ace_of_spades)
print(string_of_card)

In de eerste regel probeer je Card kaartinstantie een string te maken en in de tweede regel toon je deze.

Het probleem

Het probleem dat u tegenkomt, komt voort uit het feit dat u, hoewel u Python alles vertelde wat het moest weten over de Card klasse om kaarten te maken , niet vertelde hoe u Card instanties wilde converteren naar strings.

En omdat het niet wist, toen je (impliciet) str(ace_of_spades) schreef, gaf het je wat je zag, een generieke weergave van de Card instantie.

De oplossing (deel 1)

Maar we kunnen Python vertellen hoe we exemplaren van onze aangepaste klassen naar tekenreeksen willen converteren. En de manier waarop we dit doen is met de __str__ "dunder" (voor dubbele underscore) of "magic" -methode.

Telkens wanneer u Python vertelt om een tekenreeks te maken van een klasse-instantie, zoekt deze naar een __str__ methode voor de klasse en roept deze aan.

Denk aan de volgende, bijgewerkte versie van onze 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 hebben we nu de __str__ methode voor onze Card klasse gedefinieerd, die, na een eenvoudige zoekopdracht in woordenboeken voor gezichtskaarten, een opgemaakte string teruggeeft, ongeacht hoe we beslissen.

(Merk op dat "retouren" hier vetgedrukt is, om het belang van het retourneren van een string te benadrukken, en niet alleen om het af te drukken. Afdrukken lijkt misschien te werken, maar dan zou je de kaart laten afdrukken als je zoiets als str(ace_of_spades) , zonder zelfs maar een afdrukfunctie-aanroep in uw hoofdprogramma te hebben. Zorg er dus voor de duidelijkheid voor dat __str__ een tekenreeks retourneert.).

De __str__ methode is een methode, dus het eerste argument is self en het moet geen aanvullende argumenten accepteren of doorgeven.

Terugkerend op ons probleem om de kaart gebruikersvriendelijker weer te geven, als we opnieuw draaien:

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

We zullen zien dat onze output veel beter is:

Ace of Spades

Zo geweldig, we zijn klaar, toch?

Nou gewoon om onze bases te dekken, laten we dubbel te controleren dat we het eerste probleem dat we ondervonden hebt opgelost, het afdrukken van de lijst met Card gevallen de hand .

Dus controleren we de volgende code opnieuw:

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

En tot onze verbazing krijgen we die grappige hex-codes weer:

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

Wat is er aan de hand? We vertelden Python hoe we wilden onze Card gevallen te geven, waarom leek het blijkbaar te vergeten?

De oplossing (deel 2)

Nou, de machines achter de schermen zijn een beetje anders wanneer Python de stringvoorstelling van items in een lijst wil krijgen. Het blijkt dat Python voor dit doel niet om __str__ .

In plaats daarvan zoekt het naar een andere methode, __repr__ , en als dat niet wordt gevonden, valt het terug op het "hexidecimale ding". [2]

Dus je zegt dat ik twee methoden moet maken om hetzelfde te doen? Een voor wanneer ik mijn kaart alleen wil print en een andere wanneer deze in een soort container zit?

Nee, maar laten we eerst eens kijken hoe onze klas eruit zou zien als we zowel __str__ als __repr__ methoden zouden implementeren:

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 is de implementatie van de twee methoden __str__ en __repr__ exact hetzelfde, behalve dat, om onderscheid te maken tussen de twee methoden, (S) wordt toegevoegd aan tekenreeksen die worden geretourneerd door __str__ en (R) wordt toegevoegd aan tekenreeksen die worden geretourneerd door __repr__ .

Merk op dat net als onze __str__ methode, __repr__ geen argumenten accepteert en een tekenreeks retourneert .

We kunnen nu zien welke methode verantwoordelijk is voor elk geval:

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)

Zoals behandeld, werd de __str__ methode aangeroepen toen we onze Card instantie passeerden om print te print en de __repr__ methode werd aangeroepen toen we een lijst met onze exemplaren om print te print passeerden.

Op dit punt is het de moeite waard erop te wijzen dat, net zoals we expliciet een string kunnen maken van een aangepaste klasse-instantie met str() zoals we eerder deden, we ook expliciet een stringrepresentatie van onze klasse kunnen maken met een ingebouwde functie genaamd repr() .

Bijvoorbeeld:

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)

En bovendien, indien gedefinieerd, zouden we de methoden direct kunnen aanroepen (hoewel het een beetje onduidelijk en onnodig lijkt):

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

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

Over die dubbele functies ...

Python-ontwikkelaars realiseerden zich dat, in het geval dat je wilde dat identieke strings werden geretourneerd uit str() en repr() je misschien functioneel methoden moet dupliceren - iets dat niemand leuk vindt.

Dus in plaats daarvan is er een mechanisme om de noodzaak daarvoor weg te nemen. Eentje waar ik je tot nu toe langs heb gesmeten. Het blijkt dat als een klasse de __repr__ methode implementeert maar niet de __str__ methode, en u een instantie van die klasse doorgeeft aan str() (impliciet of expliciet), Python terugvalt op uw __repr__ implementatie en dat gebruikt.

Overweeg dus voor de duidelijkheid de volgende versie van de 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)

Merk op dat deze versie alleen de __repr__ methode implementeert. Aanroepen van str() resulteren echter in de gebruiksvriendelijke versie:

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

net als oproepen naar repr() :

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

Samenvatting

Om uw __repr__ in staat te stellen zichzelf op gebruiksvriendelijke manieren te laten 'zien', kunt u overwegen de __repr__ methode van uw klas te __repr__ . Als geheugen dient, tijdens een gesprek zei Raymond Hettinger dat het verzekeren van klassen __repr__ implementeren een van de eerste dingen is waar hij naar op zoek is tijdens het doen van Python- __repr__ , en het zou nu duidelijk moeten zijn waarom. De hoeveelheid informatie die u met een eenvoudige methode had kunnen toevoegen aan foutopsporingsoverzichten, crashrapporten of logboekbestanden is overweldigend in vergelijking met de schamele, en vaak minder-dan-nuttige (type, id) informatie die standaard wordt gegeven.

Als u verschillende weergaven wilt voor bijvoorbeeld in een container, wilt u zowel de methoden __repr__ als __str__ implementeren. (Meer over hoe u deze twee methoden hieronder anders kunt gebruiken).

Beide methoden geïmplementeerd, eval-round-trip stijl __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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow