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 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
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
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.
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.
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')
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.
http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Metaprogramming.html
Recommended Posts