web-dev-qa-db-fra.com

Confus à propos de __str__ sur la liste dans Python

Venant d’un arrière-plan Java, je comprends que __str__ ressemble à une version Python de toString (alors que je réalise que Python est l’ancien la langue).

J'ai donc défini une petite classe avec une méthode __str__ comme suit:

class Node:

    def __init__(self, id):
        self.id = id
        self.neighbours = []
        self.distance = 0


    def __str__(self):
        return str(self.id)

J'en crée ensuite quelques instances:

uno = Node(1)    
due = Node(2)    
tri = Node(3)    
qua = Node(4)

À présent, le comportement attendu lors de la tentative d'impression de l'un de ces objets est que sa valeur associée est imprimée. Cela arrive aussi.

print uno

les rendements

1

Mais quand je fais ce qui suit:

uno.neighbours.append([[due, 4], [tri, 5]])

puis

print uno.neighbours

Je reçois

[[[<__main__.Node instance at 0x00000000023A6C48>, 4], [<__main__.Node instance at 0x00000000023A6D08>, 5]]]

Où je m'attendais

[[2, 4], [3, 5]]

Qu'est-ce que je rate? Et que fais-je autrement? :)

112
Christofer Ohlsson

Python a deux manières différentes de convertir un objet en chaîne: str() et repr(). L'impression d'un objet utilise str(); l'impression d'une liste contenant un objet utilise str() pour la liste elle-même, mais l'implémentation de list.__str__() appelle repr() pour les éléments individuels.

Donc, vous devriez aussi écraser __repr__(). Un simple

__repr__ = __str__

à la fin de la classe, le corps fera l'affaire.

138
Sven Marnach

En raison de la supériorité infinie de Python sur Java, Python n'a pas n, mais deux opérations de chaîne.

L'un est __str__, l'autre est __repr__

__str__ renverra une chaîne lisible par l'homme. __repr__ retournera une représentation interne.

__repr__ peut être appelé sur un objet en appelant repr(obj) ou en utilisant des backticks `obj`.

Lors de l'impression de listes ainsi que d'autres classes de conteneur, les éléments contenus seront imprimés avec __repr__.

37
Hans Then

Il fournit une version de sortie lisible par l'homme plutôt que "Object": Exemple:

class Pet(object):

    def __init__(self, name, species):
        self.name = name
        self.species = species

    def getName(self):
        return self.name

    def getSpecies(self):
        return self.species

    def Norm(self):
        return "%s is a %s" % (self.name, self.species)

if __name__=='__main__':
    a = Pet("jax", "human")
    print a 

résultats

<__main__.Pet object at 0x029E2F90>

tandis que le code avec "str" renvoie quelque chose de différent

class Pet(object):

    def __init__(self, name, species):
        self.name = name
        self.species = species

    def getName(self):
        return self.name

    def getSpecies(self):
        return self.species

    def __str__(self):
        return "%s is a %s" % (self.name, self.species)

if __name__=='__main__':
    a = Pet("jax", "human")
    print a 

résultats:

jax is a human
24
jax

Eh bien, les méthodes __str__ des objets conteneur utiliseront repr sur leur contenu, et non str. Vous pouvez donc utiliser __repr__ au lieu de __str__, puisque vous utilisez un ID comme résultat.

9
user1675187

Réponse à la question

Comme indiqué dans un autre réponse et comme vous pouvez le lire PEP 314 , str sur un list appelle pour chaque élément __repr__ . Vous ne pouvez pas faire grand chose à propos de cette partie.

Si vous implémentez __repr__, vous obtiendrez quelque chose de plus descriptif, mais s'il est implémenté correctement, il ne correspond pas exactement à vos attentes.

Bonne mise en œuvre

La solution rapide, mais fausse consiste à alias __repr__ à __str__.

__repr__ ne doit pas être défini sur __str__ sans condition . __repr__ devrait créer une représentation, qui devrait ressembler à une expression valide Python pouvant être utilisée pour recréer un objet avec la même valeur . Dans ce cas, cela serait plutôt Node(2) que 2.

Une implémentation correcte de __repr__ permet de recréer l'objet. Dans cet exemple, il devrait également contenir les autres membres significatifs, tels que neighours et distance.

Un exemple incomplet :

class Node:

    def __init__(self, id, neighbours=[], distance=0):
        self.id = id
        self.neighbours = neighbours
        self.distance = distance


    def __str__(self):
        return str(self.id)


    def __repr__(self):
        return "Node(id={0.id}, neighbours={0.neighbours!r}, distance={0.distance})".format(self)
        # in an elaborate implementation, members that have the default
        # value could be left out, but this would hide some information


uno = Node(1)    
due = Node(2)    
tri = Node(3)    
qua = Node(4)

print uno
print str(uno)
print repr(uno)

uno.neighbours.append([[due, 4], [tri, 5]])

print uno
print uno.neighbours
print repr(uno)

Note : print repr(uno) avec une implémentation appropriée de __eq__ et __ne__ ou __cmp__ permettrait de recréer l'objet et de vérifier pour l'égalité.

6
trapicki

__str__ n'est appelé que lorsqu'un représentation sous forme de chaîne est requis d'un objet.

Par exemple, str(uno), print "%s" % uno ou print uno

Cependant, il existe une autre méthode magique appelée __repr__ c'est la représentation d'un objet. Lorsque vous ne convertissez pas explicitement l'objet en chaîne, la représentation est utilisée.

Si vous faites ceci uno.neighbors.append([[str(due),4],[str(tri),5]]) il fera ce que vous attendez.

4
Burhan Khalid

Le problème des classes et de la définition de variables globales non numérotées égales à une valeur de la classe est que ce que votre variable globale stocke est en réalité la référence à l'emplacement de mémoire où la valeur est réellement stockée.

Ce que vous voyez dans votre production en est une indication.

Si vous pouvez voir la valeur et utiliser print sans problème les variables globales initiales que vous avez utilisées à cause de la méthode str et du fonctionnement de print, vous ne pourrez pas le faire avec des listes, parce que ce qui est stocké dans les éléments de cette liste est simplement une référence à l'emplacement de la mémoire de la valeur - lisez sur les alias, si vous voulez en savoir plus.

De plus, lorsque vous utilisez des listes et que vous perdez la trace de ce qui est un alias et de ce qui ne l’est pas, vous pouvez constater que vous modifiez la valeur de l’élément de la liste d’origine, si vous le modifiez dans une liste d’alias. Si l'élément de liste est égal à une liste ou à un élément d'une liste, la nouvelle liste ne stocke que la référence à l'emplacement de la mémoire (elle ne crée pas réellement de nouvel espace de mémoire spécifique à cette nouvelle variable). C'est là que deepcopy est utile!

1
Tommy W.