class
, verwendet eine dynamische Definition eine Inline-Klasse type
.
Geben Sie metaclass ein
Die Typklasse wird häufig verwendet, um den Typ eines Objekts abzurufen. Zum Beispiel so:
h = "hello"
type(h)
<class 'str'>
Aber es hat andere Verwendungszwecke. Es kann neue Typen initialisieren.Wie Sie wissen, ist alles in Python ein Objekt. Daraus folgt, dass alle Definitionen Typen haben, einschließlich Klassen und Objekte. Zum Beispiel:
class A:
pass
type(A)
<class 'type'>
Es ist möglicherweise nicht ganz klar, warum einer Klasse
type
im Gegensatz zu ihren Instanzen ein Klassentyp zugewiesen wird :
a = A()
type(a)
<class '__main__.A'>
Dem Objekt
a
wird eine Klasse als Typ zugewiesen. Auf diese Weise behandelt der Interpreter das Objekt als Instanz der Klasse. Die Klasse selbst hat einen Klassentyp, type
da sie ihn von der Basisklasse erbt object
:
A.__bases__
(<class 'object'>,)
Klassentyp
object
:
type(object)
<class 'type'>
Die Klasse wird
object
standardmäßig von allen Klassen geerbt, dh:
class A(object):
pass
Gleich wie:
class A:
pass
Die zu definierende Klasse erbt die Basisklasse als Typ. Dies erklärt jedoch nicht, warum die Basisklasse vom Klassentyp
object
ist type
. Der Punkt ist, es type
ist eine Metaklasse. Wie Sie bereits wissen, erben alle Klassen von der Basisklasse object
, die vom Typ Metaklasse ist type
. Daher haben auch alle Klassen diesen Typ, einschließlich der Metaklasse selbst type
:
type(type)
<class 'type'>
Dies ist der "Endpunkt der Eingabe" in Python. Die Typvererbungskette ist für die Klasse geschlossen
type
. Die Metaklasse type
dient als Basis für alle Klassen in Python. Dies lässt sich leicht überprüfen:
builtins = [list, dict, tuple]
for obj in builtins:
type(obj)
<class 'type'>
<class 'type'>
<class 'type'>
Eine Klasse ist ein abstrakter Datentyp, und Instanzen davon haben eine Klassenreferenz als Typ.
Initialisieren neuer Typen mit der Typklasse
Bei der Überprüfung von Typen wird die Klasse
type
mit einem einzigen Argument initialisiert:
type(object) -> type
Dabei wird der Typ des Objekts zurückgegeben. Die Klasse implementiert jedoch eine andere Initialisierungsmethode mit drei Argumenten, die einen neuen Typ zurückgibt:
type(name, bases, dict) -> new type
Geben Sie die Initialisierungsparameter ein
name
Eine Zeichenfolge, die den Namen der neuen Klasse (Typ) definiert.bases
Ein Tupel von Basisklassen (Klassen, die die neue Klasse erben wird).dict
Wörterbuch mit den Attributen der zukünftigen Klasse. Normalerweise mit Zeichenfolgen in Schlüsseln und aufrufbaren Typen in Werten.
Dynamische Klassendefinition
Wir initialisieren die Klasse des neuen Typs, indem wir alle erforderlichen Argumente bereitstellen und aufrufen:
MyClass = type("MyClass", (object, ), dict())
MyClass
<class '__main__.MyClass'>
Sie können wie gewohnt mit der neuen Klasse arbeiten:
m = MyClass()
m
<__main__.MyClass object at 0x7f8b1d69acc0>
Darüber hinaus entspricht die Methode der üblichen Klassendefinition:
class MyClass:
pass
Dynamische Definition von Klassenattributen
Eine leere Klasse hat wenig Sinn, daher stellt sich die Frage: Wie werden Attribute und Methoden hinzugefügt?
Betrachten Sie zur Beantwortung dieser Frage den anfänglichen Initialisierungscode:
MyClass = type(“MyClass”, (object, ), dict())
In der Regel werden der Klasse in der Initialisierungsphase Attribute als drittes Argument hinzugefügt - das Wörterbuch. Sie können Attributnamen und Werte im Wörterbuch angeben. Zum Beispiel könnte es eine Variable sein:
MyClass = type(“MyClass”, (object, ), dict(foo=“bar”)
m = MyClass()
m.foo
'bar'
Dynamische Methodendefinition
Aufrufbare Objekte können auch an das Wörterbuch übergeben werden, z. B. Methoden:
def foo(self):
return “bar”
MyClass = type(“MyClass”, (object, ), dict(foo=foo))
m = MyClass()
m.foo
'bar'
Diese Methode hat einen wesentlichen Nachteil - die Notwendigkeit, die Methode statisch zu definieren (ich denke, dass dies im Zusammenhang mit Metaprogrammierungsaufgaben als Nachteil angesehen werden kann). Außerdem
self
sieht die Definition einer Methode mit einem Parameter außerhalb des Klassenkörpers seltsam aus. Kehren wir also zur dynamischen Initialisierung einer Klasse ohne Attribute zurück:
MyClass = type(“MyClass”, (object, ), dict())
Nach dem Initialisieren einer leeren Klasse können Sie ihr dynamisch Methoden hinzufügen, dh ohne explizite statische Definition:
code = compile('def foo(self): print(“bar”)', "<string>", "exec")
compile
Ist eine integrierte Funktion, die Quellcode in ein Objekt kompiliert. Der Code kann von Funktionen exec()
oder ausgeführt werden eval()
.
Funktionsparameter kompilieren
- Quelle
Quellcode kann ein Link zu einem Modul sein. - Dateiname
Der Name der Datei, in die das Objekt kompiliert wird. - Modus
Wenn angegeben"exec"
, kompiliert die Funktion den Quellcode in ein Modul.
Das Ergebnis der Arbeit
compile
ist ein Klassenobjekt code
:
type(code)
<class 'code'>
Das Objekt
code
muss in eine Methode konvertiert werden. Da eine Methode eine Funktion ist, konvertieren wir zunächst ein Klassenobjekt code
in ein Klassenobjekt function
. Importieren Sie dazu das Modul types
:
from types import FunctionType, MethodType
Ich werde es importieren
MethodType
, da ich es später benötigen werde, um die Funktion in eine Klassenmethode zu konvertieren.
function = FunctionType(code.co_consts[0], globals(), “foo”)
Parameter der FunctionType-Initialisierungsmethode
code
Klassenobjektcode
.code.co_consts[0]
Ist ein Aufruf einesco_consts
Klassendeskriptorscode
, bei dem es sich um ein Tupel mit Konstanten im Code des Objekts handelt. Stellen Sie sich ein Objektcode
als Modul mit einer einzelnen Funktion vor, die wir als Klassenmethode hinzufügen möchten.0
Ist sein Index, da es die einzige Konstante im Modul ist.globals()
Wörterbuch der globalen Variablen.name
Optionaler Parameter, der den Namen der Funktion angibt.
Das Ergebnis ist eine Funktion:
function
<function foo at 0x7fc79cb5ed90>
type(function)
<class 'function'>
Als Nächstes müssen Sie diese Funktion als Klassenmethode hinzufügen
MyClass
:
MyClass.foo = MethodType(function, MyClass)
Ein ziemlich einfacher Ausdruck, der unsere Funktion einer Klassenmethode zuweist
MyClass
.
m = MyClass()
m.foo()
bar
Warnung
In 99% der Fälle kommen Sie mit statischen Klassendefinitionen aus. Das Konzept der Metaprogrammierung ist jedoch gut darin, die Interna von Python aufzudecken. Höchstwahrscheinlich wird es für Sie schwierig sein, die Anwendung der hier beschriebenen Methoden zu finden, obwohl es in meiner Praxis einen solchen Fall gab.
Haben Sie mit dynamischen Objekten gearbeitet? Vielleicht in anderen Sprachen?