W Pythonie, czym są metaklasy i do czego ich używamy?
źródło
W Pythonie, czym są metaklasy i do czego ich używamy?
Metaklasa jest klasą klasy. Klasa określa zachowanie instancji klasy (tj. Obiektu), a metaklasa - zachowanie klasy. Klasa jest instancją metaklasy.
Podczas gdy w Pythonie możesz używać dowolnych wywołań dla metaklas (takich jak Jerub ), lepszym podejściem jest uczynienie z niej samej klasy. type
jest zwykłą metaklasą w Pythonie. type
sam jest klasą i jest swoim własnym typem. Nie będziesz mógł odtworzyć czegoś takiego jak type
czysto w Pythonie, ale Python trochę oszukuje. Aby utworzyć własną metaklasę w Pythonie, naprawdę chcesz po prostu podklasę type
.
Metaklasa jest najczęściej używana jako fabryka klas. Kiedy tworzysz obiekt przez wywołanie klasy, Python tworzy nową klasę (kiedy wykonuje instrukcję „class”) przez wywołanie metaklasy. W połączeniu z normalną __init__
i __new__
metodami, metaklasy pozwalają zatem robić „dodatkowe rzeczy” podczas tworzenia klasy, takie jak rejestracja nowej klasy w jakimś rejestrze lub zamiana klasy na coś zupełnie innego.
Kiedy class
instrukcja jest wykonywana, Python najpierw wykonuje jej treść class
jako normalny blok kodu. Powstała przestrzeń nazw (dykt) zawiera atrybuty przyszłej klasy. Metaklasę określa się, patrząc na klasy podstawowe przyszłej klasy (metaklasy są dziedziczone), na podstawie __metaclass__
atrybutu przyszłej klasy (jeśli istnieje) lub __metaclass__
zmiennej globalnej. Metaklasa jest następnie wywoływana z nazwą, zasadami i atrybutami klasy w celu jej utworzenia.
Jednak metaklasy faktycznie określają typ klasy, a nie tylko jej fabrykę, dzięki czemu możesz zrobić z nimi znacznie więcej. Możesz na przykład zdefiniować normalne metody metaklasy. Te metody metaklasy są jak metody klasowe, ponieważ można je wywoływać w klasie bez wystąpienia, ale nie są one również podobne do metod klasowych, ponieważ nie można ich wywoływać w instancji klasy. type.__subclasses__()
jest przykładem metody type
metaklasy. Można również zdefiniować sposoby na normal „Magic”, jak __add__
, __iter__
i __getattr__
, aby wdrożyć lub zmienić sposób zachowuje się klasa.
Oto zagregowany przykład bitów i kawałków:
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__
nie jest obsługiwane w Pythonie 3. W użyciu w Pythonie 3class MyObject(metaclass=MyType)
, zobacz python.org/dev/peps/pep-3115 i odpowiedź poniżej.Klasy jako obiekty
Zanim zrozumiesz metaklasy, musisz opanować klasy w języku Python. Python ma bardzo osobliwe pojęcie o klasach, zapożyczone z języka Smalltalk.
W większości języków klasy to tylko fragmenty kodu opisujące sposób tworzenia obiektu. Tak jest również w Pythonie:
Ale klasy są czymś więcej niż w Pythonie. Klasy też są przedmiotami.
Tak, przedmioty.
Jak tylko użyjesz słowa kluczowego
class
, Python wykonuje go i tworzy OBIEKT. Instrukcjatworzy w pamięci obiekt o nazwie „ObjectCreator”.
Ten obiekt (klasa) może sam tworzyć obiekty (instancje) i dlatego jest klasą .
Ale nadal jest to obiekt, a zatem:
na przykład:
Dynamiczne tworzenie klas
Ponieważ klasy są obiektami, możesz je tworzyć w locie, jak każdy obiekt.
Po pierwsze, możesz utworzyć klasę w funkcji, używając
class
:Ale to nie jest tak dynamiczne, ponieważ wciąż musisz sam napisać całą klasę.
Ponieważ klasy są obiektami, muszą być przez coś wygenerowane.
Kiedy używasz
class
słowa kluczowego, Python tworzy ten obiekt automatycznie. Ale tak jak w przypadku większości rzeczy w Pythonie, daje to możliwość zrobienia tego ręcznie.Pamiętasz funkcję
type
? Dobra stara funkcja, która pozwala wiedzieć, jakiego typu jest obiekt:Cóż,
type
ma zupełnie inną zdolność, może także tworzyć klasy w locie.type
może przyjąć opis klasy jako parametry i zwrócić klasę.(Wiem, to głupie, że ta sama funkcja może mieć dwa zupełnie różne zastosowania w zależności od przekazanych parametrów. Jest to problem związany z kompatybilnością wsteczną w Pythonie)
type
działa w ten sposób:Gdzie:
name
: nazwa klasybases
: krotka klasy nadrzędnej (dla dziedziczenia może być pusta)attrs
: słownik zawierający nazwy i wartości atrybutówna przykład:
można utworzyć ręcznie w ten sposób:
Zauważysz, że używamy „MyShinyClass” jako nazwy klasy i jako zmiennej do przechowywania odwołania do klasy. Mogą być różne, ale nie ma powodu, aby to komplikować.
type
akceptuje słownik w celu zdefiniowania atrybutów klasy. Więc:Można przetłumaczyć na:
I używany jako normalna klasa:
I oczywiście możesz po nim dziedziczyć, więc:
byłoby:
W końcu będziesz chciał dodać metody do swojej klasy. Wystarczy zdefiniować funkcję z odpowiednim podpisem i przypisać ją jako atrybut.
Możesz dodać jeszcze więcej metod po dynamicznym utworzeniu klasy, podobnie jak dodawanie metod do normalnie tworzonego obiektu klasy.
Widzisz, dokąd zmierzamy: w Pythonie klasy są obiektami i możesz tworzyć klasy w locie, dynamicznie.
To właśnie robi Python, gdy używasz słowa kluczowego
class
, i robi to za pomocą metaklasy.Co to są metaklasy (wreszcie)
Metaklasy to „rzeczy”, które tworzą klasy.
Definiujesz klasy, aby tworzyć obiekty, prawda?
Dowiedzieliśmy się jednak, że klasy Python są obiektami.
Cóż, metaklasy tworzą te obiekty. Są to zajęcia klas, które możesz przedstawić w następujący sposób:
Widziałeś, że
type
możesz zrobić coś takiego:Jest tak, ponieważ funkcja
type
jest w rzeczywistości metaklasą.type
to metaklasa, której używa Python do tworzenia wszystkich klas za kulisami.Teraz zastanawiasz się, dlaczego, do cholery, jest napisane małymi literami, a nie
Type
?Cóż, myślę, że jest to kwestia spójności z
str
klasą, która tworzy obiekty łańcuchowe iint
klasą, która tworzy obiekty całkowite.type
to tylko klasa, która tworzy obiekty klasowe.Widzisz to, sprawdzając
__class__
atrybut.Wszystko, i mam na myśli wszystko, jest przedmiotem w Pythonie. Obejmuje inty, ciągi znaków, funkcje i klasy. Wszystkie są przedmiotami. Wszystkie zostały utworzone z klasy:
Teraz, co jest
__class__
z dowolnego__class__
?Tak więc metaklasa jest tylko tym, co tworzy obiekty klasowe.
Jeśli chcesz, możesz to nazwać „klasową fabryką”.
type
jest wbudowaną metaklasą, której używa Python, ale oczywiście możesz stworzyć własną metaklasę.__metaclass__
atrybutW Python 2 możesz dodać
__metaclass__
atrybut podczas pisania klasy (zobacz następną sekcję dotyczącą składni Python 3):Jeśli to zrobisz, Python użyje metaklasy do utworzenia klasy
Foo
.Ostrożnie, to trudne.
class Foo(object)
Najpierw piszesz , ale obiekt klasyFoo
nie jest jeszcze tworzony w pamięci.Python będzie szukał
__metaclass__
w definicji klasy. Jeśli go znajdzie, użyje go do utworzenia klasy obiektuFoo
. Jeśli nie, użyje gotype
do utworzenia klasy.Przeczytaj to kilka razy.
Kiedy to zrobisz:
Python wykonuje następujące czynności:
Czy jest w tym jakiś
__metaclass__
atrybutFoo
?Jeśli tak, stwórz w pamięci obiekt klasy (powiedziałem obiekt klasy, zostań ze mną tutaj) o nazwie
Foo
, używając tego, co jest w środku__metaclass__
.Jeśli Python nie może znaleźć
__metaclass__
, będzie szukał__metaclass__
na poziomie MODUŁU i spróbuje zrobić to samo (ale tylko dla klas, które niczego nie dziedziczą, w zasadzie klas w starym stylu).Następnie, jeśli w ogóle nie może znaleźć
__metaclass__
, użyjeBar
własnej metaklasy (pierwszego rodzica) (która może być domyślnatype
), aby utworzyć obiekt klasy.Uważaj tutaj, aby
__metaclass__
atrybut nie został odziedziczony, ponieważ będzie to metaklasa elementu nadrzędnego (Bar.__class__
). Jeśli zostanieBar
użyty__metaclass__
atrybut, który został utworzony zaBar
pomocątype()
(i nietype.__new__()
), podklasy nie odziedziczą tego zachowania.Najważniejsze pytanie brzmi: co możesz włożyć
__metaclass__
?Odpowiedź brzmi: coś, co może stworzyć klasę.
A co może stworzyć klasę?
type
lub cokolwiek, co go podklasuje lub wykorzystuje.Metaklasy w Pythonie 3
Składnia ustawiania metaklasy została zmieniona w Pythonie 3:
tzn.
__metaclass__
atrybut nie jest już używany, na korzyść argumentu słowa kluczowego na liście klas podstawowych.Zachowanie metaklas pozostaje jednak w dużej mierze takie samo .
Jedną z rzeczy dodanych do metaklas w Pythonie 3 jest to, że możesz również przekazywać atrybuty jako argumenty-słowa kluczowe do metaklasy, tak jak:
Przeczytaj poniższą sekcję, w jaki sposób Python sobie z tym radzi.
Niestandardowe metaklasy
Głównym celem metaklasy jest automatyczna zmiana klasy po jej utworzeniu.
Zwykle robisz to dla interfejsów API, w których chcesz tworzyć klasy pasujące do bieżącego kontekstu.
Wyobraź sobie głupi przykład, w którym decydujesz, że wszystkie klasy w module powinny mieć atrybuty zapisane wielkimi literami. Można to zrobić na kilka sposobów, ale jednym ze sposobów jest ustawienie
__metaclass__
na poziomie modułu.W ten sposób wszystkie klasy tego modułu zostaną utworzone przy użyciu tej metaklasy, a my musimy tylko powiedzieć metaklasie, aby wszystkie atrybuty zamieniały na wielkie litery.
Na szczęście
__metaclass__
może być w ogóle dowolna, nie musi to być formalna klasa (wiem, że coś z „klasą” w jej nazwie nie musi być klasą, idź do figury ... ale to jest pomocne).Zaczniemy więc od prostego przykładu, używając funkcji.
Sprawdźmy:
Teraz zróbmy dokładnie to samo, ale używając prawdziwej klasy dla metaklasy:
Przepiszmy powyższe, ale z krótszymi i bardziej realistycznymi nazwami zmiennych, skoro wiemy, co one oznaczają:
Być może zauważyłeś dodatkowy argument
cls
. Nie ma w tym nic specjalnego:__new__
zawsze otrzymuje klasę, w której jest zdefiniowany, jako pierwszy parametr. Tak jak wself
przypadku zwykłych metod, które otrzymują instancję jako pierwszy parametr lub klasę definiującą dla metod klasowych.Ale to nie jest właściwe OOP. Dzwonimy
type
bezpośrednio i nie zastępujemy ani nie dzwonimy do rodziców__new__
. Zróbmy to zamiast tego:Możemy sprawić, że będzie jeszcze czystszy, używając
super
, co ułatwi dziedziczenie (ponieważ tak, możesz mieć metaklasy, dziedziczenie po metaklasach, dziedziczenie po typie):Aha, aw Pythonie 3, jeśli wykonujesz to wywołanie z argumentami słów kluczowych, to tak:
Przekłada się to na metaklasę, aby go użyć:
Otóż to. Naprawdę nie ma nic więcej o metaklasach.
Powodem złożoności kodu używającego metaklas nie jest to, że metaklasy są używane, ponieważ zwykle używasz metaklas do robienia skręconych rzeczy w oparciu o introspekcję, manipulację dziedziczeniem, zmienne takie jak
__dict__
itp.Rzeczywiście, metaklasy są szczególnie przydatne do robienia czarnej magii, a zatem skomplikowanych rzeczy. Ale same w sobie są proste:
Dlaczego miałbyś używać klas metaklas zamiast funkcji?
Ponieważ
__metaclass__
można zaakceptować dowolne wywołanie, dlaczego miałbyś skorzystać z zajęć, ponieważ jest to oczywiście bardziej skomplikowane?Istnieje kilka powodów:
UpperAttrMetaclass(type)
, wiesz, co będzie dalej__new__
,__init__
i__call__
. Co pozwoli ci robić różne rzeczy. Nawet jeśli zwykle możesz to wszystko zrobić__new__
, niektórzy ludzie są po prostu bardziej komfortowi w użyciu__init__
.Dlaczego miałbyś używać metaklasy?
Teraz wielkie pytanie. Dlaczego miałbyś skorzystać z niejasnej, podatnej na błędy funkcji?
Zazwyczaj nie:
Python Guru Tim Peters
Głównym przypadkiem użycia metaklasy jest utworzenie interfejsu API. Typowym tego przykładem jest Django ORM. Pozwala zdefiniować coś takiego:
Ale jeśli to zrobisz:
Nie zwróci
IntegerField
obiektu. Zwróciint
i może nawet pobrać go bezpośrednio z bazy danych.Jest to możliwe, ponieważ
models.Model
definiuje__metaclass__
i wykorzystuje magię, która przekształciPerson
zdefiniowane przed chwilą proste instrukcje w złożony zaczep do pola bazy danych.Django sprawia, że coś złożonego wygląda na proste, ujawniając prosty interfejs API i używając metaklas, odtwarzając kod z tego interfejsu API, aby wykonać prawdziwą pracę za kulisami.
Ostatnie słowo
Po pierwsze, wiesz, że klasy to obiekty, które mogą tworzyć instancje.
Cóż, w rzeczywistości same klasy są instancjami. Metaklas.
W Pythonie wszystko jest przedmiotem i wszystkie one są instancjami klas lub instancjami metaklas.
Z wyjątkiem
type
.type
jest właściwie własną metaklasą. Nie jest to coś, co można by odtworzyć w czystym języku Python, a odbywa się to poprzez oszukiwanie trochę na poziomie implementacji.Po drugie, metaklasy są skomplikowane. Możesz nie chcieć ich używać do bardzo prostych zmian klas. Możesz zmienić klasy, używając dwóch różnych technik:
W 99% przypadków, gdy potrzebujesz zmiany klasy, lepiej skorzystaj z nich.
Ale w 98% przypadków wcale nie potrzebujesz zmiany klasy.
źródło
models.Model
nie używa klasy,__metaclass__
leczclass Model(metaclass=ModelBase):
odnosi się doModelBase
klasy, która następnie wykonuje wyżej wspomnianą magię metaklasy. Wspaniały post! Oto źródło Django: github.com/django/django/blob/master/django/db/models/…__metaclass__
atrybut nie został odziedziczony, ponieważ będzie to metaklasa elementu nadrzędnego (Bar.__class__
). JeśliBar
używany jest__metaclass__
atrybut, który utworzonyBar
ztype()
(i nietype.__new__()
), podklasy nie posiądą że zachowanie >>. - Czy mógłby pan / ktoś proszę wyjaśnić nieco głębiej ten fragment?Now you wonder why the heck is it written in lowercase, and not Type?
- cóż, ponieważ jest zaimplementowany w C - to ten sam powód, dla którego defaultdict ma małe litery, podczas gdy OrDERDict (w python 2) jest normalnym CamelCaseUwaga: ta odpowiedź dotyczy Pythona 2.x, ponieważ została napisana w 2008 r., Metaklasy różnią się nieco w 3.x.
Metaklasy to sekretny sos, który sprawia, że praca „klasowa”. Domyślna metaklasa dla nowego obiektu o nazwie „typ”.
Metaklasy przyjmują 3 argumenty. „ name ”, „ bases ” i „ dict ”
Tutaj zaczyna się sekret. Poszukaj nazwy, podstaw i dykta w tej przykładowej definicji klasy.
Zdefiniujmy metaklasę, która pokaże, jak nazywa ją „ klasa: ”.
A teraz przykład, który tak naprawdę coś znaczy, spowoduje to, że zmienne na liście „atrybuty” zostaną ustawione w klasie i ustawione na Brak.
Zauważ, że magiczne zachowanie, które
Initialised
zyskuje dzięki metaklasie,init_attributes
nie jest przenoszone na podklasęInitialised
.Oto jeszcze bardziej konkretny przykład pokazujący, jak można podklasować „typ”, aby utworzyć metaklasę, która wykonuje akcję podczas tworzenia klasy. Jest to dość trudne:
źródło
Inni wyjaśnili, jak działają metaklasy i jak pasują do systemu typu Python. Oto przykład, do czego można ich użyć. W ramach testów, które napisałem, chciałem śledzić kolejność, w jakiej klasy zostały zdefiniowane, aby móc później utworzyć ich instancję w tej kolejności. Najłatwiej było to zrobić za pomocą metaklasy.
Wszystko, co jest wówczas podklasą,
MyType
otrzymuje atrybut klasy,_order
który rejestruje kolejność, w której klasy zostały zdefiniowane.źródło
__init__(self)
mówitype(self)._order = MyBase.counter; MyBase.counter += 1
?Jednym z zastosowań metaklas jest automatyczne dodawanie nowych właściwości i metod do instancji.
Na przykład, jeśli spojrzysz na modele Django , ich definicja wydaje się nieco myląca. Wygląda na to, że definiujesz tylko właściwości klasy:
Jednak w czasie wykonywania obiekty Person są wypełniane różnego rodzaju przydatnymi metodami. Zobacz źródło niesamowitych metaclassery.
źródło
Myślę, że wprowadzenie ONLamp do programowania w metaklasach jest dobrze napisane i daje naprawdę dobre wprowadzenie do tematu, mimo że ma już kilka lat.
http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html (zarchiwizowany pod adresem https://web.archive.org/web/20080206005253/http://www.onlamp. com / pub / a / python / 2003/04/17 / metaclasses.html )
W skrócie: Klasa jest planem tworzenia instancji, metaklasa jest planem tworzenia klasy. Łatwo można zauważyć, że w klasach Python również muszą być obiektami pierwszej klasy, aby umożliwić takie zachowanie.
Nigdy sam tego nie napisałem, ale myślę, że jedno z najpiękniejszych zastosowań metaklas można zobaczyć w środowisku Django . Klasy modeli używają metaklasy, aby umożliwić deklaratywny styl pisania nowych modeli lub klas formularzy. Podczas gdy metaklasa tworzy klasę, wszyscy członkowie mają możliwość dostosowania samej klasy.
Pozostaje do powiedzenia: jeśli nie wiesz, co to są metaklasy, prawdopodobieństwo, że nie będziesz ich potrzebować, wynosi 99%.
źródło
TLDR: metaklasa tworzy instancję i definiuje zachowanie klasy tak jak klasa tworzy instancję i definiuje zachowanie instancji.
Pseudo kod:
Powyższe powinno wyglądać znajomo. Skąd się
Class
bierze? Jest to przykład metaklasy (również pseudokodu):W prawdziwym kodzie możemy przekazać domyślną metaklasę,
type
wszystko, czego potrzebujemy do utworzenia instancji klasy i otrzymujemy klasę:Mówiąc inaczej
Klasa jest dla instancji tak jak metaklasa dla klasy.
Kiedy tworzymy instancję obiektu, otrzymujemy instancję:
Podobnie, gdy jawnie definiujemy klasę za pomocą domyślnej metaklasy
type
, tworzymy jej instancję:Innymi słowy, klasa jest instancją metaklasy:
Mówiąc inaczej, metaklasa jest klasą klasy.
Kiedy piszesz definicję klasy, a Python ją wykonuje, używa metaklasy do tworzenia instancji obiektu klasy (który z kolei będzie używany do tworzenia instancji tej klasy).
Tak jak możemy użyć definicji klasy, aby zmienić zachowanie niestandardowych instancji obiektów, możemy użyć definicji klasy metaklasy, aby zmienić sposób zachowania obiektu klasy.
Do czego można je wykorzystać? Z dokumentów :
Niemniej jednak zwykle zachęca się użytkowników, aby unikali używania metaklas, chyba że jest to absolutnie konieczne.
Używasz metaklasy za każdym razem, gdy tworzysz klasę:
Kiedy piszesz na przykład definicję klasy, taką jak ta,
Tworzysz obiekt klasy.
Jest to to samo, co funkcjonalne wywoływanie
type
za pomocą odpowiednich argumentów i przypisywanie wyniku do zmiennej o tej nazwie:Uwaga: niektóre rzeczy są automatycznie dodawane do
__dict__
np. Przestrzeni nazw:Metaklasa obiektu stworzyliśmy, w obu przypadkach jest
type
.(Dodatkowa uwaga na temat zawartości klasy
__dict__
:__module__
istnieje, ponieważ klasy muszą wiedzieć, gdzie są zdefiniowane,__dict__
i__weakref__
są tam, ponieważ nie definiujemy__slots__
- jeśli zdefiniujemy__slots__
, zaoszczędzimy trochę miejsca w instancjach, ponieważ możemy zabronić__dict__
i__weakref__
poprzez ich wykluczenie. Na przykład:... ale dygresję.)
Możemy przedłużyć
type
tak jak każdą inną definicję klasy:Oto domyślne
__repr__
klasy:Jedną z najcenniejszych rzeczy, które możemy zrobić domyślnie, pisząc obiekt Python, jest dostarczenie mu dobra
__repr__
. Kiedy dzwonimyhelp(repr)
, dowiadujemy się, że istnieje dobry test,__repr__
który również wymaga testu równościobj == eval(repr(obj))
. Poniższa prosta implementacja__repr__
i__eq__
dla instancji klas naszej klasy typu zapewnia nam demonstrację, która może ulec poprawie w stosunku do domyślnych__repr__
klas:Kiedy więc tworzymy obiekt za pomocą tej metaklasy,
__repr__
echo w wierszu poleceń zapewnia znacznie mniej brzydki widok niż domyślny:Dzięki ładnemu
__repr__
zdefiniowaniu dla instancji klasy mamy silniejszą zdolność do debugowania naszego kodu. Jednak dalsze sprawdzanie za pomocąeval(repr(Class))
jest mało prawdopodobne (ponieważ funkcje byłyby raczej niemożliwe do wyewaluowania z domyślnych__repr__
).Oczekiwane użycie:
__prepare__
przestrzeń nazwJeśli na przykład chcemy wiedzieć, w jakiej kolejności są tworzone metody klasy, możemy podać uporządkowany dyktus jako przestrzeń nazw klasy. Chcemy to zrobić z
__prepare__
którym zwraca dict nazw dla tej klasy, jeżeli jest realizowany w Pythonie 3 :I użycie:
A teraz mamy zapis kolejności, w jakiej te metody (i inne atrybuty klas) zostały utworzone:
Uwaga: ten przykład został zaadaptowany z dokumentacji - nowego wyliczenia w standardowej bibliotece robi to .
Więc stworzyliśmy metaklasę, tworząc klasę. Metaklasę możemy również traktować jak każdą inną klasę. Ma kolejność rozwiązywania metod:
I ma w przybliżeniu poprawną
repr
(której nie możemy już ewaluować, chyba że znajdziemy sposób na reprezentację naszych funkcji):źródło
Aktualizacja języka Python 3
Istnieją (w tym momencie) dwie kluczowe metody metaklasy:
__prepare__
, i__new__
__prepare__
umożliwia dostarczenie niestandardowego odwzorowania (takiego jak anOrderedDict
), które będzie używane jako przestrzeń nazw podczas tworzenia klasy. Musisz zwrócić instancję dowolnej wybranej przestrzeni nazw. Jeśli nie zaimplementujesz__prepare__
normalnego,dict
zostanie użyty.__new__
odpowiada za faktyczne stworzenie / modyfikację końcowej klasy.Metaklasa typu bare-bone, nic nie rób, chciałaby:
Prosty przykład:
Załóżmy, że chcesz, aby na twoich atrybutach działał prosty kod sprawdzania poprawności - tak jak zawsze musi to być
int
znak lubstr
. Bez metaklasy Twoja klasa wyglądałaby mniej więcej tak:Jak widać, musisz dwukrotnie powtórzyć nazwę atrybutu. Umożliwia to literówki i irytujące błędy.
Prosta metaklasa może rozwiązać ten problem:
Tak wyglądałaby metaklasa (nie używa,
__prepare__
ponieważ nie jest potrzebna):Przykładowy przebieg:
produkuje:
Uwaga : ten przykład jest dość prosty, można go również osiągnąć za pomocą dekoratora klas, ale przypuszczalnie rzeczywista metaklasa zrobiłaby znacznie więcej.
Klasa „ValidateType” w celach informacyjnych:
źródło
__set_name__(cls, name)
deskryptora (ValidateType
), aby ustawić nazwę w deskryptorze (self.name
i w tym przypadku równieżself.attr
). Zostało to dodane, aby nie trzeba było zanurzać się w metaklasy dla tego szczególnego wspólnego zastosowania (patrz PEP 487).Rola metody metaklasy
__call__()
podczas tworzenia instancji klasyJeśli programujesz w Pythonie przez ponad kilka miesięcy, ostatecznie natkniesz się na kod, który wygląda następująco:
To drugie jest możliwe, gdy zastosujesz
__call__()
magiczną metodę na klasie.__call__()
Metoda jest wywoływana gdy przykład klasy stosuje się jako wywoływalny. Ale jak widzieliśmy z poprzednich odpowiedzi, sama klasa jest instancją metaklasy, więc kiedy używamy tej klasy jako wywoływalnej (tj. Kiedy tworzymy jej instancję), w rzeczywistości nazywamy__call__()
metodę jej metaklasy . W tym momencie większość programistów Pythona jest nieco zdezorientowana, ponieważ powiedziano im, że podczas tworzenia takiej instancjiinstance = SomeClass()
wywołujesz jej__init__()
metodę. Niektórzy, którzy byli wykopali nieco głębiej wiedzieć, że zanim__init__()
tam__new__()
. Cóż, dzisiaj ujawnia się kolejna warstwa prawdy, zanim__new__()
pojawi się metaklasa ”__call__()
.Przeanalizujmy łańcuch wywołań metod ze szczególnego punktu widzenia tworzenia instancji klasy.
Jest to metaklasa, która rejestruje się dokładnie na chwilę przed utworzeniem instancji i momentem, w którym ma ją zwrócić.
Jest to klasa, która korzysta z tej metaklasy
A teraz stwórzmy instancję
Class_1
Zauważ, że powyższy kod nie robi nic więcej niż rejestrowanie zadań. Każda metoda deleguje rzeczywistą pracę do implementacji swojego rodzica, zachowując w ten sposób zachowanie domyślne. Ponieważ
type
jestMeta_1
to klasa nadrzędna (type
będąca domyślną metaklasą nadrzędną) i biorąc pod uwagę sekwencję porządkowania danych wyjściowych powyżej, mamy teraz wskazówkę, jaka byłaby pseudo-implementacjatype.__call__()
:Widzimy, że
__call__()
metoda metaklasy jest wywoływana jako pierwsza. Następnie deleguje tworzenie instancji do metody klasy__new__()
i inicjalizację do instancji__init__()
. Jest to również ten, który ostatecznie zwraca instancję.Z powyższego wynika, że metaklasa
__call__()
jest również możliwość decydowania, czy wywołanieClass_1.__new__()
lubClass_1.__init__()
zostanie ostatecznie wykonany. W trakcie wykonywania może faktycznie zwrócić obiekt, który nie został dotknięty żadną z tych metod. Weźmy na przykład takie podejście do wzorca singletonu:Zobaczmy, co się dzieje, gdy wielokrotnie próbujemy utworzyć obiekt typu
Class_2
źródło
Metaklasa to klasa, która mówi, w jaki sposób (niektóre) inne klasy powinny zostać utworzone.
Jest to przypadek, w którym widziałem metaklasę jako rozwiązanie mojego problemu: miałem naprawdę skomplikowany problem, który prawdopodobnie mógł zostać rozwiązany inaczej, ale zdecydowałem się rozwiązać go za pomocą metaklasy. Ze względu na złożoność jest to jeden z niewielu modułów, które napisałem, w których komentarze w module przekraczają ilość napisanego kodu. Oto jest ...
źródło
Wersja tl; dr
Ta
type(obj)
funkcja pozwala uzyskać typ obiektu.type()
Klasy jest jego metaklasa .Aby użyć metaklasy:
type
jest własną metaklasą. Klasą klasy jest metaklasa - jej ciałem są argumenty przekazywane metaklasie używanej do konstruowania klasy.Tutaj możesz przeczytać o tym, jak używać metaklas do dostosowywania konstrukcji klas.
źródło
type
jest właściwiemetaclass
klasą, która tworzy kolejne klasy. Większośćmetaclass
to podklasytype
.metaclass
Otrzymujenew
klasy jako pierwszy argument i zapewnić dostęp do obiektu klasy ze szczegółami, jak podano poniżej:Note:
Zauważ, że klasa nie została utworzona w żadnym momencie; prosty akt utworzenia klasy uruchomił wykonanie
metaclass
.źródło
Klasy Python same w sobie są obiektami - jak na przykład - ich meta-klasy.
Domyślna metaklasa, która jest stosowana, gdy określasz klasy jako:
meta klasa służy do zastosowania jakiejś reguły do całego zestawu klas. Załóżmy na przykład, że budujesz ORM w celu uzyskania dostępu do bazy danych i chcesz, aby rekordy z każdej tabeli były klasy odwzorowanej na tę tabelę (na podstawie pól, reguł biznesowych itp.), Możliwe użycie metaklasy jest na przykład logiką puli połączeń, która jest współużytkowana przez wszystkie klasy rekordów ze wszystkich tabel. Innym zastosowaniem jest logika do obsługi kluczy obcych, która obejmuje wiele klas rekordów.
podczas definiowania metaklasy wpisujesz podklasę i możesz przesłonić następujące magiczne metody, aby wstawić logikę.
w każdym razie te dwa są najczęściej używanymi hakami. metaklasowanie jest potężne, a powyżej nie ma nigdzie blisko wyczerpującej listy zastosowań metaklasowania.
źródło
Funkcja type () może zwrócić typ obiektu lub utworzyć nowy typ,
na przykład możemy utworzyć klasę Hi za pomocą funkcji type () i nie musimy używać w ten sposób z klasą Hi (obiekt):
Oprócz użycia type () do dynamicznego tworzenia klas, możesz kontrolować zachowanie tworzenia klasy i używać metaklasy.
Według modelu obiektowego Python klasa jest obiektem, więc klasa musi być instancją innej określonej klasy. Domyślnie klasa Python jest instancją klasy typu. Oznacza to, że typ jest metaklasą większości wbudowanych klas i metaklasą klas zdefiniowanych przez użytkownika.
Magia zacznie działać, gdy przekażemy argumenty słów kluczowych w metaklasie, oznacza to, że interpreter Pythona tworzy listę niestandardową za pomocą ListMetaclass. new (), w tym momencie możemy zmodyfikować definicję klasy, na przykład dodać nową metodę, a następnie zwrócić poprawioną definicję.
źródło
Oprócz opublikowanych odpowiedzi mogę powiedzieć, że
metaclass
określa zachowanie klasy. Możesz więc jawnie ustawić swoją metaklasę. Ilekroć Python dostaje słowo kluczoweclass
, zaczyna szukaćmetaclass
. Jeśli nie zostanie znaleziony - do utworzenia obiektu klasy używany jest domyślny typ metaklasy. Za pomocą tego__metaclass__
atrybutu możesz ustawićmetaclass
swoją klasę:Wyprodukuje takie dane wyjściowe:
I oczywiście możesz stworzyć własną,
metaclass
aby zdefiniować zachowanie dowolnej klasy, która jest tworzona za pomocą twojej klasy.W tym
metaclass
celu należy odziedziczyć domyślną klasę typów, ponieważ jest to głównametaclass
:Dane wyjściowe będą:
źródło
W programowaniu obiektowym metaklasa jest klasą, której instancjami są klasy. Tak jak zwykła klasa definiuje zachowanie niektórych obiektów, tak metaklasa określa zachowanie niektórych klas i ich instancji. Termin metaklasa oznacza po prostu coś, co służy do tworzenia klas. Innymi słowy, jest to klasa klasy. Metaklasa służy do tworzenia klasy, więc podobnie jak obiekt będący instancją klasy, klasa jest instancją metaklasy. W python klasy są również uważane za obiekty.
źródło
Oto kolejny przykład tego, do czego można go użyć:
metaclass
do zmiany funkcji jego instancji (klasy).metaclass
Jest potężny, istnieje wiele rzeczy (takie jak małpa magii) można z nim zrobić, ale należy uważać, to może być znany tylko Tobie.źródło
Klasa w Pythonie jest obiektem i podobnie jak każdy inny obiekt jest instancją „czegoś”. To „coś” jest określane jako metaklasa. Ta metaklasa jest specjalnym typem klasy, który tworzy obiekty innych klas. Dlatego metaklasa jest odpowiedzialna za tworzenie nowych klas. Pozwala to programiście dostosować sposób generowania klas.
Aby utworzyć metaklasę, zwykle wykonuje się przesłonięcie metod new () i init (). new () można zastąpić, aby zmienić sposób tworzenia obiektów, natomiast init () można zastąpić, aby zmienić sposób inicjowania obiektu. Metaklasę można utworzyć na wiele sposobów. Jednym ze sposobów jest użycie funkcji type (). Funkcja type (), wywołana z 3 parametrami, tworzy metaklasę. Parametry to:
Innym sposobem tworzenia metaklasy jest słowo kluczowe „metaklasa”. Zdefiniuj metaklasę jako prostą klasę. W parametrach klasy dziedziczonej podaj metaclass = nazwa_metaklasy
Metaklasa może być szczególnie używana w następujących sytuacjach:
źródło
Zauważ, że w Pythonie 3.6 wprowadzono nową metodę dundera,
__init_subclass__(cls, **kwargs)
która zastępuje wiele typowych przypadków użycia metaklas. Jest wywoływany, gdy tworzona jest podklasa klasy definiującej. Zobacz dokumenty Pythona .źródło
Metaklasa jest rodzajem klasy, która określa jej zachowanie, lub możemy powiedzieć, że sama klasa jest instancją metaklasy.
źródło