Wie kann ich eine benutzerdefinierte Zeichenfolgendarstellung für eine Klasse selbst auswählen (nicht für Instanzen der Klasse)?
Lesezeit: 6 Minuten
Björn Pollex
Betrachten Sie diese Klasse:
class foo(object):
pass
Die Standard-String-Darstellung sieht etwa so aus:
>>> str(foo)
"<class '__main__.foo'>"
Wie kann ich diese Anzeige zu einer benutzerdefinierten Zeichenfolge machen?
Siehe Wie drucke ich Instanzen einer Klasse mit print()? für die entsprechende Frage zu Instanzen der Klasse.
Tatsächlich ist diese Frage wirklich ein Sonderfall davon – denn in Python sind Klassen selbst auch Objekte, die zu ihrer eigenen Klasse gehören – aber es ist nicht direkt ersichtlich, wie der Rat angewendet werden soll, da die Standard-“Klasse der Klassen” pre ist -definiert.
class MC(type):
def __repr__(self):
return 'Wahaha!'
class C(object):
__metaclass__ = MC
print(C)
Verwenden __str__ wenn Sie eine lesbare Zeichenfolge meinen, verwenden Sie __repr__ für eindeutige Darstellungen.
Bearbeiten: Python 3-Version
class MC(type):
def __repr__(self):
return 'Wahaha!'
class C(object, metaclass=MC):
pass
print(C)
Ich möchte eine Art Klassen-Decorator erstellen, damit ich einfach benutzerdefinierte Zeichenfolgendarstellungen für meine Klassen festlegen kann, ohne für jede von ihnen eine Metaklasse schreiben zu müssen. Ich bin mit den Metaklassen von Python nicht sehr vertraut, können Sie mir also irgendwelche Hinweise geben?
– Björn Pollex
8. Februar 2011 um 11:50 Uhr
Leider ist dies mit Klassendekorateuren nicht möglich; es muss gesetzt werden, wenn die Klasse definiert wird.
– Ignacio Vazquez-Abrams
8. Februar 2011 um 11:55 Uhr
@Space_C0wb0y: Sie könnten eine Zeichenfolge wie hinzufügen _representation an den Klassenkörper und return self._representation im __repr__() Methode der Metaklasse.
– Sven Marnach
8. Februar 2011 um 12:50 Uhr
@BjörnPollex Möglicherweise können Sie dies mit Decorator erreichen, aber ich würde erwarten, dass Sie mit vielen Feinheiten der Sprache zu kämpfen haben. Und selbst wenn Sie dies tun, müssen Sie die Metaklasse auf die eine oder andere Weise verwenden, da Sie die Standardeinstellung nicht verwenden möchten __repr__ zu repräsentieren C. Eine Alternative zu einem _representation Mitglied ist es, eine Metaklassen-Fabrik zu erstellen, die eine Metaklasse mit der richtigen erzeugt __repr__ (Dies könnte nett sein, wenn Sie dies häufig verwenden).
class foo(object):
def __str__(self):
return "representation"
def __unicode__(self):
return u"representation"
Dies ändert die Zeichenfolgendarstellung für instances der Klasse, nicht für die Klasse selbst.
– Stier
8. Februar 2011 um 11:39 Uhr
Entschuldigung, ich sehe den zweiten Teil Ihres Beitrags nicht. Verwenden Sie die obige Methode.
– Andrej Gubarew
8. Februar 2011 um 11:43 Uhr
@RobertSiemer Warum? Obwohl seine Antwort nicht speziell auf die Frage des OP abzielt, ist sie dennoch hilfreich. Es hat mir geholfen. Und auf den ersten Blick sehe ich keine Frage nach der Implementierung. Wahrscheinlich landen die Leute also zuerst auf dieser Seite.
– akinuri
21. Mai 2018 um 11:55 Uhr
Benutzer1767754
Wenn Sie sich entscheiden müssen __repr__ oder __str__ gehen Sie für die erste, als Standardimplementierung __str__ Anrufe __repr__ wenn es nicht definiert war.
In diesem Beispiel repr gibt wieder einen String zurück, der direkt konsumiert/ausgeführt werden kann, wohingegen str ist als Debug-Ausgabe nützlicher.
Während Ihr Punkt ungefähr __repr__ vs __str__ richtig ist, beantwortet dies nicht die eigentliche Frage, bei der es um Klassenobjekte geht, nicht um Instanzen.
– Björn Pollex
12. Januar 2018 um 9:39 Uhr
Danke für die Rückmeldung, habe das komplett übersehen. Lassen Sie mich meine Antwort überprüfen.
– Benutzer1767754
12. Januar 2018 um 9:48 Uhr
Ich denke, Sie Implementierungen für repr und str sind vertauscht.
– jspencer
9. März 2018 um 0:09 Uhr
Die anerkannte Antwort von Ignacio Vazquez-Abrams ist vollkommen richtig. Es stammt jedoch aus der Python 2-Generation. Ein Update für das jetzt aktuelle Python 3 wäre:
class MC(type):
def __repr__(self):
return 'Wahaha!'
class C(object, metaclass=MC):
pass
print(C)
Wenn Sie Code wünschen, der sowohl in Python 2 als auch in Python 3 ausgeführt wird, ist die sechs Modul hat Sie abgedeckt:
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)
Wenn Sie schließlich eine Klasse haben, für die Sie eine benutzerdefinierte statische Darstellung haben möchten, funktioniert der obige klassenbasierte Ansatz hervorragend. Aber wenn Sie mehrere haben, müssten Sie eine Metaklasse ähnlich wie generieren MC für jeden, und das kann ermüdend werden. Wenn Sie in diesem Fall Ihre Metaprogrammierung einen Schritt weiter gehen und eine Metaklassenfabrik erstellen, wird die Sache etwas sauberer:
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)
Drucke:
Wahaha! Booyah! Gotcha!
Metaprogrammierung braucht man im Allgemeinen nicht jeden Tag – aber wenn man sie braucht, ist sie genau das Richtige!
Fügen Sie einfach zu all den guten Antworten meine Version mit Dekoration hinzu:
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)
Standardausgabe:
Wahaha!
Die Schattenseiten:
Sie können nicht deklarieren C ohne Superklasse (Nr class C:)
C Instanzen werden Instanzen einer seltsamen Ableitung sein, daher ist es wahrscheinlich eine gute Idee, a hinzuzufügen __repr__ auch für die Instanzen.
Rebs
Da Sie dafür eine Metaklasse benötigen, aber die Metaklasse selbst einen Parameter haben muss, können Sie dies mit einer Metaklasse tun, die den Namen über den lexikalischen Gültigkeitsbereich erfasst.
Ich finde dies etwas einfacher zu lesen / zu befolgen als einige der Alternativen.
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
Noch eine Antwort mit:
Dekorateur
Typen (damit Sie die automatische Vervollständigung in IDEs beibehalten)
funktioniert ab 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)
14433500cookie-checkWie kann ich eine benutzerdefinierte Zeichenfolgendarstellung für eine Klasse selbst auswählen (nicht für Instanzen der Klasse)?yes