Comment créer une représentation de chaîne personnalisée pour un objet de classe?

202

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 de cet affichage une chaîne personnalisée?

Björn Pollex
la source

Réponses:

273

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

À utiliser __str__si vous entendez une chaîne de caractères lisible, à utiliser __repr__pour des représentations non ambiguës.

Ignacio Vazquez-Abrams
la source
2
Je voudrais créer une sorte de décorateur de classe, afin que je puisse facilement définir des représentations de chaînes personnalisées pour mes classes sans avoir à écrire une métaclasse pour chacune d'entre elles. Je ne connais pas très bien les métaclasses de Python, pouvez-vous me donner des conseils là-bas?
Björn Pollex
Malheureusement, cela ne peut pas être fait avec des décorateurs de classe; il doit être défini lors de la définition de la classe.
Ignacio Vazquez-Abrams
10
@ Space_C0wb0y: Vous pouvez ajouter une chaîne similaire _representationau corps de classe et return self._representationdans la __repr__()méthode de la métaclasse.
Sven Marnach
@ BjörnPollex Vous pourriez être en mesure de tirer cela avec le décorateur, mais je m'attends à ce que vous ayez du mal avec beaucoup de subtilités de la langue. Et même si vous le faites, vous êtes toujours tenu d'utiliser la métaclasse d'une manière ou d'une autre, car vous ne voulez pas utiliser la valeur par défaut __repr__pour représenter C. Une alternative à avoir un _representationmembre est de créer une fabrique de métaclasses qui produit une métaclasse avec le bon __repr__(cela pourrait être bien si vous l'utilisez beaucoup).
skyking
2
@ThomasLeonard: stackoverflow.com/questions/39013249/metaclass-in-python3-5
Ignacio Vazquez-Abrams
22
class foo(object):
    def __str__(self):
        return "representation"
    def __unicode__(self):
        return u"representation"
Andrey Gubarev
la source
10
Cela modifie la représentation sous forme de chaîne pour instancesla classe, pas pour la classe elle-même.
tauran
désolé, ne voit pas la deuxième partie de votre message. Utilisez la méthode ci-dessus.
Andrey Gubarev
6
@RobertSiemer Pourquoi? Bien que sa réponse ne cible pas spécifiquement la question du PO, elle est toujours utile. Ça m'a aidé. Et en un coup d'œil, je ne vois aucune question demandant par exemple la mise en œuvre. Donc, les gens atterrissent probablement sur cette page en premier.
akinuri
14

Si vous devez choisir entre __repr__ou __str__opter pour le premier, comme par défaut l'implémentation __str__appelle __repr__quand elle n'a pas été 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, reprrenvoie à nouveau une chaîne qui peut être directement consommée / exécutée, alors qu'elle strest plus utile comme sortie de débogage.

v = Vector3([1,2,3])
print repr(v)    #Vector3([1,2,3])
print str(v)     #x:1, y:2, z:3
user1767754
la source
1
Bien que votre point sur __repr__vs __str__soit correct, cela ne répond pas à la question réelle, qui concerne les objets de classe, pas les instances.
Björn Pollex,
Merci pour les commentaires, complètement supervisé. Permettez-moi de revoir ma réponse.
user1767754
Je pense que vos implémentations pour repr et str sont échangées.
jspencer
4

La réponse approuvée d'Ignacio Vazquez-Abrams est tout à fait juste. Il s'agit cependant de la génération Python 2. Une mise à jour pour le Python 3 actuel serait:

class MC(type):
  def __repr__(self):
    return 'Wahaha!'

class C(object, metaclass=MC):
    pass

print(C)

Si vous voulez du code qui s'exécute à la fois sur 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 avez une classe que vous souhaitez avoir une représentation statique personnalisée, l'approche basée sur les classes ci-dessus fonctionne très bien. Mais si vous en avez plusieurs, vous devrez générer une métaclasse similaire à MCchacune, et cela peut devenir fastidieux. Dans ce cas, aller plus loin dans votre métaprogrammation et créer une fabrique 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 quelque chose dont vous avez généralement besoin tous les jours, mais quand vous en avez besoin, elle frappe vraiment!

Jonathan Eunice
la source
0

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:

  1. Vous ne pouvez pas déclarer Csans super classe (non class C:)
  2. Cles instances seront des instances d'une dérivation étrange, c'est donc probablement une bonne idée d'ajouter également un __repr__pour les instances.
Aviv Goll
la source