Björn Pollex
Considere esta clase:
class foo(object):
pass
La representación de cadena predeterminada se parece a esto:
>>> str(foo)
"<class '__main__.foo'>"
¿Cómo puedo hacer que esta pantalla sea una cadena personalizada?
Consulte ¿Cómo imprimir instancias de una clase usando print()? para la pregunta correspondiente sobre las instancias de la clase.
De hecho, esta pregunta es realmente un caso especial de esa, porque en Python, las clases también son objetos que pertenecen a su propia clase, pero no es directamente obvio cómo aplicar el consejo, ya que la “clase de clases” predeterminada es pre -definido.
Ignacio Vázquez-Abrams
Implementar __str__()
o __repr__()
en la metaclase de la clase.
class MC(type):
def __repr__(self):
return 'Wahaha!'
class C(object):
__metaclass__ = MC
print(C)
Usar __str__
si te refieres a una cadena legible, usa __repr__
para representaciones inequívocas.
Editar: Versión de Python 3
class MC(type):
def __repr__(self):
return 'Wahaha!'
class C(object, metaclass=MC):
pass
print(C)
-
Me gustaría crear algún tipo de decorador de clases, de modo que pueda configurar fácilmente representaciones de cadenas personalizadas para mis clases sin tener que escribir una metaclase para cada una de ellas. No estoy muy familiarizado con las metaclases de Python, ¿puede darme algún consejo al respecto?
–Björn Pollex
8 de febrero de 2011 a las 11:50
-
Desafortunadamente, esto no se puede hacer con los decoradores de clase; debe establecerse cuando se define la clase.
– Ignacio Vázquez-Abrams
8 de febrero de 2011 a las 11:55
-
@Space_C0wb0y: podría agregar una cadena como
_representation
al cuerpo de clase yreturn self._representation
en el__repr__()
método de la metaclase.– Sven Marnach
8 de febrero de 2011 a las 12:50
-
@BjörnPollex Es posible que pueda lograr esto con el decorador, pero espero que tenga que luchar con muchas sutilezas del lenguaje. E incluso si lo hace, todavía está obligado a usar metaclase de una forma u otra, ya que no desea usar el valor predeterminado
__repr__
representarC
. Una alternativa a tener un_representation
miembro es crear una fábrica de metaclases que produzca una metaclase con el__repr__
(Esto podría ser bueno si lo estás usando mucho).– Rey del cielo
01/04/2016 a las 10:10
-
@ThomasLeonard: stackoverflow.com/questions/39013249/metaclass-in-python3-5
– Ignacio Vázquez-Abrams
8 de noviembre de 2017 a las 18:34
class foo(object):
def __str__(self):
return "representation"
def __unicode__(self):
return u"representation"
-
Esto cambia la representación de cadena para
instances
de la clase, no para la clase misma.– tauro
8 de febrero de 2011 a las 11:39
-
lo siento, no veo la segunda parte de tu publicación. Utilice el método anterior.
– Andrei Gubarev
8 de febrero de 2011 a las 11:43
-
@RobertSiemer ¿Por qué? Si bien su respuesta no se dirige específicamente a la pregunta del OP, sigue siendo útil. Me ayudó. Y de un vistazo, no veo ninguna pregunta que solicite la implementación de instancias. Así que probablemente la gente llegue primero a esta página.
– akinuri
21 de mayo de 2018 a las 11:55
usuario1767754
Si tienes que elegir entre __repr__
o __str__
ve por el primero, como por defecto implementación __str__
llamadas __repr__
cuando no estaba definido.
Ejemplo de 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)
En este ejemplo, repr
devuelve de nuevo una cadena que se puede consumir/ejecutar directamente, mientras que str
es más útil como salida de depuración.
v = Vector3([1,2,3])
print repr(v) #Vector3([1,2,3])
print str(v) #x:1, y:2, z:3
-
Mientras que su punto sobre
__repr__
contra__str__
es correcto, esto no responde la pregunta real, que se trata de objetos de clase, no de instancias.–Björn Pollex
12 de enero de 2018 a las 9:39
-
Gracias por los comentarios, lo supervisé por completo. Déjame revisar mi respuesta.
– usuario1767754
12 de enero de 2018 a las 9:48
-
Creo que sus implementaciones para repr y str están intercambiadas.
– jspencer
9 de marzo de 2018 a las 0:09
La respuesta aprobada de Ignacio Vázquez-Abrams es bastante correcta. Sin embargo, es de la generación Python 2. Una actualización para el actual Python 3 sería:
class MC(type):
def __repr__(self):
return 'Wahaha!'
class C(object, metaclass=MC):
pass
print(C)
Si desea un código que se ejecute tanto en Python 2 como en Python 3, el seis el módulo lo tiene cubierto:
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)
Finalmente, si tiene una clase que desea tener una representación estática personalizada, el enfoque basado en clases anterior funciona muy bien. Pero si tiene varios, tendría que generar una metaclase similar a MC
para cada uno, y eso puede volverse tedioso. En ese caso, llevar su metaprogramación un paso más allá y crear una fábrica de metaclases hace que las cosas sean un poco más limpias:
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)
huellas dactilares:
Wahaha! Booyah! Gotcha!
La metaprogramación no es algo que generalmente necesite todos los días, pero cuando lo necesita, ¡realmente da en el clavo!
Solo agregando a todas las buenas respuestas, mi versión con decoración:
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)
salida estándar:
Wahaha!
Los lados negativos:
- no puedes declarar
C
sin superclase (noclass C:
) C
instancias serán instancias de alguna derivación extraña, por lo que probablemente sea una buena idea agregar un__repr__
para las instancias también.
rebeldes
Debido a que necesita una metaclase para hacer esto, pero necesita que la metaclase en sí tenga un parámetro, puede hacerlo con una metaclase que capture el nombre a través del alcance léxico.
Encuentro esto un poco más fácil de leer/seguir que algunas de las alternativas.
class type_: pass
def create_type(name):
# we do this so that we can print the class type out
# otherwise we must instantiate it to get a proper print out
class type_metaclass(type):
def __repr__(self):
return f'<{name}>'
class actual_type(type_, metaclass=type_metaclass):
pass
return actual_type
my_type = create_type('my_type')
print(my_type)
# prints "<my_type>"
david gilbertson
Otra respuesta, con:
- decorador
- tipos (por lo que mantiene autocompletar en IDE)
- funciona a partir de v3.10
import typing
class ClassReprMeta(type):
def __repr__(self):
attrs_str = ", ".join(
f"{key}={getattr(self, key)}"
for key in dir(self)
if not key.startswith("_")
)
return f"{self.__name__}({attrs_str})"
T = typing.TypeVar("T")
def printable_class(cls: T) -> T:
"""Decorator to make a class object printable"""
return ClassReprMeta(cls.__name__, cls.__bases__, dict(cls.__dict__))
@printable_class
class CONFIG:
FIRST = 1
SECOND = 2
print(CONFIG) # CONFIG(FIRST=1, SECOND=2)