Python: implémenter simplement __str__ sur une classe quand __unicode__ est défini
En Python 2.7 et avant, appeler la fonction str(…)
sur un objet appelle forcément son implémentation de __str__
, même si la méthode __unicode__
est définie (l’inverse fonctionne par contre). Il en va de même pour le statement print
, qui appelle en sous-main la fonction str
.
Voici comment simplement implémenter une méthode __str__
qui s’appuie sur __unicode__
.
Prenons par exemple une classe représentant un fichier. Typiquement, le nom de mon fichier peut tout-à-fait contenir des caractères non ASCII: des caractères accentués.
class MyFile(object): def __init__(self, path): if isinstance(path, str): path = transform_to_unicode(path) # Je passe les détails de cette fonction magique # qui pourra faire l'objet d'un autre article self._fpath = path def path(self): return self._fpath def __unicode__(self): # Ex: [monfichier.txt] return u'[{0}]'.format(self.path())
Ce qui se passe, si je fais un print
sur un objet, c’est que python appelle sa méthode __repr__
:
>>> print MyFile('monfichier.txt')
Ce n’est pas ce que je veux!
Je pourrais donc bêtement faire que __str__
renvoie le résultat de __unicode__
, mais je m’exposerai lors d’un affichage dans une console, des problèmes d’encodage, se manifestant par une exception de type UnicodeEncodeError
. En plus, ça ne serait évidemment pas correct que cette fonction renvoie autre chose qu’un str
. Il faut donc préciser explicitement la conversion d’encodage. Pour cela, je peux utiliser la valeur liée à la console courante.
class MyFile(object): # ... # ... def __str__(self): import sys return unicode(self).encode(sys.stdout.encoding or 'ascii','replace')
J’utilise la méthode encode
du type unicode
pour convertir en chaîne de caractères que comprendra ma console. En encodage fallback (si sys.stdout.encoding
vaut None
), j’utilise ASCII
. Et si jamais certains caractères ne peuvent pas être convertis dans l’encodage cible, j’ai passé l’argument 'replace'
pour, au pire, remplacer les caractères invalides par un '?'
.
Au fait, si vous vous demandez pourquoi «
sys.stdout.encoding or 'ascii'
« , allez voir cet article.
À partir de Python 3, le problème devient plus simple, car toutes les chaînes de caractères sont des unicode
, la fonction encode
servant alors à obtenir la chaîne d’octets dans le bon encodage.