3711 votes

Différence entre __str__ et __repr__ en Python

Quelle est la différence entre __str__ et __repr__ en Python ?

3497voto

moshez Points 9516

Alex a bien résumé mais, étonnamment, il a été trop succinct.

Tout d'abord, permettez-moi de réitérer les principaux points du post d'Alex :

  • L'implémentation par défaut est inutile (il est difficile de penser à une implémentation qui ne le serait pas, mais oui)
  • __repr__ l'objectif est d'être sans ambiguïté
  • __str__ l'objectif est d'être lisible
  • Container's __str__ utilise les "objets contenus". __repr__

L'implémentation par défaut est inutile

C'est surtout une surprise car les valeurs par défaut de Python ont tendance à être assez utiles. Cependant, dans ce cas, le fait d'avoir une valeur par défaut pour le paramètre __repr__ qui agirait comme :

return "%s(%r)" % (self.__class__, self.__dict__)

aurait été trop dangereux (par exemple, trop facile de se retrouver dans une récursion infinie si les objets se référencent les uns les autres). Donc Python s'en sort. Notez qu'il existe un défaut qui est vrai : si __repr__ est défini, et __str__ ne l'est pas, l'objet se comportera comme si __str__=__repr__ .

Cela signifie, en termes simples, que presque chaque objet que vous implémentez doit avoir une fonction __repr__ qui soit utilisable pour comprendre l'objet. Mise en œuvre de __str__ est facultatif : faites-le si vous avez besoin d'une fonctionnalité de "jolie impression" (par exemple, utilisée par un générateur de rapports).

L'objectif de __repr__ doit être sans ambiguïté

Permettez-moi de le dire franchement : je ne crois pas aux débogueurs. Je ne sais pas vraiment comment utiliser un débogueur, et je n'en ai jamais utilisé un sérieusement. De plus, je crois que le grand défaut des débogueurs est leur nature même - la plupart des échecs que je débogue se sont produits il y a très longtemps, dans une galaxie très lointaine. Cela signifie que je crois, avec une ferveur religieuse, à la journalisation. La journalisation est l'élément vital de tout système de serveur décent "fire-and-forget". Python facilite la journalisation : avec peut-être quelques wrappers spécifiques à un projet, tout ce dont vous avez besoin est un fichier de type

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)

Mais vous devez faire la dernière étape - vous assurer que chaque objet que vous implémentez a une représentation utile, afin que le code comme celui-ci puisse fonctionner. C'est pour cela que l'on parle de "eval" : si vous avez suffisamment d'informations pour que eval(repr(c))==c cela signifie que vous savez tout ce qu'il y a à savoir sur c . Si c'est assez facile, au moins de manière floue, faites-le. Sinon, assurez-vous d'avoir suffisamment d'informations sur les points suivants c de toute façon. J'utilise généralement un format de type eval : "MyClass(this=%r,that=%r)" % (self.this,self.that) . Cela ne signifie pas que vous pouvez réellement construire MyClass, ou que ce sont les bons arguments du constructeur - mais c'est une forme utile pour exprimer "c'est tout ce que vous devez savoir sur cette instance".

Note : J'ai utilisé %r ci-dessus, pas %s . Vous voulez toujours utiliser repr() [ou %r caractère de formatage, de manière équivalente] à l'intérieur __repr__ ou vous allez à l'encontre de l'objectif de la répression. Vous voulez être capable de différencier MyClass(3) et MyClass("3") .

L'objectif de __str__ est d'être lisible

Plus précisément, il n'est pas destiné à être sans ambiguïté - remarquez que str(3)==str("3") . De la même manière, si vous implémentez une abstraction IP, le fait que la chaîne de caractères ressemble à 192.168.1.1 est tout à fait acceptable. Lorsque vous implémentez une abstraction de date/heure, la chaîne peut être "2010/4/12 15:35:22", etc. L'objectif est de la représenter de manière à ce qu'un utilisateur, et non un programmeur, veuille la lire. Supprimez les chiffres inutiles, faites semblant d'être une autre classe - tant que cela permet la lisibilité, c'est une amélioration.

Container's __str__ utilise les "objets contenus". __repr__

Cela semble surprenant, n'est-ce pas ? C'est un peu, mais à quel point serait-il lisible

