Apropos Python-Klassenattribute und Metaklassen

Problem

class Base:
    __BASE_PARAMS__ = ["base"]


class A(Base):
    __PARAMS__ = ["a"]

    
class B(Base):
    __PARAMS__ = ["b"]

Wie oben erwähnt, gibt es eine "Base" -Klasse, und die "A" - und "B" -Klassen erben von der "Base" -Klasse. Ich möchte ein Klassenattribut namens "ALL_PARAMS" hinzufügen, das die übergeordnete Klasse "BASE_PARAMS" und mein eigenes "PARAMS" in die "A" -Klasse und die "B" -Klasse integriert. Das heißt, wir möchten die folgenden Ergebnisse erhalten.

A.__ALL_PARAMS__
# ['base', 'a']

B.__ALL_PARAMS__
# ['base', 'b']

Bei normaler Implementierung würde dies folgendermaßen aussehen:

class Base:
    __BASE_PARAMS__ = ["base"]


class A(Base):
    __PARAMS__ = ["a"]
    __ALL_PARAMS__ = Base.__BASE_PARAMS__ + __PARAMS__

    
class B(Base):
    __PARAMS__ = ["b"]
    __ALL_PARAMS__ = Base.__BASE_PARAMS__ + __PARAMS__


A.__ALL_PARAMS__
# ['base', 'a']

B.__ALL_PARAMS__
# ['base', 'b']

Es gibt jedoch viele Klassen, die die Basisklasse erben, und es ist schwierig, das Klassenattribut ALL_PARAMS = Basis .__ BASE_PARAMS__ + PARAMS, das nicht mit der Verarbeitung zusammenhängt, in alle geerbten Klassen zu schreiben.

Gibt es eine einfache Möglichkeit, dieses Problem zu lösen, ohne dass die geerbte Klasse bedeutungslos schreibt? Ich möchte, dass du darüber nachdenkst.

Metaprogrammierung

Metaprogrammierung ist eine Technik zum programmgesteuerten Definieren eines Programms.

Ich werde hier nicht über die Grundlagen der Metaprogrammierung sprechen. Wenn Sie mehr über Metaprogrammierung erfahren möchten, lesen Sie bitte die folgenden Artikel.

Metaprogrammierung mit Python \ _ \ _ New \ _ \ _ und \ _ \ _ init \ _ \ _ und Metaklasse Übergeben Sie in der Python-Metaklasse den Vergleichsoperator als Zeichenfolge an die ORM-Methode, um die where-Klausel zu erstellen.

Mit Metaklassen können wir nun das obige Problem wie folgt lösen.

Metaklasse für Python3


class MetaClass(type):    
    def __init__(cls, name, bases, attribute):
        super(MetaClass, cls).__init__(name, bases, attribute)
        cls.__ALL_PARAMS__ = cls.__BASE_PARAMS__ + getattr(cls, "__PARAMS__", [])


class Base(metaclass=MetaClass):
    __BASE_PARAMS__ = ["base"]


class A(Base):
    __PARAMS__ = ["a"]

    
class B(Base):
    __PARAMS__ = ["b"]


A.__ALL_PARAMS__
# ['base', 'a']

B.__ALL_PARAMS__
# ['base', 'b']

Weisen Sie für Python2 dem speziellen Attribut "metaclass" eine Metaklasse zu.

Für Python 2


class Base(object):
    __metaclass__ = MetaClass

Verschiedene Meta-Programmierungen

Sie können die Sprache sogar mit Metaprogrammierung erweitern. Endgültige Klassen in Java sind in Python nicht vorhanden, aber mit Metaklassen können Sie ähnliche Funktionen definieren.

final_metaclass.py


class FinalMetaClass(type):
    def __init__(cls, name, bases, namespace):
        super(FinalMetaClass, cls).__init__(name, bases, namespace)
        for _class in bases:
            if isinstance(_class, FinalMetaClass):
                raise TypeError()


class A:
    pass


class B(A, metaclass=FinalMetaClass):
    pass


# Error!!
class C(B):
    pass

\ _ \ _ Init \ _ \ _ oder \ _ \ _ new \ _ \ _?

Sollte ich in einer Metaklasse, die den Typ erbt, "init" oder "new" verwenden? Es ist im Grunde eine Frage des Geschmacks, und es spielt keine Rolle, welche. "New" hat jedoch einen höheren Freiheitsgrad, z. B. die Möglichkeit, "slots" neu zu schreiben.

Neue spezielle Methode \ _ \ _ vorbereiten \ _ \ _

Eine spezielle Methode "prepare" wurde aus Python3 hinzugefügt. Normalerweise ist __dict__ ein Diktattyp, dessen Reihenfolge nicht garantiert ist (was in Python3.6 passiert ist), und Sie können dies mit __prepare__ steuern.

Das Folgende ist ein Auszug aus Python-Dokumentation.

import collections

class OrderedClass(type):

    @classmethod
    def __prepare__(metacls, name, bases, **kwds):
        return collections.OrderedDict()

    def __new__(metacls, name, bases, namespace, **kwds):
        cls = type.__new__(metacls, name, bases, dict(namespace))
        cls.members = tuple(namespace)
        return cls

