Come creare una rappresentazione di stringa personalizzata per un oggetto di classe?


202

Considera questa classe:

class foo(object):
    pass

La rappresentazione di stringa predefinita è simile alla seguente:

>>> str(foo)
"<class '__main__.foo'>"

Come posso rendere questo display una stringa personalizzata?

Risposte:


272

Implementa __str__()o __repr__()nella metaclasse della classe.

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

class C(object):
  __metaclass__ = MC

print C

Utilizzare __str__se si intende una stringificazione leggibile, utilizzare __repr__per rappresentazioni non ambigue.


2
Vorrei creare una sorta di decoratore di classi, in modo da poter impostare facilmente rappresentazioni di stringhe personalizzate per le mie classi senza dover scrivere una metaclasse per ognuna di esse. Non conosco molto bene le metaclasse di Python, quindi puoi darmi qualche suggerimento?
Björn Pollex,

Purtroppo questo non può essere fatto con i decoratori di classe; deve essere impostato quando viene definita la classe.
Ignacio Vazquez-Abrams,

10
@ Space_C0wb0y: è possibile aggiungere una stringa simile _representational corpo della classe e return self._representational __repr__()metodo della metaclasse.
Sven Marnach,

@ BjörnPollex Potresti riuscire a farcela con Decorator, ma mi aspetto che tu debba lottare con molte sottigliezze del linguaggio. E anche se lo fai, sei comunque obbligato a usare la metaclasse in un modo o nell'altro, poiché non vuoi usare il valore predefinito __repr__per rappresentare C. Un'alternativa all'avere un _representationmembro è quella di creare una fabbrica di metaclasse che produca una metaclasse con il corretto __repr__(questo potrebbe essere utile se lo usi molto).
skyking


22
class foo(object):
    def __str__(self):
        return "representation"
    def __unicode__(self):
        return u"representation"

10
Ciò modifica la rappresentazione della stringa per instancesla classe, non per la classe stessa.
Tauran,

scusa, non vedi la seconda parte del tuo post. Usa il metodo sopra.
Andrey Gubarev,

6
@RobertSiemer Perché? Sebbene la sua risposta non riguardi specificamente la domanda del PO, è comunque utile. Mi ha aiutato E a colpo d'occhio, non vedo alcuna domanda che richieda l'implementazione. Quindi probabilmente la gente atterra prima su questa pagina.
akinuri,

14

Se devi scegliere tra __repr__o __str__scegliere il primo, come per impostazione predefinita, l'implementazione __str__chiama __repr__quando non è stato definito.

Esempio di Vector3 personalizzato:

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)

In questo esempio, reprrestituisce di nuovo una stringa che può essere consumata / eseguita direttamente, mentre strè più utile come output di debug.

v = Vector3([1,2,3])
print repr(v)    #Vector3([1,2,3])
print str(v)     #x:1, y:2, z:3

1
Mentre il tuo punto su __repr__vs __str__è corretto, questo non risponde alla domanda reale, che riguarda gli oggetti di classe, non le istanze.
Björn Pollex,

Grazie per il feedback, supervisionato completamente. Lasciami rivedere la mia risposta.
user1767754,

Penso che le tue implementazioni per repr e str vengano scambiate.
jspencer,

4

La risposta approvata da Ignacio Vazquez-Abrams è assolutamente corretta. È, tuttavia, dalla generazione di Python 2. Un aggiornamento per l'attuale Python 3 sarebbe:

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

class C(object, metaclass=MC):
    pass

print(C)

Se si desidera che il codice venga eseguito su Python 2 e Python 3, il modulo sei è coperto:

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)

Infine, se si dispone di una classe per cui si desidera avere una repr statica personalizzata, l'approccio basato sulla classe sopra funziona alla grande. Ma se ne hai diversi, dovresti generare una metaclasse simile a MCciascuno di essi e questo può diventare noioso. In tal caso, fare un passo avanti con la metaprogrammazione e creare una fabbrica di metaclasse rende le cose un po 'più pulite:

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)

stampe:

Wahaha! Booyah! Gotcha!

La metaprogrammazione non è qualcosa di cui di solito hai bisogno ogni giorno, ma quando ne hai bisogno, colpisce davvero il punto!


0

Aggiungo solo a tutte le belle risposte, la mia versione con decorazioni:

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!

I lati negativi:

  1. Non puoi dichiarare Csenza una super classe (no class C:)
  2. Cle istanze saranno istanze di una strana derivazione, quindi è probabilmente una buona idea aggiungere anche una __repr__per le istanze.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.