Was sind Metaklassen in Python und wofür verwenden wir sie?
quelle
Was sind Metaklassen in Python und wofür verwenden wir sie?
Eine Metaklasse ist die Klasse einer Klasse. Eine Klasse definiert, wie sich eine Instanz der Klasse (dh ein Objekt) verhält, während eine Metaklasse definiert, wie sich eine Klasse verhält. Eine Klasse ist eine Instanz einer Metaklasse.
Während Sie in Python beliebige Callables für Metaklassen verwenden können (wie Jerub zeigt), besteht der bessere Ansatz darin, es selbst zu einer tatsächlichen Klasse zu machen. type
ist die übliche Metaklasse in Python. type
ist selbst eine Klasse und es ist ein eigener Typ. Sie werden nicht in der Lage sein, so etwas wie type
rein in Python neu zu erstellen , aber Python betrügt ein wenig. Um Ihre eigene Metaklasse in Python zu erstellen, möchten Sie wirklich nur eine Unterklasse erstellen type
.
Eine Metaklasse wird am häufigsten als Klassenfabrik verwendet. Wenn Sie ein Objekt durch Aufrufen der Klasse erstellen, erstellt Python eine neue Klasse (wenn die Anweisung 'class' ausgeführt wird), indem Sie die Metaklasse aufrufen. In Kombination mit den Normalen __init__
und __new__
Methoden können Sie mit Metaklassen beim Erstellen einer Klasse zusätzliche Dinge tun, z. B. die neue Klasse bei einer Registrierung registrieren oder die Klasse durch etwas ganz anderes ersetzen.
Wenn die class
Anweisung ausgeführt wird, führt Python zuerst den Hauptteil der class
Anweisung als normalen Codeblock aus. Der resultierende Namespace (ein Diktat) enthält die Attribute der zukünftigen Klasse. Die Metaklasse wird bestimmt, indem die Basisklassen der zukünftigen Klasse (Metaklassen werden vererbt), das __metaclass__
Attribut der zukünftigen Klasse (falls vorhanden) oder die __metaclass__
globale Variable betrachtet werden. Die Metaklasse wird dann mit dem Namen, den Basen und den Attributen der Klasse aufgerufen, um sie zu instanziieren.
Metaklassen definieren jedoch tatsächlich den Typ einer Klasse, nicht nur eine Fabrik dafür, sodass Sie viel mehr damit tun können. Sie können beispielsweise normale Methoden für die Metaklasse definieren. Diese Metaklassenmethoden sind insofern wie Klassenmethoden, als sie für die Klasse ohne Instanz aufgerufen werden können, aber sie sind auch nicht wie Klassenmethoden, da sie nicht für eine Instanz der Klasse aufgerufen werden können. type.__subclasses__()
ist ein Beispiel für eine Methode für die type
Metaklasse. Sie können auch die normale ‚Magie‘ Methoden definieren, wie __add__
, __iter__
und __getattr__
zu implementieren oder zu ändern , wie die Klasse verhält.
Hier ist ein aggregiertes Beispiel für die einzelnen Teile:
def make_hook(f):
"""Decorator to turn 'foo' method into '__foo__'"""
f.is_hook = 1
return f
class MyType(type):
def __new__(mcls, name, bases, attrs):
if name.startswith('None'):
return None
# Go over attributes and see if they should be renamed.
newattrs = {}
for attrname, attrvalue in attrs.iteritems():
if getattr(attrvalue, 'is_hook', 0):
newattrs['__%s__' % attrname] = attrvalue
else:
newattrs[attrname] = attrvalue
return super(MyType, mcls).__new__(mcls, name, bases, newattrs)
def __init__(self, name, bases, attrs):
super(MyType, self).__init__(name, bases, attrs)
# classregistry.register(self, self.interfaces)
print "Would register class %s now." % self
def __add__(self, other):
class AutoClass(self, other):
pass
return AutoClass
# Alternatively, to autogenerate the classname as well as the class:
# return type(self.__name__ + other.__name__, (self, other), {})
def unregister(self):
# classregistry.unregister(self)
print "Would unregister class %s now." % self
class MyObject:
__metaclass__ = MyType
class NoneSample(MyObject):
pass
# Will print "NoneType None"
print type(NoneSample), repr(NoneSample)
class Example(MyObject):
def __init__(self, value):
self.value = value
@make_hook
def add(self, other):
return self.__class__(self.value + other.value)
# Will unregister the class
Example.unregister()
inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()
print inst + inst
class Sibling(MyObject):
pass
ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__
class A(type):pass<NEWLINE>class B(type,metaclass=A):pass<NEWLINE>b.__class__ = b
__metaclass__
dies in Python 3 nicht unterstützt wird. In Python 3class MyObject(metaclass=MyType)
finden Sie unter python.org/dev/peps/pep-3115 und in der folgenden Antwort.Klassen als Objekte
Bevor Sie Metaklassen verstehen, müssen Sie Klassen in Python beherrschen. Und Python hat eine sehr eigenartige Vorstellung davon, was Klassen sind, die aus der Smalltalk-Sprache entlehnt sind.
In den meisten Sprachen sind Klassen nur Codeteile, die beschreiben, wie ein Objekt erstellt wird. Das stimmt auch in Python:
Aber Klassen sind mehr als das in Python. Klassen sind auch Objekte.
Ja, Objekte.
Sobald Sie das Schlüsselwort verwenden
class
, führt Python es aus und erstellt ein OBJEKT. Die AnleitungErstellt im Speicher ein Objekt mit dem Namen "ObjectCreator".
Dieses Objekt (die Klasse) kann selbst Objekte (die Instanzen) erstellen, und deshalb ist es eine Klasse .
Trotzdem ist es ein Objekt und daher:
z.B:
Klassen dynamisch erstellen
Da Klassen Objekte sind, können Sie sie wie jedes Objekt im laufenden Betrieb erstellen.
Zunächst können Sie eine Klasse in einer Funktion erstellen, indem Sie
class
:Aber es ist nicht so dynamisch, da Sie immer noch die ganze Klasse selbst schreiben müssen.
Da Klassen Objekte sind, müssen sie von etwas generiert werden.
Wenn Sie das
class
Schlüsselwort verwenden, erstellt Python dieses Objekt automatisch. Aber wie bei den meisten Dingen in Python gibt es eine Möglichkeit, dies manuell zu tun.Erinnerst
type
du dich an die Funktion ? Die gute alte Funktion, mit der Sie wissen, welcher Typ ein Objekt ist:Nun,
type
hat eine ganz andere Fähigkeit, es kann auch Klassen im laufenden Betrieb erstellen.type
kann die Beschreibung einer Klasse als Parameter verwenden und eine Klasse zurückgeben.(Ich weiß, es ist albern, dass dieselbe Funktion je nach den von Ihnen übergebenen Parametern zwei völlig unterschiedliche Verwendungszwecke haben kann. Dies ist ein Problem aufgrund der Abwärtskompatibilität in Python.)
type
funktioniert so:Wo:
name
: Name der Klassebases
: Tupel der übergeordneten Klasse (für die Vererbung kann leer sein)attrs
: Wörterbuch mit Attributnamen und -wertenz.B:
kann manuell auf folgende Weise erstellt werden:
Sie werden feststellen, dass wir "MyShinyClass" als Namen der Klasse und als Variable für die Klassenreferenz verwenden. Sie können unterschiedlich sein, aber es gibt keinen Grund, die Dinge zu komplizieren.
type
Akzeptiert ein Wörterbuch, um die Attribute der Klasse zu definieren. Damit:Kann übersetzt werden in:
Und als normale Klasse verwendet:
Und natürlich können Sie davon erben, also:
wäre:
Schließlich möchten Sie Ihrer Klasse Methoden hinzufügen. Definieren Sie einfach eine Funktion mit der richtigen Signatur und weisen Sie sie als Attribut zu.
Und Sie können nach dem dynamischen Erstellen der Klasse noch mehr Methoden hinzufügen, genau wie Sie einem normal erstellten Klassenobjekt Methoden hinzufügen.
Sie sehen, wohin wir gehen: In Python sind Klassen Objekte, und Sie können eine Klasse im laufenden Betrieb dynamisch erstellen.
Dies ist, was Python tut, wenn Sie das Schlüsselwort verwenden
class
, und dies unter Verwendung einer Metaklasse.Was sind Metaklassen (endlich)
Metaklassen sind das Zeug, das Klassen erstellt.
Sie definieren Klassen, um Objekte zu erstellen, oder?
Wir haben jedoch gelernt, dass Python-Klassen Objekte sind.
Nun, Metaklassen schaffen diese Objekte. Sie sind die Klassen der Klassen, Sie können sie sich so vorstellen:
Sie haben gesehen, dass
type
Sie so etwas tun können:Das liegt daran, dass die Funktion
type
tatsächlich eine Metaklasse ist.type
ist die Metaklasse, mit der Python alle Klassen hinter den Kulissen erstellt.Jetzt fragen Sie sich, warum zum Teufel es in Kleinbuchstaben geschrieben ist und nicht
Type
?Nun, ich denke, es ist eine Frage der Konsistenz mit
str
der Klasse, die Zeichenfolgenobjekte erstellt, undint
der Klasse, die Ganzzahlobjekte erstellt.type
ist nur die Klasse, die Klassenobjekte erstellt.Sie sehen das, indem Sie das
__class__
Attribut überprüfen .Alles, und ich meine alles, ist ein Objekt in Python. Dazu gehören Ints, Strings, Funktionen und Klassen. Alle von ihnen sind Objekte. Und alle wurden aus einer Klasse erstellt:
Was ist das
__class__
von irgendjemandem__class__
?Eine Metaklasse ist also genau das, was Klassenobjekte erstellt.
Sie können es eine "Klassenfabrik" nennen, wenn Sie möchten.
type
ist die integrierte Metaklasse, die Python verwendet, aber Sie können natürlich Ihre eigene Metaklasse erstellen.Das
__metaclass__
AttributIn Python 2 können Sie
__metaclass__
beim Schreiben einer Klasse ein Attribut hinzufügen (Informationen zur Python 3-Syntax finden Sie im nächsten Abschnitt):In diesem Fall verwendet Python die Metaklasse, um die Klasse zu erstellen
Foo
.Vorsicht, es ist schwierig.
Sie schreiben
class Foo(object)
zuerst, aber das KlassenobjektFoo
wird noch nicht im Speicher erstellt.Python sucht
__metaclass__
in der Klassendefinition. Wenn es es findet, wird es verwendet, um die Objektklasse zu erstellenFoo
. Wenn dies nicht der Fall ist, wirdtype
die Klasse erstellt.Lies das mehrmals.
Wenn Sie das tun:
Python macht Folgendes:
Gibt es ein
__metaclass__
Attribut inFoo
?Wenn ja, erstellen Sie im Speicher ein Klassenobjekt (ich sagte ein Klassenobjekt, bleiben Sie hier bei mir), mit dem Namen,
Foo
indem Sie das verwenden, was sich darin befindet__metaclass__
.Wenn Python nicht finden kann, sucht
__metaclass__
es__metaclass__
auf MODULE-Ebene nach einem und versucht, dasselbe zu tun (jedoch nur für Klassen, die nichts erben, im Grunde alte Klassen).Wenn es dann überhaupt keine findet
__metaclass__
, verwendet es dieBar
eigene Metaklasse (die erste übergeordnete) (möglicherweise die Standardeinstellungtype
), um das Klassenobjekt zu erstellen.Achten Sie hier darauf, dass das
__metaclass__
Attribut nicht vererbt wird, sondern die Metaklasse von parent (Bar.__class__
). WennBar
ein__metaclass__
Attribut verwendet wird, dasBar
mittype()
(und nichttype.__new__()
) erstellt wurde, erben die Unterklassen dieses Verhalten nicht.Die große Frage ist nun, was können Sie eingeben
__metaclass__
?Die Antwort lautet: etwas, das eine Klasse erstellen kann.
Und was kann eine Klasse schaffen?
type
oder irgendetwas, das es unterordnet oder verwendet.Metaklassen in Python 3
Die Syntax zum Festlegen der Metaklasse wurde in Python 3 geändert:
Das heißt, das
__metaclass__
Attribut wird nicht mehr zugunsten eines Schlüsselwortarguments in der Liste der Basisklassen verwendet.Das Verhalten von Metaklassen bleibt jedoch weitgehend gleich .
Eine Sache, die Metaklassen in Python 3 hinzugefügt wurde, ist, dass Sie Attribute auch als Schlüsselwortargumente an eine Metaklasse übergeben können, wie folgt:
Lesen Sie den folgenden Abschnitt, um zu erfahren, wie Python damit umgeht.
Benutzerdefinierte Metaklassen
Der Hauptzweck einer Metaklasse besteht darin, die Klasse beim Erstellen automatisch zu ändern.
Dies tun Sie normalerweise für APIs, bei denen Sie Klassen erstellen möchten, die dem aktuellen Kontext entsprechen.
Stellen Sie sich ein dummes Beispiel vor, in dem Sie entscheiden, dass alle Klassen in Ihrem Modul ihre Attribute in Großbuchstaben schreiben sollen. Es gibt verschiedene Möglichkeiten, dies zu tun, aber eine Möglichkeit besteht darin,
__metaclass__
auf Modulebene festzulegen.Auf diese Weise werden alle Klassen dieses Moduls mit dieser Metaklasse erstellt, und wir müssen der Metaklasse lediglich mitteilen, dass alle Attribute in Großbuchstaben umgewandelt werden sollen.
Glücklicherweise
__metaclass__
kann es tatsächlich jede aufrufbare Klasse sein, es muss keine formelle Klasse sein (ich weiß, etwas mit 'Klasse' im Namen muss keine Klasse sein, mach eine Figur ... aber es ist hilfreich).Wir beginnen also mit einem einfachen Beispiel, indem wir eine Funktion verwenden.
Lass uns nachsehen:
Lassen Sie uns jetzt genau das Gleiche tun, aber eine echte Klasse für eine Metaklasse verwenden:
Lassen Sie uns das Obige umschreiben, aber mit kürzeren und realistischeren Variablennamen, jetzt wo wir wissen, was sie bedeuten:
Möglicherweise haben Sie das zusätzliche Argument bemerkt
cls
. Es gibt nichts Besonderes:__new__
Empfängt immer die Klasse, in der es definiert ist, als ersten Parameter. Genau wieself
bei normalen Methoden, die die Instanz als ersten Parameter erhalten, oder der definierenden Klasse für Klassenmethoden.Aber das ist nicht richtig OOP. Wir rufen
type
direkt an und überschreiben oder rufen nicht die der Eltern an__new__
. Lassen Sie uns das stattdessen tun:Wir können es noch sauberer machen, indem wir verwenden
super
, was die Vererbung erleichtert (denn ja, Sie können Metaklassen haben, die von Metaklassen erben, vom Typ erben):Oh, und in Python 3, wenn Sie diesen Aufruf mit Schlüsselwortargumenten wie folgt ausführen:
Dies wird in der Metaklasse übersetzt, um es zu verwenden:
Das ist es. Metaklassen haben wirklich nichts mehr zu bieten.
Der Grund für die Komplexität des Codes bei der Verwendung von Metaklassen liegt nicht in Metaklassen, sondern darin, dass Sie normalerweise Metaklassen verwenden, um verdrehte Dinge zu erledigen, die auf Introspektion, Manipulation der Vererbung, Variablen wie
__dict__
usw. beruhen .In der Tat sind Metaklassen besonders nützlich, um schwarze Magie und damit komplizierte Dinge zu tun. Aber für sich sind sie einfach:
Warum sollten Sie Metaklassenklassen anstelle von Funktionen verwenden?
Da
__metaclass__
jede aufrufbare annehmen können, warum sollten Sie eine Klasse verwenden , da ist es offensichtlich kompliziert mehr?Dafür gibt es mehrere Gründe:
UpperAttrMetaclass(type)
, wissen Sie, was folgen wird__new__
,__init__
und__call__
. So können Sie verschiedene Dinge tun. Selbst wenn Sie normalerweise alles erledigen können__new__
, sind einige Leute einfach komfortabler__init__
.Warum sollten Sie Metaklassen verwenden?
Nun die große Frage. Warum sollten Sie eine obskure fehleranfällige Funktion verwenden?
Normalerweise tun Sie das nicht:
Python Guru Tim Peters
Der Hauptanwendungsfall für eine Metaklasse ist das Erstellen einer API. Ein typisches Beispiel hierfür ist das Django ORM. Hier können Sie Folgendes definieren:
Aber wenn Sie dies tun:
Es wird kein
IntegerField
Objekt zurückgegeben. Es gibt ein zurückint
und kann es sogar direkt aus der Datenbank übernehmen.Dies ist möglich, weil
models.Model
definiert__metaclass__
und es etwas Magie verwendet, diePerson
das soeben definierte mit einfachen Anweisungen in einen komplexen Hook für ein Datenbankfeld verwandelt .Django lässt etwas Komplexes einfach aussehen, indem es eine einfache API verfügbar macht und Metaklassen verwendet. Dabei wird Code aus dieser API neu erstellt, um die eigentliche Aufgabe hinter den Kulissen zu erledigen.
Das letzte Wort
Erstens wissen Sie, dass Klassen Objekte sind, die Instanzen erstellen können.
In der Tat sind Klassen selbst Instanzen. Von Metaklassen.
Alles ist ein Objekt in Python, und alle sind entweder Instanzen von Klassen oder Instanzen von Metaklassen.
Außer
type
.type
ist eigentlich eine eigene Metaklasse. Dies ist nichts, was Sie in reinem Python reproduzieren könnten, und dies geschieht durch ein wenig Betrug auf der Implementierungsebene.Zweitens sind Metaklassen kompliziert. Möglicherweise möchten Sie sie nicht für sehr einfache Klassenänderungen verwenden. Sie können Klassen mit zwei verschiedenen Techniken ändern:
In 99% der Fälle, in denen Sie eine Klassenänderung benötigen, ist es besser, diese zu verwenden.
In 98% der Fälle müssen Sie jedoch überhaupt keine Klassenänderungen vornehmen.
quelle
models.Model
nicht verwendet__metaclass__
, sondernclass Model(metaclass=ModelBase):
auf eineModelBase
Klasse verweist , die dann die oben erwähnte Metaklassenmagie ausführt. Guter Eintrag! Hier ist die Django-Quelle: github.com/django/django/blob/master/django/db/models/…__metaclass__
Attribut nicht vererbt wird, sondern die Metaklasse von parent (Bar.__class__
). WennBar
ein__metaclass__
Attribut verwendet wird, dasBar
mittype()
(und nichttype.__new__()
) erstellt wurde, erben die Unterklassen dieses Verhalten nicht. >> - Könnten Sie / jemand diese Passage etwas näher erläutern?Now you wonder why the heck is it written in lowercase, and not Type?
- Nun, weil es in C implementiert ist - es ist der gleiche Grund, warum defaultdict in Kleinbuchstaben geschrieben ist, während OrderedDict (in Python 2) normal ist. CamelCaseBeachten Sie, dass diese Antwort für Python 2.x gilt, wie es 2008 geschrieben wurde. Metaklassen unterscheiden sich in 3.x geringfügig.
Metaklassen sind die geheime Sauce, mit der „Klasse“ funktioniert. Die Standardmetaklasse für ein neues Stilobjekt heißt "Typ".
Metaklassen benötigen 3 Argumente. ' Name ', ' Basen ' und ' Diktat '
Hier beginnt das Geheimnis. Suchen Sie in dieser Beispielklassendefinition, woher Name, Basen und das Diktat stammen.
Definieren wir eine Metaklasse, die zeigt, wie ' class: ' sie aufruft.
Und jetzt, ein Beispiel, das tatsächlich etwas bedeutet, werden die Variablen in der Liste "Attribute" automatisch für die Klasse festgelegt und auf "Keine" gesetzt.
Beachten Sie, dass das magische Verhalten,
Initialised
das durch die Metaklasseinit_attributes
entsteht, nicht an eine Unterklasse von übergeben wirdInitialised
.Hier ist ein noch konkreteres Beispiel, das zeigt, wie Sie 'Typ' in eine Unterklasse umwandeln können, um eine Metaklasse zu erstellen, die beim Erstellen der Klasse eine Aktion ausführt. Das ist ziemlich schwierig:
quelle
Andere haben erklärt, wie Metaklassen funktionieren und wie sie in das Python-Typsystem passen. Hier ist ein Beispiel dafür, wofür sie verwendet werden können. In einem von mir geschriebenen Testframework wollte ich die Reihenfolge verfolgen, in der Klassen definiert wurden, damit ich sie später in dieser Reihenfolge instanziieren kann. Ich fand es am einfachsten, dies mit einer Metaklasse zu tun.
Alles, was eine Unterklasse von
MyType
ist, erhält dann ein Klassenattribut,_order
das die Reihenfolge aufzeichnet, in der die Klassen definiert wurden.quelle
__init__(self)
Aussagetype(self)._order = MyBase.counter; MyBase.counter += 1
?Eine Verwendung für Metaklassen besteht darin, einer Instanz automatisch neue Eigenschaften und Methoden hinzuzufügen.
Wenn Sie sich beispielsweise Django-Modelle ansehen , sieht ihre Definition etwas verwirrend aus. Es sieht so aus, als würden Sie nur Klasseneigenschaften definieren:
Zur Laufzeit werden die Personenobjekte jedoch mit allen möglichen nützlichen Methoden gefüllt. In der Quelle finden Sie einige erstaunliche Metaclassery.
quelle
Ich denke, die ONLamp-Einführung in die Metaklassenprogrammierung ist gut geschrieben und bietet eine wirklich gute Einführung in das Thema, obwohl sie bereits mehrere Jahre alt ist.
http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html (archiviert unter https://web.archive.org/web/20080206005253/http://www.onlamp. com / pub / a / python / 2003/04/17 / metaclasses.html )
Kurz gesagt: Eine Klasse ist eine Blaupause für die Erstellung einer Instanz, eine Metaklasse ist eine Blaupause für die Erstellung einer Klasse. Es ist leicht zu erkennen, dass in Python-Klassen auch erstklassige Objekte sein müssen, um dieses Verhalten zu ermöglichen.
Ich habe selbst noch nie eine geschrieben, aber ich denke, eine der schönsten Anwendungen von Metaklassen ist im Django-Framework zu sehen . Die Modellklassen verwenden einen Metaklassenansatz, um einen deklarativen Stil zum Schreiben neuer Modelle oder Formularklassen zu ermöglichen. Während die Metaklasse die Klasse erstellt, haben alle Mitglieder die Möglichkeit, die Klasse selbst anzupassen.
Die Sache, die noch zu sagen bleibt, ist: Wenn Sie nicht wissen, was Metaklassen sind, beträgt die Wahrscheinlichkeit, dass Sie sie nicht benötigen, 99%.
quelle
TLDR: Eine Metaklasse instanziiert und definiert das Verhalten einer Klasse genau wie eine Klasse das Verhalten einer Instanz.
Pseudocode:
Das Obige sollte vertraut aussehen. Woher kommt
Class
das? Es ist eine Instanz einer Metaklasse (auch Pseudocode):In echtem Code können wir die Standard-Metaklasse übergeben,
type
alles, was wir zum Instanziieren einer Klasse benötigen, und wir erhalten eine Klasse:Anders ausgedrückt
Eine Klasse gehört zu einer Instanz wie eine Metaklasse zu einer Klasse.
Wenn wir ein Objekt instanziieren, erhalten wir eine Instanz:
Wenn wir eine Klasse explizit mit der Standard-Metaklasse definieren,
type
instanziieren wir sie ebenfalls:Anders ausgedrückt, eine Klasse ist eine Instanz einer Metaklasse:
Ein dritter Weg, eine Metaklasse ist die Klasse einer Klasse.
Wenn Sie eine Klassendefinition schreiben und Python sie ausführt, wird das Klassenobjekt mithilfe einer Metaklasse instanziiert (die wiederum zum Instanziieren von Instanzen dieser Klasse verwendet wird).
So wie wir Klassendefinitionen verwenden können, um das Verhalten benutzerdefinierter Objektinstanzen zu ändern, können wir eine Metaklassen-Klassendefinition verwenden, um das Verhalten eines Klassenobjekts zu ändern.
Wofür können sie verwendet werden? Aus den Dokumenten :
Dennoch wird Benutzern normalerweise empfohlen, die Verwendung von Metaklassen zu vermeiden, sofern dies nicht unbedingt erforderlich ist.
Sie verwenden jedes Mal eine Metaklasse, wenn Sie eine Klasse erstellen:
Wenn Sie beispielsweise eine Klassendefinition schreiben,
Sie instanziieren ein Klassenobjekt.
type
Dies entspricht dem funktionalen Aufrufen mit den entsprechenden Argumenten und dem Zuweisen des Ergebnisses zu einer Variablen mit diesem Namen:Beachten Sie, dass einige Dinge automatisch
__dict__
zum Namespace hinzugefügt werden :Die Metaklasse des von uns erstellten Objekts ist in beiden Fällen
type
.(Seite A-Note auf dem Inhalt der Klasse
__dict__
:__module__
ist da , weil Klassen müssen wissen , wo sie definiert sind, und ,__dict__
und__weakref__
es gibt , weil wir nicht definieren__slots__
- wenn wir definieren__slots__
wir in den Fällen , ein wenig Platz sparen werden, wie wir können sie verbieten__dict__
und__weakref__
ausschließen. Zum Beispiel:... Aber ich schweife ab.)
Wir können
type
wie jede andere Klassendefinition erweitern:Hier ist die Standardeinstellung
__repr__
für Klassen:Eines der wertvollsten Dinge, die wir beim Schreiben eines Python-Objekts standardmäßig tun können, ist, es mit einem Gut zu versehen
__repr__
. Wenn wir anrufenhelp(repr)
, erfahren wir, dass es einen guten Test für einen gibt__repr__
, der auch einen Test für Gleichheit erfordert -obj == eval(repr(obj))
. Die folgende einfache Implementierung von__repr__
und__eq__
für Klasseninstanzen unserer Typklasse bietet uns eine Demonstration, die den Standard__repr__
von Klassen verbessern kann :Wenn wir nun ein Objekt mit dieser Metaklasse erstellen, bietet das
__repr__
Echo in der Befehlszeile einen viel weniger hässlichen Anblick als die Standardeinstellung:Mit einer
__repr__
für die Klasseninstanz definierten Funktion können wir unseren Code besser debuggen. Eine weitere Überprüfung miteval(repr(Class))
ist jedoch unwahrscheinlich (da Funktionen von ihren Standardeinstellungen eher nicht bewertet werden können__repr__
).Eine erwartete Verwendung:
__prepare__
ein NamespaceWenn wir beispielsweise wissen möchten, in welcher Reihenfolge die Methoden einer Klasse erstellt werden, können wir ein geordnetes Diktat als Namespace der Klasse angeben. Wir würden dies tun, mit
__prepare__
dem das Namespace-Diktat für die Klasse zurückgegeben wird, wenn es in Python 3 implementiert ist :Und Verwendung:
Und jetzt haben wir eine Aufzeichnung der Reihenfolge, in der diese Methoden (und andere Klassenattribute) erstellt wurden:
Beachten Sie, dass dieses Beispiel aus der Dokumentation übernommen wurde - die neue Aufzählung in der Standardbibliothek übernimmt dies.
Wir haben also eine Metaklasse instanziiert, indem wir eine Klasse erstellt haben. Wir können die Metaklasse auch wie jede andere Klasse behandeln. Es hat eine Reihenfolge der Methodenauflösung:
Und es hat ungefähr das Richtige
repr
(was wir nicht mehr bewerten können, wenn wir keinen Weg finden, unsere Funktionen darzustellen):quelle
Python 3-Update
Es gibt (zu diesem Zeitpunkt) zwei Schlüsselmethoden in einer Metaklasse:
__prepare__
, und__new__
__prepare__
Mit dieser Option können Sie eine benutzerdefinierte Zuordnung (z. B. eineOrderedDict
) angeben, die als Namespace verwendet wird, während die Klasse erstellt wird. Sie müssen eine Instanz eines beliebigen Namespace zurückgeben. Wenn Sie keine__prepare__
normale implementieren,dict
wird verwendet.__new__
ist verantwortlich für die eigentliche Erstellung / Änderung der endgültigen Klasse.Eine nackte Metaklasse, die nichts mehr tut, möchte:
Ein einfaches Beispiel:
Angenommen, Sie möchten, dass ein einfacher Validierungscode für Ihre Attribute ausgeführt wird - so wie es immer ein
int
oder ein sein mussstr
. Ohne eine Metaklasse würde Ihre Klasse ungefähr so aussehen:Wie Sie sehen, müssen Sie den Namen des Attributs zweimal wiederholen. Dies ermöglicht Tippfehler zusammen mit irritierenden Fehlern.
Eine einfache Metaklasse kann dieses Problem lösen:
So würde die Metaklasse aussehen (nicht verwendet,
__prepare__
da sie nicht benötigt wird):Ein Probelauf von:
produziert:
Hinweis : Dieses Beispiel ist einfach genug, es hätte auch mit einem Klassendekorateur durchgeführt werden können, aber vermutlich würde eine tatsächliche Metaklasse viel mehr bewirken.
Die 'ValidateType'-Klasse als Referenz:
quelle
__set_name__(cls, name)
im Deskriptor (ValidateType
) den Namen im Deskriptor (self.name
und in diesem Fall auchself.attr
) festlegen können . Dies wurde hinzugefügt, um für diesen speziellen Anwendungsfall nicht in Metaklassen eintauchen zu müssen (siehe PEP 487).Rolle einer Metaklassenmethode
__call__()
beim Erstellen einer KlasseninstanzWenn Sie Python länger als ein paar Monate programmiert haben, werden Sie schließlich auf Code stoßen, der so aussieht:
Letzteres ist möglich, wenn Sie die
__call__()
magische Methode in der Klasse implementieren .Die
__call__()
Methode wird aufgerufen, wenn eine Instanz einer Klasse als aufrufbar verwendet wird. Wie wir aus früheren Antworten gesehen haben, ist eine Klasse selbst eine Instanz einer Metaklasse. Wenn wir also die Klasse als aufrufbare Klasse verwenden (dh wenn wir eine Instanz davon erstellen), rufen wir tatsächlich die__call__()
Methode ihrer Metaklasse auf . Zu diesem Zeitpunkt sind die meisten Python-Programmierer etwas verwirrt, da ihnen mitgeteilt wurde, dass Sie beim Erstellen einer solchen Instanzinstance = SomeClass()
deren__init__()
Methode aufrufen . Einige, die etwas tiefer gegraben haben, wissen das, bevor__init__()
es das gibt__new__()
. Nun, heute wird eine weitere Ebene der Wahrheit enthüllt, bevor__new__()
es die Metaklasse gibt__call__()
.Lassen Sie uns die Methodenaufrufkette unter dem Gesichtspunkt der Erstellung einer Instanz einer Klasse untersuchen.
Dies ist eine Metaklasse, die genau in dem Moment protokolliert, bevor eine Instanz erstellt wird und in dem sie zurückgegeben werden soll.
Dies ist eine Klasse, die diese Metaklasse verwendet
Und jetzt erstellen wir eine Instanz von
Class_1
Beachten Sie, dass der obige Code nichts anderes tut, als die Aufgaben zu protokollieren. Jede Methode delegiert die eigentliche Arbeit an die Implementierung ihres übergeordneten Elements, wodurch das Standardverhalten beibehalten wird. Da die übergeordnete Klasse von
type
isMeta_1
(type
die standardmäßige übergeordnete Metaklasse) ist und die Reihenfolge der obigen Ausgabe berücksichtigt wird, haben wir jetzt einen Hinweis darauf, was die Pseudoimplementierung vontype.__call__()
:Wir können sehen, dass die
__call__()
Methode der Metaklasse diejenige ist, die zuerst aufgerufen wird. Anschließend wird die Erstellung der Instanz an die__new__()
Methode der Klasse und die Initialisierung an die Instanz delegiert__init__()
. Es ist auch derjenige, der letztendlich die Instanz zurückgibt.Aus dem oben Gesagten ergibt sich, dass der Metaklasse
__call__()
auch die Möglichkeit gegeben wird, zu entscheiden, ob ein Anruf beiClass_1.__new__()
oderClass_1.__init__()
irgendwann getätigt wird oder nicht . Während der Ausführung könnte tatsächlich ein Objekt zurückgegeben werden, das von keiner dieser Methoden berührt wurde. Nehmen Sie zum Beispiel diesen Ansatz für das Singleton-Muster:Beobachten wir, was passiert, wenn wiederholt versucht wird, ein Objekt vom Typ zu erstellen
Class_2
quelle
Eine Metaklasse ist eine Klasse, die angibt, wie (einige) andere Klassen erstellt werden sollen.
Dies ist ein Fall, in dem ich Metaklasse als Lösung für mein Problem sah: Ich hatte ein wirklich kompliziertes Problem, das wahrscheinlich anders hätte gelöst werden können, aber ich entschied mich, es mit einer Metaklasse zu lösen. Aufgrund der Komplexität ist es eines der wenigen Module, die ich geschrieben habe, bei denen die Kommentare im Modul die Menge des geschriebenen Codes überschreiten. Hier ist es...
quelle
Die tl; dr-Version
Mit der
type(obj)
Funktion erhalten Sie den Typ eines Objekts.Das
type()
einer Klasse ist ihre Metaklasse .So verwenden Sie eine Metaklasse:
type
ist eine eigene Metaklasse. Die Klasse einer Klasse ist eine Metaklasse - der Hauptteil einer Klasse sind die Argumente, die an die Metaklasse übergeben werden, die zum Erstellen der Klasse verwendet wird.Hier erfahren Sie, wie Sie mithilfe von Metaklassen die Klassenkonstruktion anpassen.
quelle
type
ist eigentlich einemetaclass
- eine Klasse, die andere Klassen erstellt. Die meistenmetaclass
sind die Unterklassen vontype
. Dermetaclass
empfängt dienew
Klasse als erstes Argument und bietet Zugriff auf das Klassenobjekt mit den folgenden Details:Note:
Beachten Sie, dass die Klasse zu keinem Zeitpunkt instanziiert wurde. Der einfache Vorgang des Erstellens der Klasse löste die Ausführung des aus
metaclass
.quelle
Python-Klassen sind selbst Objekte - wie zum Beispiel - ihrer Meta-Klasse.
Die Standard-Metaklasse, die angewendet wird, wenn Sie Klassen wie folgt bestimmen:
Meta-Klassen werden verwendet, um eine Regel auf eine ganze Reihe von Klassen anzuwenden. Angenommen, Sie erstellen ein ORM für den Zugriff auf eine Datenbank und möchten, dass Datensätze aus jeder Tabelle einer Klasse angehören, die dieser Tabelle zugeordnet ist (basierend auf Feldern, Geschäftsregeln usw.), eine mögliche Verwendung der Metaklasse ist beispielsweise die Verbindungspoollogik, die von allen Datensatzklassen aus allen Tabellen gemeinsam genutzt wird. Eine andere Verwendung ist die Logik zur Unterstützung von Fremdschlüsseln, die mehrere Klassen von Datensätzen umfasst.
Wenn Sie eine Metaklasse definieren, geben Sie einen Unterklassentyp ein und können die folgenden magischen Methoden überschreiben, um Ihre Logik einzufügen.
Jedenfalls sind diese beiden die am häufigsten verwendeten Haken. Metaclassing ist leistungsstark, und oben finden Sie bei weitem keine vollständige Liste der Verwendungszwecke für Metaclassing.
quelle
Die Funktion type () kann den Typ eines Objekts zurückgeben oder einen neuen Typ erstellen.
Zum Beispiel können wir eine Hi-Klasse mit der Funktion type () erstellen und müssen diese Methode nicht mit der Klasse Hi (Objekt) verwenden:
Zusätzlich zur Verwendung von type () zum dynamischen Erstellen von Klassen können Sie das Erstellungsverhalten von Klassen steuern und Metaklassen verwenden.
Gemäß dem Python-Objektmodell ist die Klasse das Objekt, daher muss die Klasse eine Instanz einer anderen bestimmten Klasse sein. Standardmäßig ist eine Python-Klasse eine Instanz der Typklasse. Das heißt, Typ ist Metaklasse der meisten integrierten Klassen und Metaklasse benutzerdefinierter Klassen.
Magic wird wirksam, wenn wir Schlüsselwortargumente in der Metaklasse übergeben haben. Es gibt den Python-Interpreter an, der die CustomList über ListMetaclass erstellen soll. new (), an dieser Stelle können wir beispielsweise die Klassendefinition ändern, eine neue Methode hinzufügen und dann die überarbeitete Definition zurückgeben.
quelle
Zusätzlich zu den veröffentlichten Antworten kann ich sagen, dass a
metaclass
das Verhalten für eine Klasse definiert. So können Sie Ihre Metaklasse explizit festlegen. Immer wenn Python ein Schlüsselwort erhältclass
, beginnt es nach dem zu suchenmetaclass
. Wenn es nicht gefunden wird, wird der Standard-Metaklassentyp verwendet, um das Objekt der Klasse zu erstellen. Mit dem__metaclass__
Attribut können Siemetaclass
Ihre Klasse festlegen :Die Ausgabe wird folgendermaßen erzeugt:
Und natürlich können Sie Ihre eigenen erstellen
metaclass
, um das Verhalten jeder Klasse zu definieren, die mit Ihrer Klasse erstellt wird.Dazu muss Ihre Standardtypklasse
metaclass
geerbt werden, da dies die Hauptklasse istmetaclass
:Die Ausgabe wird sein:
quelle
Bei der objektorientierten Programmierung ist eine Metaklasse eine Klasse, deren Instanzen Klassen sind. So wie eine gewöhnliche Klasse das Verhalten bestimmter Objekte definiert, definiert eine Metaklasse das Verhalten bestimmter Klassen und ihrer Instanzen. Der Begriff Metaklasse bedeutet einfach etwas, das zum Erstellen von Klassen verwendet wird. Mit anderen Worten, es ist die Klasse einer Klasse. Die Metaklasse wird verwendet, um die Klasse so zu erstellen, dass eine Klasse eine Instanz einer Metaklasse ist, so wie das Objekt eine Instanz einer Klasse ist. In Python werden Klassen auch als Objekte betrachtet.
quelle
Hier ist ein weiteres Beispiel dafür, wofür es verwendet werden kann:
metaclass
die Funktion der Instanz (der Klasse) ändern.Das
metaclass
ist mächtig, es gibt viele Dinge (wie Affenmagie), die Sie damit machen können, aber seien Sie vorsichtig, dies ist möglicherweise nur Ihnen bekannt.quelle
Eine Klasse in Python ist ein Objekt und wie jedes andere Objekt eine Instanz von "etwas". Dieses "Etwas" wird als Metaklasse bezeichnet. Diese Metaklasse ist ein spezieller Klassentyp, der Objekte anderer Klassen erstellt. Daher ist die Metaklasse für die Erstellung neuer Klassen verantwortlich. Auf diese Weise kann der Programmierer die Art und Weise anpassen, wie Klassen generiert werden.
Um eine Metaklasse zu erstellen, werden normalerweise die Methoden new () und init () überschrieben . new () kann überschrieben werden, um die Art und Weise zu ändern, wie Objekte erstellt werden, während init () überschrieben werden kann, um die Art und Weise der Initialisierung des Objekts zu ändern. Metaklasse kann auf verschiedene Arten erstellt werden. Eine Möglichkeit besteht darin, die Funktion type () zu verwenden. Die Funktion type () erstellt beim Aufruf mit 3 Parametern eine Metaklasse. Die Parameter sind: -
Eine andere Möglichkeit zum Erstellen einer Metaklasse besteht aus dem Schlüsselwort "Metaklasse". Definieren Sie die Metaklasse als einfache Klasse. Übergeben Sie in den Parametern der geerbten Klasse metaclass = metaclass_name
Metaklasse kann speziell in den folgenden Situationen verwendet werden: -
quelle
Beachten Sie, dass in Python 3.6 eine neue Dunder-Methode
__init_subclass__(cls, **kwargs)
eingeführt wurde, um viele gängige Anwendungsfälle für Metaklassen zu ersetzen. Is wird aufgerufen, wenn eine Unterklasse der definierenden Klasse erstellt wird. Siehe Python-Dokumente .quelle
Metaklasse ist eine Art Klasse, die definiert, wie sich die Klasse verhält, oder wir können sagen, dass eine Klasse selbst eine Instanz einer Metaklasse ist.
quelle