Python __str__ contre __unicode__


213

Existe-t-il une convention python pour le moment où vous devez implémenter __str__()versus __unicode__(). J'ai vu des classes remplacer __unicode__()plus fréquemment que __str__()cela, mais cela ne semble pas être cohérent. Existe-t-il des règles spécifiques lorsqu'il est préférable de mettre en œuvre l'une par rapport à l'autre? Est-il nécessaire / bonne pratique de mettre en œuvre les deux?

Réponses:


257

__str__()est l'ancienne méthode - elle renvoie des octets. __unicode__()est la nouvelle méthode préférée - elle renvoie des caractères. Les noms sont un peu déroutants, mais dans 2.x nous sommes coincés avec eux pour des raisons de compatibilité. En règle générale, vous devez placer toute la mise en forme de vos chaînes __unicode__()et créer une __str__()méthode de remplacement :

def __str__(self):
    return unicode(self).encode('utf-8')

Dans 3.0, strcontient des caractères, donc les mêmes méthodes sont nommées __bytes__()et __str__(). Ceux-ci se comportent comme prévu.


2
sa vous voulez dire créer des méthodes unicode et str ou simplement conserver des chaînes dans _ (u "") et créer une chaîne (sans la méthode unicode)?
muntu

12
Y a-t-il un écueil à mettre en œuvre un seul d'entre eux? Que se passe-t-il lorsque vous implémentez __unicode__et effectuez uniquement str(obj)?
RickyA

9
unicodesoulève un NameErrorsur Python 3, est un modèle simple qui fonctionne à la fois sur 2 et 3?
bradley.ayers

1
@ bradley.ayers le futurepaquet fournit également python_2_unicode_compatiblesans avoir Django comme dépendance.
Monkpit

1
Ça dépend. Parce que python3 n'utilise pas unicode mais à la place str ;) pour python 2 unicode
Eddwin Paz

23

Si je ne m'intéressais pas particulièrement à la micro-optimisation de la chaîne de caractères pour une classe donnée, je mettrais toujours en œuvre __unicode__uniquement, car c'est plus général. Lorsque je me soucie de problèmes de performances aussi minimes (ce qui est l'exception, pas la règle), avoir __str__seulement (quand je peux prouver qu'il n'y aura jamais de caractères non ASCII dans la sortie stringifiée) ou les deux (lorsque les deux sont possibles), pourrait Aidez-moi.

Je pense que ce sont des principes solides, mais dans la pratique, il est très courant de SAVOIR qu'il n'y aura que des caractères ASCII sans faire d'effort pour le prouver (par exemple, la forme stringifiée n'a que des chiffres, de la ponctuation et peut-être un nom ASCII court ;-) dans lequel cas, il est assez typique de passer directement à l' __str__approche "juste " (mais si une équipe de programmation avec laquelle j'ai travaillé a proposé une directive locale pour éviter cela, je serais +1 sur la proposition, car il est facile de se tromper sur ces questions ET "l'optimisation prématurée est la racine de tout mal dans la programmation" ;-).


2
En python 2.6.2, j'ai récemment été interrompu parce que les instances d'une sous-classe d'exception intégrée particulière ont donné des résultats différents avec str (e) et unicode (e). str (e) a donné une sortie conviviale; unicode (e) a donné une sortie différente et peu conviviale. Est-ce considéré comme un comportement buggé? La classe est UnicodeDecodeError; Je ne l'ai pas nommé à l'avance pour éviter toute confusion - le fait que l'exception soit liée à l'unicode n'est pas particulièrement pertinent.
Paul Du Bois

13

Le monde devenant plus petit, il est probable que toute chaîne que vous rencontrerez contiendra éventuellement Unicode. Donc, pour toute nouvelle application, vous devez au moins fournir __unicode__(). Que vous outrepassiez également ce __str__()n'est qu'une question de goût.


8

Si vous travaillez à la fois en python2 et en python3 dans Django, je recommande le décorateur python_2_unicode_compatible:

Django fournit un moyen simple de définir les méthodes str () et unicode () qui fonctionnent sur Python 2 et 3: vous devez définir une méthode str () renvoyant du texte et appliquer le décorateur python_2_unicode_compatible ().

