Como criar uma representação de string personalizada para um objeto de classe?

202

Considere esta classe:

class foo(object):
    pass

A representação de string padrão é algo como isto:

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

Como faço para exibir essa string personalizada?

Björn Pollex
fonte

Respostas:

272

Implemente __str__()ou __repr__()na metaclasse da classe.

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

class C(object):
  __metaclass__ = MC

print C

Use __str__se você quer dizer uma stringificação legível, use __repr__para representações inequívocas.

Ignacio Vazquez-Abrams
fonte
2
Gostaria de criar algum tipo de decorador de classe, para que eu possa definir facilmente representações de strings personalizadas para minhas classes sem precisar escrever uma metaclasse para cada uma delas. Eu não estou muito familiarizado com as metaclasses do Python, então você pode me dar alguma dica aí?
Björn Pollex
Infelizmente, isso não pode ser feito com decoradores de classe; deve ser definido quando a classe estiver definida.
Ignacio Vazquez-Abrams
10
@ Space_C0wb0y: Você pode adicionar uma string como _representationno corpo da classe e return self._representationno __repr__()método da metaclasse.
Sven Marnach 8/02
@ BjörnPollex Você pode conseguir isso com o decorador, mas eu espero que você tenha que lidar com muitas sutilezas do idioma. E mesmo se você o fizer, ainda poderá usar a metaclasse de uma maneira ou de outra, pois não deseja usar o padrão __repr__para representar C. Uma alternativa para ter um _representationmembro é criar uma fábrica de metaclasse que produza uma metaclasse com o apropriado __repr__(isso pode ser bom se você estiver usando muito isso).
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
fonte
10
Isso altera a representação de string para instancesa classe, não para a própria classe.
tauran
desculpe, não vê a segunda parte da sua postagem. Use o método acima.
Andrey Gubarev
6
@RobertSiemer Why? Embora a resposta dele não esteja voltada especificamente para a pergunta do OP, ainda é útil. Isso me ajudou. E de relance, não vejo nenhuma pergunta pedindo, por exemplo, implementação. Então, provavelmente as pessoas chegam primeiro a esta página.
akinuri
14

Se você precisar escolher __repr__ou __str__optar pelo primeiro, como a implementação padrão __str__chama __repr__quando não foi definido.

Exemplo Vector3 personalizado:

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)

Neste exemplo, reprretorna novamente uma sequência que pode ser consumida / executada diretamente, enquanto stré mais útil como uma saída de depuração.

v = Vector3([1,2,3])
print repr(v)    #Vector3([1,2,3])
print str(v)     #x:1, y:2, z:3
user1767754
fonte
1
Embora seu argumento sobre __repr__vs __str__esteja correto, isso não responde à pergunta real, que é sobre objetos de classe, não instâncias.
Björn Pollex
Obrigado pelo feedback, supervisionado completamente isso. Deixe-me revisar minha resposta.
precisa saber é o seguinte
Eu acho que você implementações para repr e str são trocadas.
jspencer
4

A resposta aprovada de Ignacio Vazquez-Abrams está certa. É, no entanto, da geração Python 2. Uma atualização para o Python 3 agora atual seria:

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

class C(object, metaclass=MC):
    pass

print(C)

Se você deseja que o código seja executado tanto no Python 2 quanto no Python 3, o módulo seis foi coberto:

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)

Por fim, se você tem uma classe que deseja ter uma repr estática personalizada, a abordagem baseada em classe acima funciona muito bem. Mas se você tiver vários, terá que gerar uma metaclasse semelhante a MCcada um, e isso pode ser cansativo. Nesse caso, levar sua metaprogramação um passo adiante e criar uma fábrica de metaclasses torna as coisas um pouco mais limpas:

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)

impressões:

Wahaha! Booyah! Gotcha!

A metaprogramação não é algo que você geralmente precisa todos os dias - mas quando você precisa, realmente atinge o ponto!

Jonathan Eunice
fonte
0

Apenas adicionando a todas as boas respostas, minha versão com decoração:

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!

Os lados inferiores:

  1. Você não pode declarar Csem uma super classe (não class C:)
  2. Cinstâncias serão instâncias de alguma derivação estranha; portanto, provavelmente é uma boa ideia adicionar um __repr__para as instâncias também.
Aviv Goll
fonte