class A(metaclass=OrderedClass):
    def one(self): pass
    def two(self): pass
    def three(self): pass
    def four(self): pass
    
A.members
# ('__module__', '__qualname__', 'one', 'two', 'three', 'four')

Es kann bestätigt werden, dass die Aufzählung der Klassenmitglieder in der Reihenfolge der Definition erfolgt.

Vererbung der Metaklasse

class MetaA(type):
    def __new__(mcls, *args, **kwargs):
        cls = type.__new__(mcls, *args, **kwargs)
        cls.MetaA = "Yes"
        return cls


class MetaB(type):
    def __new__(mcls, *args, **kwargs):
        cls = type.__new__(mcls, *args, **kwargs)
        cls.MetaB = "Yes"
        return cls        


class A(metaclass=MetaA):
    pass


class B(A, metaclass=MetaB):
    pass

Wenn ich versuche, die Klasse B zu definieren, wird der folgende Fehler angezeigt:

TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

Es können nicht nur Unterklassen vom Typ "an" Metaklasse "übergeben werden. Alles, was Callable ist und einem bestimmten Argument entspricht, kann verwendet werden. In diesem Fall kann eine Funktion übergeben werden, um die Diamantvererbung zu beseitigen.

class MetaA(type):
    def __new__(mcls, *args, **kwargs):
        cls = type.__new__(mcls, *args, **kwargs)
        cls.MetaA = "Yes"
        return cls


class A(metaclass=MetaA):
    pass


def MetaB(mcls, *args, **kwargs):
    cls = type(mcls, *args, **kwargs)
    cls.MetaB = "Yes"
    return cls
    

class B(A, metaclass=MetaAB):
    pass

B.MetaA, B.MetaB
# ('Yes', 'Yes')

Verwendung der Metaprogrammierung

Die Metaprogrammierung ist leistungsfähig genug, um Sprachspezifikationen zu ändern. Daher kann die starke Verwendung von Metaprogrammierung zu Verwirrung bei der Teamprogrammierung führen.

Wenn das Design solide ist, sollte die Metaprogrammierung grundsätzlich selten ins Spiel kommen. In Ausnahmefällen ist es beim Generieren des geerbten Klassenattributs häufig möglich, mithilfe einer Metaklasse klar zu schreiben.

Referenz

http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Metaprogramming.html

Recommended Posts

Apropos Python-Klassenattribute und Metaklassen
Über Python, Klasse
Apropos alte und neue Klassen in Python
Über Klasse und Instanz
Python: Klassen- und Instanzvariablen
Informationen zu Python-Objekten und -Klassen
Informationen zu Python-Variablen und -Objekten
Über Python, len () und randint ()
Informationen zu Python-Datums- und Zeitzone
Python-Klassen- und Instanzvariablen
Über Python und reguläre Ausdrücke
Informationen zu Python- und Betriebssystemoperationen
Python # Über Referenz und Kopie
Über Python sort () und reverse ()
Informationen zur Installation der Serien Pwntools und Python2
Perl-Objekt und Python-Klasse Teil 2.
Python-Klassendefinitionen und Instanzbehandlung
Über Python-Diktat und sortierte Funktionen
[Python] Über Executor und zukünftige Klassen
Über Python, aus und importieren, als
(Persönliche Notizen) Python-Metaklassen und Metaprogrammierung
Über _ und __
[Python] Unterschied zwischen Klassenmethode und statischer Methode
Eine Geschichte über Python Pop und Append
Über Python-Slices
Über die Einschlussnotation von Python
[Python] -Klasse, Instanz
"Kanrika" die Python-Klasse
Über Python tqdm.
Über die Python-Ausbeute
Unterschied zwischen Variablen und Selbst. Variablen in der [Python] -Klasse
Ich habe eine Klasse in Python3 und Java geschrieben
Informationen zur Python-Vererbung
Über Python, range ()
Probleme mit pseudo-privaten Python-Variablen und Klassenvererbung
Über Python Decorator
Python-Klasse, Instanz
Eine Geschichte über das Ändern von Python und das Hinzufügen von Funktionen
[Python] Erfahren Sie mehr über asynchrone Programmierung und Ereignisschleifen
[Python] Über Multi-Prozess
Über flache und tiefe Kopien von Python / Ruby
[Python] Klassentyp und Verwendung des datetime-Moduls
# Python-Grundlagen (Klasse)
Erste Schritte mit Python3 # 2 Erfahren Sie mehr über Typen und Variablen
[Python] Konvertieren Sie Allzweckcontainer und Klasse ineinander
Informationen zum Erstellen und Ändern von benutzerdefinierten Designs für Python IDLE
Beispiel für das Abrufen des Modulnamens und des Klassennamens in Python
[Python] Kapitel 01-02 Über Python (Ausführung und Installation der Entwicklungsumgebung)
[Python] Komprimieren und dekomprimieren
Über Python für Schleife
Python-Syslog-Wrapper-Klasse
Python-Klasse (Python-Lernnotiz ⑦)
Python- und Numpy-Tipps
[Python] Pip und Wheel
Fallklasse in Python
Python Iterator und Generator
[Python] Klassenvererbung (super)