Comme indiqué dans les commentaires précédents d'une autre réponse, certaines versions de future.utils prennent également en charge ce décorateur. Sur mon système, j'avais besoin d'installer un futur module plus récent pour python2 et d'installer future pour python3. Après cela, voici un exemple fonctionnel:

#! /usr/bin/env python

from future.utils import python_2_unicode_compatible
from sys import version_info

@python_2_unicode_compatible
class SomeClass():
    def __str__(self):
        return "Called __str__"


if __name__ == "__main__":
    some_inst = SomeClass()
    print(some_inst)
    if (version_info > (3,0)):
        print("Python 3 does not support unicode()")
    else:
        print(unicode(some_inst))

Voici un exemple de sortie (où venv2 / venv3 sont des instances de virtualenv):

~/tmp$ ./venv3/bin/python3 demo_python_2_unicode_compatible.py 
Called __str__
Python 3 does not support unicode()

~/tmp$ ./venv2/bin/python2 demo_python_2_unicode_compatible.py 
Called __str__
Called __str__

3

Python 2: implémentez __str __ () uniquement et renvoyez un unicode.

Lorsque __unicode__()est omis et que quelqu'un appelle unicode(o)ou u"%s"%o, Python appelle o.__str__()et convertit en unicode en utilisant l'encodage système. (Voir la documentation de__unicode__() .)

L'inverse n'est pas vrai. Si vous implémentez __unicode__()mais pas __str__(), alors quand quelqu'un appelle str(o)ou "%s"%o, Python revient repr(o).


Raisonnement

Pourquoi serait - il travailler pour revenir un à unicodepartir __str__()?
Si __str__()renvoie un unicode, Python le convertit automatiquement enstr utilisant l'encodage système.

Quel est l'avantage?
① Cela vous évite de vous soucier de l’encodage du système (c.-à locale.getpreferredencoeding(…)-d.). Non seulement c'est désordonné, personnellement, mais je pense que c'est quelque chose que le système devrait prendre en charge de toute façon. ② Si vous faites attention, votre code peut sortir compatible avec Python 3, dans lequel__str__() renvoie unicode.

N'est-il pas trompeur de renvoyer un unicode à partir d'une fonction appelée __str__()?
Un peu. Cependant, vous le faites peut-être déjà. Si vous avez from __future__ import unicode_literalsen haut de votre fichier, il y a de fortes chances que vous retourniez un unicode sans même le savoir.

Et Python 3?
Python 3 n'utilise pas __unicode__(). Cependant, si vous implémentez__str__() pour qu'il renvoie unicode sous Python 2 ou Python 3, alors cette partie de votre code sera compatible.

Et si je veux unicode(o)être substantiellement différent de str()?
Implémentez à la fois __str__()(éventuellement le retour str) et __unicode__(). J'imagine que ce serait rare, mais vous voudrez peut-être une sortie substantiellement différente (par exemple, des versions ASCII de caractères spéciaux, comme ":)"pour u"☺").

Je me rends compte que certains peuvent trouver cela controversé.


1

Il convient de signaler à ceux qui ne connaissent pas la __unicode__fonction certains des comportements par défaut qui l'entourent dans Python 2.x, en particulier lorsqu'ils sont définis côte à côte avec __str__.

class A :
    def __init__(self) :
        self.x = 123
        self.y = 23.3

    #def __str__(self) :
    #    return "STR      {}      {}".format( self.x , self.y)
    def __unicode__(self) :
        return u"UNICODE  {}      {}".format( self.x , self.y)

a1 = A()
a2 = A()

print( "__repr__ checks")
print( a1 )
print( a2 )

print( "\n__str__ vs __unicode__ checks")
print( str( a1 ))
print( unicode(a1))
print( "{}".format( a1 ))
print( u"{}".format( a1 ))

donne la sortie console suivante ...

__repr__ checks
<__main__.A instance at 0x103f063f8>
<__main__.A instance at 0x103f06440>

__str__ vs __unicode__ checks
<__main__.A instance at 0x103f063f8>
UNICODE 123      23.3
<__main__.A instance at 0x103f063f8>
UNICODE 123      23.3

Maintenant, quand je commente le __str__ méthode

__repr__ checks
STR      123      23.3
STR      123      23.3

__str__ vs __unicode__ checks
STR      123      23.3
UNICODE  123      23.3
STR      123      23.3
UNICODE  123      23.3
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.