[moshe is, 3, hello
world, this is a list, oh I don't know, containing just 4 elements]

être ? Pas vraiment. Plus précisément, les chaînes d'un conteneur trouveraient qu'il est beaucoup trop facile de perturber sa représentation des chaînes. Face à l'ambiguïté, rappelez-vous que Python résiste à la tentation de deviner. Si vous voulez le comportement ci-dessus lorsque vous imprimez une liste, il suffit de

print "["+", ".join(l)+"]"

(vous pouvez probablement aussi trouver une solution pour les dictionnaires.

Résumé

Mettre en œuvre __repr__ pour toute classe que vous implémentez. Cela devrait être une seconde nature. Implémenter __str__ si vous pensez qu'il serait utile de disposer d'une version de chaîne de caractères qui privilégie la lisibilité au détriment de l'ambiguïté.

375 votes

Je ne suis absolument pas d'accord avec votre opinion selon laquelle le débogage n'est pas la solution. Pour le développement, utilisez un débogueur (et/ou la journalisation), pour la production, utilisez la journalisation. Avec un débogueur, vous avez une vue de tout ce qui s'est passé lorsque le problème est survenu. Vous pouvez voir l'ensemble de la situation. À moins que vous n'enregistriez TOUT, vous ne pouvez pas obtenir cela. De plus, si vous enregistrez tout, vous devrez passer en revue des tonnes de données pour obtenir ce que vous voulez.

44 votes

Excellente réponse (à l'exception de la partie concernant la non-utilisation de débogueurs). J'aimerais juste ajouter un lien vers ceci d'autres questions et réponses sur str vs unicode dans Python 3 qui pourraient être pertinents pour la discussion pour les personnes qui ont fait le changement.

17 votes

Les plus1 pour les débogueurs sont inutiles, et ne s'échelonnent pas pour un sou. Augmentez plutôt le débit de votre logging. Et oui, c'était un post bien écrit. Il s'avère que __repr__ était ce dont j'avais besoin pour le débogage. Merci pour votre aide.

805voto

Ned Batchelder Points 128913

Ma règle d'or : __repr__ s'adresse aux développeurs, __str__ est destiné aux clients.

26 votes

Ceci est vrai car pour obj = uuid.uuid1(), obj.__str__() est "2d7fc7f0-7706-11e9-94ae-0242ac110002" et obj.__repr__() est "UUID('2d7fc7f0-7706-11e9-94ae-0242ac110002')". Les développeurs ont besoin de (valeur + origine) alors que les clients ont besoin d'une valeur et ils ne se soucient pas de savoir comment ils l'ont obtenue !

15 votes

Ici client ne signifie pas nécessairement utilisateur final. C'est le client ou l'utilisateur de l'objet. Ainsi, s'il s'agit d'un SDK, les développeurs du SDK utiliseront les termes suivants __str__ pour que les développeurs normaux aient un objet lisible. D'un autre côté, __repr__ est destiné aux développeurs du SDK eux-mêmes.

1 votes

@NarenYellavula si vous exposez un UUID à un client, vous faites probablement quelque chose de mal.

538voto

Alex Martelli Points 330805

À moins que vous n'agissiez spécifiquement pour qu'il en soit autrement, la plupart des cours n'ont pas de résultats utiles pour l'un ou l'autre :

>>> class Sic(object): pass
... 
>>> print str(Sic())
<__main__.Sic object at 0x8b7d0>
>>> print repr(Sic())
<__main__.Sic object at 0x8b7d0>
>>>

Comme vous le voyez, il n'y a pas de différence, et aucune information autre que la classe et le nom de l'objet. id . Si vous ne dérogez qu'à l'un des deux... :

>>> class Sic(object): 
...   def __repr__(object): return 'foo'
... 
>>> print str(Sic())
foo
>>> print repr(Sic())
foo
>>> class Sic(object):
...   def __str__(object): return 'foo'
... 
>>> print str(Sic())
foo
>>> print repr(Sic())
<__main__.Sic object at 0x2617f0>
>>>

comme vous le voyez, si vous remplacez __repr__ qui est AUSSI utilisé pour __str__ mais pas l'inverse.

Autres informations importantes à connaître : __str__ sur un conteneur construit utilise l'option __repr__ et non le __str__ pour les articles qu'il contient. Et, malgré les mots sur le sujet que l'on trouve dans les docs typiques, presque personne ne se soucie de faire l'effort de faire l'inventaire des éléments. __repr__ d'objets est une chaîne de caractères qui eval peut utiliser pour construire un objet équivalent (c'est tout simplement trop difficile, et le fait de ne pas savoir comment le module concerné a été importé rend la chose carrément impossible).

Donc, mon conseil : concentrez-vous sur la réalisation __str__ raisonnablement lisible par l'homme, et __repr__ le moins ambigu possible, même si cela interfère avec l'objectif flou et irréalisable de rendre __repr__ La valeur retournée par l'utilisateur est acceptable comme entrée pour __eval__ !

53 votes

Dans mes tests unitaires, je vérifie toujours que eval(repr(foo)) est évalué à un objet égal à foo . Vous avez raison de dire que cela ne fonctionnera pas en dehors de mes scénarios de test puisque je ne sais pas comment le module est importé, mais cela permet au moins de s'assurer que cela fonctionne en un peu de contexte prévisible. Je pense que c'est une bonne façon d'évaluer si le résultat de __repr__ est suffisamment explicite. Faire cela dans un test unitaire permet également de s'assurer que __repr__ suit les modifications apportées à la classe.

7 votes

J'essaie toujours de m'assurer que eval(repr(spam)) == spam (du moins dans le bon contexte), ou encore eval(repr(spam)) soulève un SyntaxError . De cette façon, vous évitez toute confusion. (Et c'est presque vrai pour les builtins et la plupart de la stdlib, sauf pour, par exemple, les listes récursives, où a=[]; a.append(a); print(eval(repr(a))) vous donne [[Ellipses]] ) Bien sûr, je ne fais pas cela pour réellement utiliser eval(repr(spam)) sauf pour vérifier la cohérence des tests unitaires mais je faire parfois copier et coller repr(spam) dans une session interactive.

1 votes

Pourquoi les conteneurs (listes, tuples) n'utiliseraient-ils pas __str__ pour chaque élément au lieu de __repr__ ? Cela me semble tout à fait erroné, car j'ai mis en place un système de __str__ dans mon objet et quand il fait partie d'une liste, je vois le plus laid. __repr__ à la place.

222voto

elliospizzaman Points 898

En bref, l'objectif de __repr__ doit être sans ambiguïté et __str__ est d'être lisible.

Voici un bon exemple :

>>> import datetime
>>> today = datetime.datetime.now()
>>> str(today)
'2012-03-14 09:21:58.130922'
>>> repr(today)
'datetime.datetime(2012, 3, 14, 9, 21, 58, 130922)'

Lisez cette documentation pour en savoir plus :

repr(object)

Renvoie une chaîne de caractères contenant une représentation imprimable d'un objet. C'est la même valeur que celle obtenue par les conversions (guillemets inversés). guillemets inversés). Il est parfois utile de pouvoir accéder à cette opération en tant que une fonction ordinaire. Pour de nombreux types, cette fonction tente de retourner une chaîne de caractères qui donnerait un objet de même valeur lorsqu'elle est passé à eval() sinon, la représentation est une chaîne de caractères entre entre crochets qui contient le nom du type de l'objet ainsi que des informations supplémentaires comprenant souvent le nom et l'adresse de l'objet. Une classe peut contrôler ce que cette fonction renvoie pour ses instances en définissant un paramètre __repr__() méthode.

Voici la documentation pour str :

str(object='')

Retourne une chaîne de caractères contenant une représentation d'un objet. Pour les chaînes de caractères, cela renvoie la chaîne elle-même. La différence avec repr(object) c'est que str(object) ne ne tente pas toujours de retourner une chaîne de caractères acceptable pour eval() ; son objectif est de retourner une chaîne imprimable. Si aucun argument n'est donné, retourne la chaîne vide, '' .

3 votes

Quelle est la signification de la chaîne imprimable ici ? Pouvez-vous l'expliquer ?

3 votes

En se basant sur l'exemple ci-dessus de "bitoffdev" et @deadly, nous pouvons voir comment str est pour l'utilisateur final car il ne nous donne qu'une chaîne de caractères lisible alors que répression est un outil pour les développeurs car il nous donne la valeur ainsi que le type. Si vous cherchez des réponses à des entretiens, il est parfait.

218voto

__repr__ Représentation d'un objet python. En général, eval le convertit en cet objet.

__str__ : est ce que vous pensez être cet objet sous forme de texte.

par exemple

>>> s="""w'o"w"""
>>> repr(s)
'\'w\\\'o"w\''
>>> str(s)
'w\'o"w'
>>> eval(str(s))==s
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    w'o"w
       ^
SyntaxError: EOL while scanning single-quoted string
>>> eval(repr(s))==s
True

4 votes

__repr__() : utilisé pour créer une "expression de type constructeur" dans une chaîne de caractères, de sorte que eval() puisse reconstruire un objet à partir de cette représentation __str__() : utilisé pour créer une chaîne de caractères contenant une représentation imprimable d'un objet

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X