Considérez cette classe:
class foo(object):
pass
La représentation de chaîne par défaut ressemble à ceci:
>>> str(foo)
"<class '__main__.foo'>"
Comment puis-je faire en sorte que cet affichage soit une chaîne personnalisée?
Implémentez __str__()
ou __repr__()
dans la métaclasse de la classe.
class MC(type):
def __repr__(self):
return 'Wahaha!'
class C(object):
__metaclass__ = MC
print C
Utilisez __str__
si vous voulez une chaîne lisible, utilisez __repr__
pour des représentations non ambiguës.
class foo(object):
def __str__(self):
return "representation"
def __unicode__(self):
return u"representation"
Si vous devez choisir entre __repr__
ou __str__
, optez pour le premier, comme implémentation par défaut __str__
appelle __repr__
alors qu'elle n'était pas définie.
Exemple de Vector3 personnalisé:
class Vector3(object):
def __init__(self, args):
self.x = args[0]
self.y = args[1]
self.z = args[2]
def __repr__(self):
return "Vector3([{0},{1},{2}])".format(self.x, self.y, self.z)
def __str__(self):
return "x: {0}, y: {1}, z: {2}".format(self.x, self.y, self.z)
Dans cet exemple, repr
renvoie à nouveau une chaîne pouvant être directement consommée/exécutée, alors que str
est plus utile en tant que sortie de débogage.
v = Vector3([1,2,3])
print repr(v) #Vector3([1,2,3])
print str(v) #Vector(x:1, y:2, z:3)
La réponse approuvée d'Ignacio Vazquez-Abrams a tout à fait raison. Il s'agit toutefois de la génération Python 2. Une mise à jour pour le Python 3 actuellement en cours serait:
class MC(type):
def __repr__(self):
return 'Wahaha!'
class C(object, metaclass=MC):
pass
print(C)
Si vous voulez un code qui traverse Python 2 et Python 3, le module six vous a couvert:
from __future__ import print_function
from six import with_metaclass
class MC(type):
def __repr__(self):
return 'Wahaha!'
class C(with_metaclass(MC)):
pass
print(C)
Enfin, si vous voulez une repro statique sur mesure pour une classe, l’approche basée sur la classe ci-dessus fonctionne très bien. Mais si vous en avez plusieurs, vous devrez générer une métaclasse semblable à MC
pour chaque élément, ce qui peut être fastidieux. Dans ce cas, aller plus loin dans votre métaprogrammation et créer une usine de métaclasses rend les choses un peu plus propres:
from __future__ import print_function
from six import with_metaclass
def custom_class_repr(name):
"""
Factory that returns custom metaclass with a class ``__repr__`` that
returns ``name``.
"""
return type('whatever', (type,), {'__repr__': lambda self: name})
class C(with_metaclass(custom_class_repr('Wahaha!'))): pass
class D(with_metaclass(custom_class_repr('Booyah!'))): pass
class E(with_metaclass(custom_class_repr('Gotcha!'))): pass
print(C, D, E)
impressions:
Wahaha! Booyah! Gotcha!
La métaprogrammation n'est pas une tâche dont vous avez généralement besoin tous les jours, mais quand vous en avez besoin, elle frappe vraiment!
En ajoutant à toutes les bonnes réponses, ma version avec décoration:
from __future__ import print_function
import six
def classrep(rep):
def decorate(cls):
class RepMetaclass(type):
def __repr__(self):
return rep
class Decorated(six.with_metaclass(RepMetaclass, cls)):
pass
return Decorated
return decorate
@classrep("Wahaha!")
class C(object):
pass
print(C)
stdout:
Wahaha!
Les inconvénients:
C
sans une super classe (no class C:
)C
seront des instances d'une dérivation étrange, c'est donc probablement une bonne idée d'ajouter un __repr__
pour les instances également.