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? :)
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.
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__
.
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
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.
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.
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é.
__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.
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!