Wie kann ich eine benutzerdefinierte Zeichenfolgendarstellung für eine Klasse selbst auswählen (nicht für Instanzen der Klasse)?

Lesezeit: 6 Minuten

Benutzeravatar von Björn Pollex
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.

Benutzeravatar von Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Implementieren __str__() oder __repr__() in der Metaklasse der Klasse.

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).

    – Himmel König

    1. April 2016 um 10:10 Uhr

  • @ThomasLeonard: stackoverflow.com/questions/39013249/metaclass-in-python3-5

    – Ignacio Vazquez-Abrams

    8. November 2017 um 18:34 Uhr

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


Benutzeravatar von user1767754
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.

Benutzerdefiniertes Vector3-Beispiel:

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 diesem Beispiel repr gibt wieder einen String zurück, der direkt konsumiert/ausgeführt werden kann, wohingegen str ist als Debug-Ausgabe nützlicher.

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

  • 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:

  1. Sie können nicht deklarieren C ohne Superklasse (Nr class C:)
  2. C Instanzen werden Instanzen einer seltsamen Ableitung sein, daher ist es wahrscheinlich eine gute Idee, a hinzuzufügen __repr__ auch für die Instanzen.

Benutzeravatar von Rebs
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>"

Benutzeravatar von David Gilbertson
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)

1443350cookie-checkWie kann ich eine benutzerdefinierte Zeichenfolgendarstellung für eine Klasse selbst auswählen (nicht für Instanzen der Klasse)?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy