Co to są metaklasy w Pythonie?


W Pythonie, czym są metaklasy i do czego ich używamy?
Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:


Klasy jako obiekty
>
Zanim uporasz się z metaklasami, musisz wziąć udział w kursach mistrzowskich w Pythonie. Python ma bardzo osobliwą koncepcję tego, czym są klasy, zapożyczoną z Smalltalk.
W większości języków klasy to po prostu fragmenty kodu opisujące sposób tworzenia obiektu. Jest to również prawdą w Pythonie:
>>> class ObjectCreator(object):... pass...>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

Ale klasy to coś więcej niż Python. Klasy też są obiektami.
Tak, przedmioty.
Po użyciu słowa kluczowego
class
Python wykonuje je i tworzy
OBIEKT. Instrukcje
>>> class ObjectCreator(object):... pass...

tworzy w pamięci obiekt o nazwie „ObjectCreator”.

Ten obiekt (klasa) sam jest w stanie tworzyć obiekty (instancje),
i dlatego jest to klasa

.
Ale nadal jest przedmiotem, a zatem:
  • możesz przypisać go do zmiennej
  • możesz to skopiować [*]możesz dodać do niego atrybuty
  • możesz przekazać go jako parametr funkcji
  • na przykład: [/*]

>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):... print(o)...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>


Dynamiczne tworzenie zajęć
>
Ponieważ klasy są obiektami, możesz je tworzyć w locie, tak jak każdy inny obiekt.
Po pierwsze, możesz utworzyć klasę w funkcji za pomocą
class
:
>>> def choose_class(name):... if name == 'foo':... class Foo(object):... pass... return Foo # return the class, not an instance... else:... class Bar(object):... pass... return Bar...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

Ale to nie jest tak dynamiczne, ponieważ nadal musisz sam napisać całą klasę.
Ponieważ klasy są obiektami, muszą być przez coś generowane.
Używając słowa kluczowego
class
, Python tworzy ten obiekt automatycznie. Ale jak
a większość rzeczy w Pythonie daje możliwość zrobienia tego ręcznie.
Pamiętasz funkcję
type
? Dobra, staroświecka funkcja, która informuje, które
rodzaj obiektu:
>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

Dobrze,

w
http://docs.python.org/2/libra ... 3type
type
ma zupełnie inne możliwości, może też tworzyć klasy w locie.
type
może przyjmować opis klasy jako parametry
i zwróć klasę.
(Wiem, że to głupie, że ta sama funkcja może mieć dwa zupełnie różne zastosowania w zależności od parametrów, które do niej przekazujesz. Jest to problem z powodu odwrotności
kompatybilność w Pythonie)
type
działa tak:
type(name, bases, attrs)

Gdzie:
  • name
    : Nazwa klasy
  • bases
    : krotka klasy nadrzędnej (może być pusta do dziedziczenia)
  • attrs
    : słownik zawierający nazwy i wartości atrybutów np .:

>>> class MyShinyClass(object):... pass

dlatego można go utworzyć ręcznie:
>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>

Zauważysz, że jako nazwy klasy używamy „MyShinyClass”
i jako zmienna do przechowywania odniesienia do klasy. Mogą być różne
ale nie ma powodu, aby to komplikować.
type
akceptuje słownik definiujący atrybuty klasy. Więc:
>>> class Foo(object):... bar = True

Można przetłumaczyć na:
>>> Foo = type('Foo', (), {'bar':True})

I jest używany jak normalna klasa:
>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True

I oczywiście możesz po nim dziedziczyć, więc:
>>> class FooChild(Foo):... pass

by:
>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True

Ostatecznie będziesz chciał dodać metody do swojej klasy. Po prostu zdefiniuj funkcję
z odpowiednim podpisem i przypisz go jako atrybut.
>>> def echo_bar(self):... print(self.bar)...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True

Możesz dodać jeszcze więcej metod po dynamicznym utworzeniu klasy, tak jak dodawanie metod do normalnie utworzonego obiektu klasy.
>>> def echo_bar_more(self):... print('yet another method')...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

Widzisz, dokąd zmierzamy: w Pythonie klasy są obiektami i możesz dynamicznie tworzyć klasę w locie.
To właśnie robi Python, gdy używasz słowa kluczowego
class
i robi to z metaklasą.

Co to są metaklasy (wreszcie)
>
Metaklasy to „rzeczy”, które tworzą klasy.
Definiujesz klasy, aby tworzyć obiekty, prawda?
Ale dowiedzieliśmy się, że klasy Pythona są obiektami.
Cóż, metaklasy są tym, co tworzy te obiekty. Są to klasy klas,
możesz je przedstawić w ten sposób:
MyClass = MetaClass()
my_object = MyClass()

Widzieliście, że
type
pozwala zrobić coś takiego:
MyClass = type('MyClass', (), {})

Dzieje się tak, ponieważ funkcja
type
jest w rzeczywistości metaklasą.
type
to
metaklasa, której Python używa do tworzenia wszystkich klas w tle.
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 typu string, a
int
klasa tworząca obiekty typu integer.
type
to
po prostu klasa, która tworzy obiekty klas.
Możesz to sprawdzić, sprawdzając atrybut
__class__
.
Wszystko, i mam na myśli wszystko, jest obiektem w Pythonie. Obejmuje to int,
łańcuchy, funkcje i klasy. Wszystkie są obiektami. I wszystkie
powstały z klasy:
>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

Więc jaki jest
__class__
dowolnej
__class__
?
>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

Zatem metaklasa to po prostu coś, co tworzy obiekty klas.
Jeśli chcesz, możesz to nazwać „fabryką klas”.
type
to wbudowana metaklasa, której używa Python, ale oczywiście możesz stworzyć własną
własna metaklasa.

Atrybut
__metaclass__
http://docs.python.org/2/refer ... ass__
>
W Pythonie 2 można dodać atrybut
__metaclass__
podczas pisania klasy (zobacz następną sekcję dotyczącą składni Pythona 3):
class Foo(object):
__metaclass__ = something...
[...]

Jeśli to zrobisz, Python użyje metaklasy do utworzenia klasy
Foo
.
Ostrożnie, to trudne.
Najpierw piszesz
class Foo (object)
, ale obiekt klasy
Foo
nie został jeszcze utworzony
na myśli.
Python będzie szukał
__metaclass__
w definicji klasy. Jeśli go znajdzie,
użyje go do stworzenia klasy obiektu
Foo
. Jeśli tak się nie stanie, użyje
type
do utworzenia klasy.
Przeczytaj to kilka razy.
Kiedy to robisz:
class Foo(Bar):
pass

Python wykonuje następujące czynności:
Czy istnieje atrybut
__metaclass__
na
Foo
?
Jeśli tak, utwórz w pamięci obiekt klasy (powiedziałem obiekt klasy, zostań ze mną tutaj) o nazwie
Foo
, używając tego, co jest w
__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 starego stylu zajęcia).
Następnie, jeśli w ogóle nie może znaleźć żadnego
__metaclass__
, użyje własnej metaklasy
Bar
(pierwszy element nadrzędny) (który może być domyślnym typem
), aby utworzyć obiekt klasy. 
Uważaj tutaj, aby atrybut
__metaclass__
nie był dziedziczony, a metaklasa rodzica (
Bar .__ class__
) będzie. Jeśli
Bar
użył atrybutu
__metaclass__
, który utworzył
Bar
z
type ()
(a nie
type .__ new__ ()
), podklasy nie odziedziczą tego zachowania.
Teraz główne pytanie brzmi: co możesz umieścić w
__metaclass__
?
Odpowiedź brzmi: coś, co może stworzyć klasę.
A co może utworzyć klasa?
type
lub cokolwiek, co tworzy podklasy lub używa tego.

Metaklasy w Pythonie 3
>
Składnia ustawiania metaklasy została zmieniona w Pythonie 3:
class Foo(object, metaclass=something):
...

oznacza to, że atrybut
__metaclass__
nie jest już używany na rzecz argumentu klucza na liście klas bazowych.
Jednak zachowanie metaklas pozostaje w

w większości to samo
https://www.python.org/dev/peps/pep-3115/
większość.
Jedną rzeczą dodaną do metaklas w Pythonie 3 jest to, że można również przekazywać atrybuty jako argumenty słów kluczowych do metaklasy, na przykład:
class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
...

Przeczytaj poniższą sekcję o tym, jak obsługuje to Python.

Niestandardowe metaklasy
>
Głównym celem metaklasy jest automatyczna zmiana klasy
podczas tworzenia.
Zwykle robisz to dla interfejsów API, w których chcesz utworzyć pasujące klasy
aktualny kontekst.
Wyobraź sobie głupi przykład, kiedy zdecydujesz, że wszystkie klasy w Twoim module są
muszą mieć atrybuty zapisane wielkimi literami. Jest kilka sposobów
zrób to, ale jednym z nich jest ustawienie
__metaclass__
na poziomie modułu.
W ten sposób wszystkie klasy tego modułu zostaną utworzone przy użyciu tej metaklasy,
i musimy po prostu powiedzieć metaklasie, aby zamieniła wszystkie atrybuty na wielkie litery.
Na szczęście
__metaclass__
można wywołać dowolnie, nie musi to być
klasa formalna (wiem, że coś, co ma w nazwie słowo „klasa”, nie musi być
klasa, wymyśl to ... ale to przydatne).
Zaczniemy więc od prostego przykładu z użyciem funkcji.
# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attrs):
"""
Return a class object, with the list of its attribute turned
into uppercase.
"""
# pick up any attribute that doesn't start with '__' and uppercase it
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in future_class_attrs.items()
} # let `type` do the class creation
return type(future_class_name, future_class_parents, uppercase_attrs)__metaclass__ = upper_attr # this will affect all classes in the moduleclass Foo(): # global __metaclass__ won't work with "object" though
# but we can define __metaclass__ here instead to affect only this class
# and this will work with "object" children
bar = 'bip'

Sprawdźmy:
>>> hasattr(Foo, 'bar')
False
>>> hasattr(Foo, 'BAR')
True
>>> Foo.BAR
'bip'

Zróbmy teraz to samo, ale używając prawdziwej klasy dla metaklasy:
# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
# __new__ is the method called before __init__
# it's the method that creates the object and returns it
# while __init__ just initializes the object passed as parameter
# you rarely use __new__, except when you want to control how the object
# is created.
# here the created object is the class, and we want to customize it
# so we override __new__
# you can do some stuff in __init__ too if you wish
# some advanced use involves overriding __call__ as well, but we won't
# see this
def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attrs):
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in future_class_attrs.items()
}
return type(future_class_name, future_class_parents, uppercase_attrs)

Przepiszmy powyższe, ale z krótszymi i bardziej realistycznymi nazwami zmiennych teraz, gdy wiemy, co one oznaczają:
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, attrs):
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in attrs.items()
}
return type(clsname, bases, uppercase_attrs)

Być może zauważyłeś dodatkowy argument
cls
. jest
nic specjalnego w tym:
__new__
zawsze pobiera klasę, w której jest zdefiniowany jako pierwszy parametr. Tak jak masz
self
dla zwykłych metod, które otrzymują instancję jako pierwszy parametr lub definicję klasy dla metod klasowych.
Ale to nie jest całkowicie poprawne. Dzwonimy bezpośrednio do
type
i nie nadpisujemy ani nie wzywamy Rodzica
__new__
. :
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, attrs):
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in attrs.items()
}
return type.__new__(cls, clsname, bases, uppercase_attrs)

Możemy uczynić to jeszcze czystszym, używając
super
, co ułatwia dziedziczenie (ponieważ tak, metaklasy mogą dziedziczyć po metaklasach, dziedzicząc po typie):
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, attrs):
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in attrs.items()
}
return super(UpperAttrMetaclass, cls).__new__( cls, clsname, bases, uppercase_attrs)

Aha, iw Pythonie 3, jeśli wykonasz to wywołanie za pomocą argumentów słów kluczowych, takich jak:
class Foo(object, metaclass=MyMetaclass, kwarg1=value1):
...

Przekłada się to na to w metaklasie, aby go użyć:
class MyMetaclass(type):
def __new__(cls, clsname, bases, dct, kwargs1=default):
...

To wszystko. Właściwie nie ma nic więcej w metaklasach.
Przyczyną złożoności kodu używającego metaklas nie jest to
jeśli chodzi o metaklasy, dzieje się tak dlatego, że zwykle używasz metaklas do robienia perwersyjnych rzeczy
polegając na introspekcji, manipulowaniu dziedziczeniem, zmiennymi takimi jak
__dict__
itp.
Rzeczywiście, metaklasy są szczególnie przydatne w przypadku czarnej magii, co oznacza
i do złożonych rzeczy. Ale same w sobie są proste:
  • tworzenie klasy przechwytywania
  • zmień klasę
  • zwróć zmienioną klasę


Dlaczego używasz klas metaklasy zamiast funkcji?
>
Skoro
__metaclass__
może akceptować każdy wywoływalny obiekt, dlaczego miałbyś używać klasy
ponieważ jest to oczywiście trudniejsze?
Istnieje kilka powodów:
  • Zamiar jest jasny. Kiedy czytasz
    UpperAttrMetaclass (type)
    , wiesz co nastąpi
  • Możesz użyć OOP. Metaklasa może dziedziczyć z metaklasy, zastępować metody macierzyste. Metaklasy mogą nawet używać metaklas.
  • Podklasy klasy będą instancjami jej metaklasy, jeśli określisz klasę metaklasy, ale nie użyjesz funkcji metaklasy.
  • Możesz lepiej zorganizować swój kod. Nigdy nie używasz metaklas do czegoś takiego jak trywialne jak w powyższym przykładzie. Zwykle jest to trudne. Dostępność bardzo przydatna jest możliwość tworzenia wielu metod i grupowania ich w jedną klasę aby kod był łatwiejszy do odczytania.
  • Możesz dać się wciągnąć w
    __new__
    ,
    __init__
    i
    __call__
    . Co pozwoli robić różne rzeczy. Chociaż zwykle możesz to wszystko zrobić w
    __new__
    , niektórzy ludzie czują się po prostu bardziej komfortowo używając
    __init__
    .
  • Nazywają się metaklasami, do cholery! To musi coś znaczyć!


Dlaczego potrzebujesz metaklasy?
>
A teraz główne pytanie. Dlaczego miałbyś używać jakiejś niezrozumiałej funkcji podatnej na błędy?
Cóż, zazwyczaj nie:

Metaklasy to głęboka magia
99% użytkowników nigdy nie powinno się tym martwić.
Jeśli zastanawiasz się, czy ich potrzebujesz,
ty nie (ludzie, którzy naprawdę
potrzebuję ich, wiedz z całą pewnością
potrzebują ich i nie potrzebują
wyjaśnienie dlaczego).
Guru języka Python Tim Peters

Głównym przypadkiem użycia metaklasy jest utworzenie interfejsu API. Django ORM jest tego typowym przykładem. To pozwala zdefiniować coś takiego:
class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()

Ale jeśli to zrobisz:
person = Person(name='bob', age='35')
print(person.age)

Nie zwróci obiektu
IntegerField
. Zwróci
int
, a nawet może pobrać go bezpośrednio z bazy danych.
Jest to możliwe, ponieważ
models.Model
definiuje
__metaclass__
i
używa magii, która przekształci zdefiniowaną właśnie
osobę
za pomocą prostych instrukcji , w trudny punkt zaczepienia do pola bazy danych.
Django sprawia, że ​​coś złożonego staje się łatwe, udostępniając prosty interfejs API
i używając metaklas, odtwarzając kod z tego API, aby wykonać prawdziwą pracę
za kulisami.

Ostatnie słowo
>
Po pierwsze, wiesz, że klasy to obiekty, których wystąpienia można utworzyć.
Cóż, właściwie klasy są same w sobie instancjami. O metaklasach.
>>> class Foo(object): pass
>>> id(Foo)
142630324

W Pythonie wszystko jest obiektem i wszystkie są instancjami klas
lub wystąpienia metaklas.
Z wyjątkiem
type
.
type
jest w rzeczywistości własną metaklasą. To nie jest to, co możesz
zostanie odtworzony w czystym Pythonie i jest wykonywany przez małą sztuczkę na poziomie implementacji .
Po drugie, metaklasy są trudne. Możesz nie chcieć ich używać do
bardzo proste zmiany klas. Możesz zmienić klasy za pomocą dwóch różnych metod:

W 99% przypadków, kiedy musisz zmienić klasę, lepiej z nich skorzystać.
Ale w 98% przypadków w ogóle nie potrzebujesz zmiany klasy.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Metaclass to klasa klasy. Klasa definiuje, jak zachowuje się instancja klasy (tj. Obiektu), podczas gdy metaklasa określa, jak zachowuje się klasa. Klasa jest instancją metaklasy.
Podczas gdy w Pythonie możesz używać dowolnych wywoływalnych obiektów dla metaklas (jak pokazuje Jerub), najlepszym podejściem jest uczynienie z niej rzeczywistej klasy.
type
to normalna metaklasa w Pythonie.
type
jest klasą i jest własnym typem. Nie będziesz w stanie odtworzyć czegoś takiego jak
type
wyłącznie w Pythonie, ale Python trochę oszukuje. Aby stworzyć własną metaklasę w Pythonie, naprawdę chcesz 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”), wywołując metaklasę. Zatem w połączeniu ze zwykłymi metodami
__init__
i
__new__
, metaklasy umożliwiają wykonywanie „dodatkowych czynności” podczas tworzenia klasy, takich jak rejestrowanie nowej klasy w jakimś rejestrze lub zastąpienie klasy czymś innym.
Kiedy wykonywana jest instrukcja
class
, Python najpierw wykonuje treść instrukcji
class
jako normalny blok kodu. Wynikowa przestrzeń nazw (dict) zawiera atrybuty klasy, które mają zostać utworzone. Metaklasa jest definiowana przez sprawdzenie klas bazowych, które mają być klasy (dziedziczone metaklasy), atrybutu
__metaclass__
class-to-be (jeśli istnieje) lub
__metaclass__
zmienna globalna. Następnie wywoływana jest metaklasa z nazwą, podstawami i atrybutami klasy w celu jej utworzenia.
Jednak metaklasy faktycznie definiują

typ

class, a nie tylko fabryka, dzięki czemu możesz z nimi zrobić znacznie więcej. Na przykład możesz zdefiniować zwykłe metody w metaklasie. Te meta metody są podobne do metod klasowych, ponieważ można je wywołać w klasie bez instancji, ale nie lubią też metod klasowych, ponieważ nie można ich wywołać w instancji klasy. typ .__ podklasy __ () jest przykładem metody w metaklasie
type
. Możesz także zdefiniować typowe „magiczne” metody, takie jak
__add__
,
__iter__
i
__getattr__
, aby zaimplementować lub zmienić zachowanie klasy.
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 fclass 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." % selfclass 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):
passExampleSibling = 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__
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Zauważ, że ta odpowiedź jest specyficzna dla Pythona 2.x, ponieważ została napisana w 2008 roku, metaklasy są nieco inne w 3.x.

Metaklasy to sekretny sos, który sprawia, że ​​„klasa” działa. Domyślna metaklasa dla nowego obiektu stylu nosi nazwę „typ”.
<pre class="lang-none prettyprint-override">
class type(object)
| type(object) -> the object's type
| type(name, bases, dict) -> a new type

Metaklasy przyjmują 3 argumenty. "

Nazwa

", "

fusy

"i"

dykt

"
Tu zaczyna się tajemnica. Zobacz, skąd wzięła się nazwa, podstawy i dyktando w tym przykładzie definicji klasy.
class ThisIsTheName(Bases, Are, Here):
All_the_code_here
def doesIs(create, a):
dict

Zdefiniujmy metaklasę, która pokazuje, jak '

class:

'' tak to nazywa.
def test_metaclass(name, bases, dict):
print 'The Class Name is', name
print 'The Class Bases are', bases
print 'The dict has', len(dict), 'elems, the keys are', dict.keys() return "yellow"class TestName(object, None, int, 1):
__metaclass__ = test_metaclass
foo = 1
def baz(self, arr):
passprint 'TestName = ', repr(TestName)# output =>
The Class Name is TestName
The Class Bases are (<type 'object'>, None, <type 'int'>, 1)
The dict has 4 elems, the keys are ['baz', '__module__', 'foo', '__metaclass__']
TestName = 'yellow'

Teraz, dla przykładu, który naprawdę coś znaczy, spowoduje to automatycznie, że zmienne z listy „atrybutów” zostaną ustawione w klasie na Brak.
def init_attributes(name, bases, dict):
if 'attributes' in dict:
for attr in dict['attributes']:
dict[attr] = None return type(name, bases, dict)class Initialised(object):
__metaclass__ = init_attributes
attributes = ['foo', 'bar', 'baz']print 'foo =>', Initialised.foo
# output=>
foo => None


Zwróć uwagę, że magiczne zachowanie, które
Initialised
uzyskuje z metaklasą
init_attributes
nie jest propagowane do podklasy
Initialised
.

Oto jeszcze bardziej konkretny przykład pokazujący, w jaki sposób można podklasować „typ”, aby utworzyć metaklasę, która wykonuje akcję podczas tworzenia klasy. To dość trudne:
class MetaSingleton(type):
instance = None
def __call__(cls, *args, **kw):
if cls.instance is None:
cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)
return cls.instanceclass Foo(object):
__metaclass__ = MetaSingletona = Foo()
b = Foo()
assert a is b
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Inni wyjaśnili, jak działają metaklasy i jak pasują do systemu typów Pythona. Oto przykład tego, 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 instancje w tej kolejności. Najłatwiej to zrobić za pomocą metaklasy.
class MyMeta(type): counter = 0 def __init__(cls, name, bases, dic):
type.__init__(cls, name, bases, dic)
cls._order = MyMeta.counter
MyMeta.counter += 1class MyType(object): # Python 2
__metaclass__ = MyMetaclass MyType(metaclass=MyMeta): # Python 3
pass

Każda podklasa
MyType
otrzymuje następnie atrybut klasy
_order
, który rejestruje kolejność, w jakiej klasy zostały zdefiniowane.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Jednym ze sposobów korzystania z metaklas jest automatyczne dodawanie nowych właściwości i metod do instancji.
Na przykład, jeśli spojrzysz na

Django models
http://docs.djangoproject.com/ ... dels/
ich definicja wygląda trochę zagmatwana. Wygląda na to, że definiujesz tylko właściwości klasy:
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)

Jednak obiekty Person są zapełniane w czasie wykonywania różnymi użytecznymi metodami. Widzieć

źródło
http://code.djangoproject.com/ ... se.py
dla niesamowitych metaklas.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Myślę, że wprowadzenie ONLamp do programowania w metaklasach jest dobrze napisane i daje naprawdę dobre wprowadzenie do tematu, mimo że ma kilka lat.
http://www.onlamp.com/pub/a/py ... .html
http://www.onlamp.com/pub/a/py ... .html
(archiwum dla

https://web.archive.org/web/20 ... .html
https://web.archive.org/web/20 ... .html
)
W skrócie: klasa to schemat tworzenia instancji, metaklasa to schemat tworzenia instancji klasy. Łatwo zauważyć, że w Pythonie klasy muszą być również obiektami pierwszej klasy, aby zapewnić takie zachowanie.
Nigdy sam tego nie napisałem, ale myślę, że jedno z najprzyjemniejszych zastosowań metaklas można zobaczyć w

struktura
http://www.djangoproject.com/
Django. Klasy modeli używają metaklasy, aby zapewnić deklaratywny styl pisania nowych modeli lub klas formularzy. Podczas gdy metaklasa tworzy klasę, wszyscy członkowie mogą dostosowywać samą klasę.

Pozostaje powiedzieć: jeśli nie wiesz, czym są metaklasy, są szanse, że tak jest

nie są potrzebne

równa 99%.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:


Co to są metaklasy? Do czego ich używasz?
>TLDR: metaklasa tworzy i definiuje zachowanie klasy w taki sam sposób, w jaki klasa tworzy i definiuje zachowanie dla instancji.
Pseudo kod:
>>> Class(...)
instance

Wszystkie powyższe elementy powinny wyglądać znajomo. Skąd się bierze
Class
? To jest instancja metaklasy (również pseudokod):
>>> Metaclass(...)
Class

W prawdziwym kodzie możemy przekazać domyślną metaklasę,
typ
, cokolwiek potrzebujemy, aby utworzyć instancję klasy, i otrzymujemy klasę:
>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
<class '__main__.Foo'>


Mówiąc inaczej
>
  • Klasa jest powiązana z instancją w taki sam sposób, w jaki metaklasa jest z klasą. Kiedy tworzymy instancję obiektu, otrzymujemy instancję:
    >>> object() # instantiation of class<object object at 0x7f9069b4e0b0> # instance
    Podobnie, kiedy jawnie definiujemy klasę z domyślną metaklasą,
    type
    , tworzymy jej instancję:
    >>> type('Object', (object,), {}) # instantiation of metaclass<class '__main__.Object'> # instance
  • Innymi słowy, klasa jest instancją metaklasy:
    >>> isinstance(object, type)True
  • Po trzecie, metaklasa jest klasą klasy.
    >>> type(object) == typeTrue>>> object.__class__<class 'type'>

Kiedy piszesz definicję klasy i Python ją wykonuje, używa metaklasy do utworzenia instancji obiektu tej klasy (która z kolei będzie używana do tworzenia instancji tej klasy).
Tak jak możemy użyć definicji klas do zmiany zachowania instancji obiektów niestandardowych, tak samo możemy użyć definicji klasy metaklasy do zmiany zachowania obiektu klasy.
Do czego można je wykorzystać? Z

dokumenty
https://docs.python.org/3/refe ... ample
:

Potencjalne zastosowania metaklas są nieograniczone. Niektóre pomysły, które zostały zbadane, obejmują rejestrowanie, walidację interfejsu, automatyczne delegowanie, automatyczne tworzenie właściwości, serwery proxy, struktury i automatyczne blokowanie/synchronizację zasobów.

Jednak użytkownikom ogólnie zaleca się unikanie używania metaklas, chyba że jest to absolutnie konieczne.

Używasz metaklasy za każdym razem, gdy tworzysz klasę:
>
Kiedy piszesz taką definicję klasy,
class Foo(object): 
'demo'

Tworzysz instancję obiektu klasy.
>>> Foo
<class '__main__.Foo'>
>>> isinstance(Foo, type), isinstance(Foo, object)
(True, True)

Działa to tak samo, jak wywołanie funkcji
type
z odpowiednimi argumentami i przypisanie wyniku do zmiennej o tej nazwie:
name = 'Foo'
bases = (object,)
namespace = {'__doc__': 'demo'}
Foo = type(name, bases, namespace)

Zwróć uwagę, że niektóre rzeczy są automatycznie dodawane do
__dict__
, czyli do przestrzeni nazw:
>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>,
'__module__': '__main__', '__weakref__': <attribute '__weakref__'
of 'Foo' objects>, '__doc__': 'demo'})
Metaclass

utworzony przez nas obiekt jest w obu przypadkach
type
.
(Uwaga dotycząca zawartości klasy
__dict__
:
__module__
istnieje, ponieważ klasy muszą wiedzieć, gdzie są zdefiniowane, a
__dict__
i
__weakref __
istnieje, ponieważ nie definiujemy
__slots__
- jeśli tak

zdefiniuj
__slots__
,
https://coderoad.ru/472000/
zaoszczędzimy trochę miejsca w instancjach, ponieważ możemy wyłączyć
__dict__
i
__weakref__
, wykluczając je. Na przykład:
>>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
>>> Baz.__dict__
mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})

80, ale błądzę.)

Możemy rozszerzyć
type
tak jak każdą inną definicję klasy:
>
Oto domyślne klasy
__repr__
:
>>> Foo
<class '__main__.Foo'>

Jedną z najbardziej wartościowych rzeczy, które możemy zrobić domyślnie podczas pisania obiektu Pythona, jest dostarczenie mu ładnego
__repr__
. Kiedy dzwonimy do
help (repr)
, wiemy, że istnieje dobry test dla
__repr__
, który również wymaga testu równości -
obj == eval (repr (obj ))
. Poniższa prosta implementacja
__repr__
i
__eq__
dla instancji klas naszej klasy typu dostarcza nam demo, które może poprawić działanie domyślnych klas
__repr__
:
class Type(type):
def __repr__(cls):
"""
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> eval(repr(Baz))
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
"""
metaname = type(cls).__name__
name = cls.__name__
parents = ', '.join(b.__name__ for b in cls.__bases__)
if parents:
parents += ','
namespace = ', '.join(': '.join( (repr(k), repr(v) if not isinstance(v, type) else v.__name__))
for k, v in cls.__dict__.items())
return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)
def __eq__(cls, other):
"""
>>> Baz == eval(repr(Baz))
True
"""
return (cls.__name__, cls.__bases__, cls.__dict__) == ( other.__name__, other.__bases__, other.__dict__)

Więc teraz, kiedy tworzymy obiekt z tą metaklasą,
__repr__
odzwierciedlone w linii poleceń zapewnia znacznie mniej brzydki widok niż domyślny:
>>> class Bar(object): pass
>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})

Mając dobre
__repr__
zdefiniowane dla wystąpienia klasy, mamy większe możliwości debugowania naszego kodu. Jednak dalsza weryfikacja za pomocą
eval (repr (Class))
jest mało prawdopodobna (ponieważ obliczenia funkcji byłyby raczej niemożliwe z ich domyślnego
__repr__
).

Oczekiwane użycie: przestrzeń nazw
__prepare__
>
Jeśli na przykład chcemy wiedzieć, w jakiej kolejności tworzone są metody klasy, jako przestrzeń nazw klasy możemy podać uporządkowany dykt. Zrobilibyśmy to za pomocą
__prepare__
, który

zwraca przestrzeń nazw dict dla klasy, jeśli zaimplementowano ją w Pythonie 3
https://docs.python.org/3/refe ... space
:
from collections import OrderedDictclass OrderedType(Type):
@classmethod
def __prepare__(metacls, name, bases, **kwargs):
return OrderedDict()
def __new__(cls, name, bases, namespace, **kwargs):
result = Type.__new__(cls, name, bases, dict(namespace))
result.members = tuple(namespace)
return result

I zastosowanie:
class OrderedMethodsObject(object, metaclass=OrderedType):
def method1(self): pass
def method2(self): pass
def method3(self): pass
def method4(self): pass

A teraz mamy zapis kolejności, w jakiej te metody (i inne atrybuty klas) zostały utworzone:
>>> OrderedMethodsObject.members
('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')

Należy pamiętać, że ten przykład został zaadaptowany z

dokumentacja
https://docs.python.org/3/refe ... ample
- tworzy coś nowego

wyliczenie w bibliotece standardowej
https://github.com/python/cpyt ... um.py
.
Dlatego utworzyliśmy wystąpienie metaklasy, tworząc klasę. Metaklasę możemy również traktować jak każdą inną klasę. Ma kolejność rozwiązywania metod:
>>> inspect.getmro(OrderedType)
(<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)

I ma w przybliżeniu poprawne
repr
(którego nie możemy już oceniać, chyba że znajdziemy sposób na przedstawienie naszych funkcji):
>>> OrderedMethodsObject
OrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMet
hodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__d
ict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:


Aktualizacja Pythona 3

W metaklasie istnieją (w tej chwili) dwie kluczowe metody:
  • __prepare__
    i
  • __new__

__prepare__
umożliwia zapewnienie niestandardowego mapowania (na przykład
OrderedDict
), które będzie używane jako przestrzeń nazw podczas tworzenia klas. Musisz zwrócić instancję dowolnej wybranej przestrzeni nazw. Jeśli nie zaimplementujesz
__prepare__
, używany jest normalny
dict
.
__new__
jest odpowiedzialny za faktyczne tworzenie/modyfikowanie końcowej klasy.
Nagie kości, dodatkowa metaklasa nic nie rób:
class Meta(type): def __prepare__(metaclass, cls, bases):
return dict() def __new__(metacls, cls, bases, clsdict):
return super().__new__(metacls, cls, bases, clsdict)

Prosty przykład:
Załóżmy, że chcesz, aby na Twoich atrybutach działał prosty kod weryfikacyjny - na przykład zawsze powinien to być
int
lub
str
. Bez metaklasy Twoja klasa wyglądałaby mniej więcej tak:
class Person:
weight = ValidateType('weight', int)
age = ValidateType('age', int)
name = ValidateType('name', str)

Jak widać, nazwę atrybutu należy powtórzyć dwukrotnie. Dzięki temu możliwe są literówki i irytujące błędy.
Prosta metaklasa może rozwiązać ten problem:
class Person(metaclass=Validator):
weight = ValidateType(int)
age = ValidateType(int)
name = ValidateType(str)

Tak wyglądałaby metaklasa (bez użycia
__prepare__
, ponieważ nie jest potrzebna):
class Validator(type):
def __new__(metacls, cls, bases, clsdict):
# search clsdict looking for ValidateType descriptors
for name, attr in clsdict.items():
if isinstance(attr, ValidateType):
attr.name = name
attr.attr = '_' + name
# create final class and return it
return super().__new__(metacls, cls, bases, clsdict)

Przykładowe wykonanie:
p = Person()
p.weight = 9
print(p.weight)
p.weight = '9'

produkuje:
9
Traceback (most recent call last):
File "simple_meta.py", line 36, in <module>
p.weight = '9'
File "simple_meta.py", line 24, in __set__
(self.name, self.type, value))
TypeError: weight must be of type(s) <class 'int'> (got '9')


Uwaga
: ten przykład jest dość prosty, można go również wykonać za pomocą dekoratora klas, ale najwyraźniej faktyczna metaklasa zrobiłaby o wiele więcej.
Klasa „ValidateType” w celach informacyjnych:
class ValidateType:
def __init__(self, type):
self.name = None # will be set by metaclass
self.attr = None # will be set by metaclass
self.type = type
def __get__(self, inst, cls):
if inst is None:
return self
else:
return inst.__dict__[self.attr]
def __set__(self, inst, value):
if not isinstance(value, self.type):
raise TypeError('%s must be of type(s) %s (got %r)' %
(self.name, self.type, value))
else:
inst.__dict__[self.attr] = value
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:


Rola metody
__call __ ()
metaklasy podczas tworzenia wystąpienia klasy
>
Jeśli programujesz w Pythonie dłużej niż kilka miesięcy, w końcu natkniesz się na kod, który wygląda tak:
# define a class
class SomeClass(object):
# ...
# some definition here ...
# ...# create an instance of it
instance = SomeClass()# then call the object as if it's a function
result = instance('foo', 'bar')

To drugie jest możliwe, gdy zaimplementujesz w klasie magiczną metodę
__call __ ()
.
class SomeClass(object):
# ...
# some definition here ...
# ... def __call__(self, foo, bar):
return bar + foo

Metoda
__call __ ()
jest wywoływana, gdy instancja klasy jest używana jako obiekt wywoływalny. Ale jak widzieliśmy z poprzednich odpowiedzi, sama klasa jest instancją metaklasy, więc kiedy używamy klasy jako wywoływalnej (tj. Kiedy tworzymy jej instancję), w rzeczywistości wywołujemy jej metodę metaklasy '
__call __ ()
. W tym momencie większość programistów Pythona jest nieco zdezorientowanych, ponieważ powiedziano im, że tworząc instancję taką jak ta
instance = SomeClass ()
, wywołujesz jej metodę
__init __ ()
... Niektórzy, którzy sięgają nieco głębiej, wiedzą, że przed
__init __ ()
znajduje się
__new __ ()
. Cóż, dzisiaj ujawnia się kolejna warstwa prawdy, przed
__new __ ()
jest metaklasa '
__call __ ()
.
Spójrzmy na łączenie wywołań metod z punktu widzenia tworzenia instancji klasy.
Jest to metaklasa, która rejestruje dokładnie moment przed utworzeniem instancji i moment, w którym ma ją zwrócić.
class Meta_1(type):
def __call__(cls):
print "Meta_1.__call__() before creating an instance of ", cls
instance = super(Meta_1, cls).__call__()
print "Meta_1.__call__() about to return instance."
return instance

To jest klasa, której używa ta metaklasa
class Class_1(object): __metaclass__ = Meta_1 def __new__(cls):
print "Class_1.__new__() before creating an instance."
instance = super(Class_1, cls).__new__(cls)
print "Class_1.__new__() about to return instance."
return instance def __init__(self):
print "entering Class_1.__init__() for instance initialization."
super(Class_1,self).__init__()
print "exiting Class_1.__init__()."

Teraz stwórzmy instancję
Class_1
instance = Class_1()
# Meta_1.__call__() before creating an instance of <class '__main__.Class_1'>.
# Class_1.__new__() before creating an instance.
# Class_1.__new__() about to return instance.
# entering Class_1.__init__() for instance initialization.
# exiting Class_1.__init__().
# Meta_1.__call__() about to return instance.

Należy pamiętać, że powyższy kod właściwie nie robi nic poza rejestracją zadań. Każda metoda deleguje rzeczywistą pracę implementacji swojego rodzica, zachowując w ten sposób domyślne zachowanie. Ponieważ
type
jest klasą nadrzędną
Meta_1
(
type
to domyślna metaklasa nadrzędna) i biorąc pod uwagę powyższą kolejność kolejności danych wyjściowych, mamy teraz klucz co byłoby pseudo-implementacją typu
.__ call __ ()
:
class type:
def __call__(cls, *args, **kwarg): # ... maybe a few things done to cls here # then we call __new__() on the class to create an instance
instance = cls.__new__(cls, *args, **kwargs) # ... maybe a few things done to the instance here # then we initialize the instance with its __init__() method
instance.__init__(*args, **kwargs) # ... maybe a few more things done to instance here # then we return it
return instance

Widzimy, że metoda
__call __ ()
metaklasy jest wywoływana jako pierwsza. Następnie deleguje instancję do metody
__new __ ()
klasy i inicjalizację do metody
__init __ ()
instancji. Jest to również ten, który ostatecznie zwraca instancję.
Z powyższego wynika, że ​​metaklasa
__call __ ()
ma również możliwość zdecydowania, czy ostatecznie wywołanie ma zostać wykonane do
Class_1 .__ new __ ()
czy < code> Class_1 .__ init __ () . Podczas wykonywania może w rzeczywistości zwrócić obiekt, który nie został dotknięty żadną z tych metod. Weźmy na przykład takie podejście do wzorca singleton:
class Meta_2(type):
singletons = {} def __call__(cls, *args, **kwargs):
if cls in Meta_2.singletons:
# we return the only instance and skip a call to __new__()
# and __init__()
print ("{} singleton returning from Meta_2.__call__(), "
"skipping creation of new instance.".format(cls))
return Meta_2.singletons[cls] # else if the singleton isn't present we proceed as usual
print "Meta_2.__call__() before creating an instance."
instance = super(Meta_2, cls).__call__(*args, **kwargs)
Meta_2.singletons[cls] = instance
print "Meta_2.__call__() returning new instance."
return instanceclass Class_2(object): __metaclass__ = Meta_2 def __new__(cls, *args, **kwargs):
print "Class_2.__new__() before creating instance."
instance = super(Class_2, cls).__new__(cls)
print "Class_2.__new__() returning instance."
return instance def __init__(self, *args, **kwargs):
print "entering Class_2.__init__() for initialization."
super(Class_2, self).__init__()
print "exiting Class_2.__init__()."

Zobaczmy, co się stanie, gdy wiele razy spróbujesz utworzyć obiekt typu
Class_2
a = Class_2()
# Meta_2.__call__() before creating an instance.
# Class_2.__new__() before creating instance.
# Class_2.__new__() returning instance.
# entering Class_2.__init__() for initialization.
# exiting Class_2.__init__().
# Meta_2.__call__() returning new instance.b = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.c = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.a is b is c # True
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Metaklasa to klasa, która mówi, jak (niektóre) inne klasy powinny być tworzone.
Tak jest w przypadku, gdy rozważałem metaklasę jako rozwiązanie mojego problemu:
Miałem naprawdę trudny problem, który prawdopodobnie mógłby zostać rozwiązany inaczej, ale ostatecznie rozwiązałem go za pomocą metaklasy. Ze względu na swoją złożoność jest to jeden z nielicznych modułów, które napisałem, gdzie komentarze w module przekraczają ilość napisanego kodu. Oto jest ...
#!/usr/bin/env python# Copyright (C) 2013-2014 Craig Phillips. All rights reserved.# This requires some explaining. The point of this metaclass excercise is to
# create a static abstract class that is in one way or another, dormant until
# queried. I experimented with creating a singlton on import, but that did
# not quite behave how I wanted it to. See now here, we are creating a class
# called GsyncOptions, that on import, will do nothing except state that its
# class creator is GsyncOptionsType. This means, docopt doesn't parse any
# of the help document, nor does it start processing command line options.
# So importing this module becomes really efficient. The complicated bit
# comes from requiring the GsyncOptions class to be static. By that, I mean
# any property on it, may or may not exist, since they are not statically
# defined; so I can't simply just define the class with a whole bunch of
# properties that are @property @staticmethods.
#
# So here's how it works:
#
# Executing 'from libgsync.options import GsyncOptions' does nothing more
# than load up this module, define the Type and the Class and import them
# into the callers namespace. Simple.
#
# Invoking 'GsyncOptions.debug' for the first time, or any other property
# causes the __metaclass__ __getattr__ method to be called, since the class
# is not instantiated as a class instance yet. The __getattr__ method on
# the type then initialises the class (GsyncOptions) via the __initialiseClass
# method. This is the first and only time the class will actually have its
# dictionary statically populated. The docopt module is invoked to parse the
# usage document and generate command line options from it. These are then
# paired with their defaults and what's in sys.argv. After all that, we
# setup some dynamic properties that could not be defined by their name in
# the usage, before everything is then transplanted onto the actual class
# object (or static class GsyncOptions).
#
# Another piece of magic, is to allow command line options to be set in
# in their native form and be translated into argparse style properties.
#
# Finally, the GsyncListOptions class is actually where the options are
# stored. This only acts as a mechanism for storing options as lists, to
# allow aggregation of duplicate options or options that can be specified
# multiple times. The __getattr__ call hides this by default, returning the
# last item in a property's list. However, if the entire list is required,
# calling the 'list()' method on the GsyncOptions class, returns a reference
# to the GsyncListOptions class, which contains all of the same properties
# but as lists and without the duplication of having them as both lists and
# static singlton values.
#
# So this actually means that GsyncOptions is actually a static proxy class...
#
# ...And all this is neatly hidden within a closure for safe keeping.
def GetGsyncOptionsType():
class GsyncListOptions(object):
__initialised = False class GsyncOptionsType(type):
def __initialiseClass(cls):
if GsyncListOptions._GsyncListOptions__initialised: return from docopt import docopt
from libgsync.options import doc
from libgsync import __version__ options = docopt( doc.__doc__ % __version__,
version = __version__,
options_first = True
) paths = options.pop('<path>', None)
setattr(cls, "destination_path", paths.pop() if paths else None)
setattr(cls, "source_paths", paths)
setattr(cls, "options", options) for k, v in options.iteritems():
setattr(cls, k, v) GsyncListOptions._GsyncListOptions__initialised = True def list(cls):
return GsyncListOptions def __getattr__(cls, name):
cls.__initialiseClass()
return getattr(GsyncListOptions, name)[-1] def __setattr__(cls, name, value):
# Substitut option names: --an-option-name for an_option_name
import re
name = re.sub(r'^__', "", re.sub(r'-', "_", name))
listvalue = [] # Ensure value is converted to a list type for GsyncListOptions
if isinstance(value, list):
if value:
listvalue = [] + value
else:
listvalue = [ None ]
else:
listvalue = [ value ] type.__setattr__(GsyncListOptions, name, listvalue) # Cleanup this module to prevent tinkering.
import sys
module = sys.modules[__name__]
del module.__dict__['GetGsyncOptionsType'] return GsyncOptionsType# Our singlton abstract proxy class.
class GsyncOptions(object):
__metaclass__ = GetGsyncOptionsType()
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:


Wersja Tl; dr
>
Funkcja
type (obj)
zwraca typ obiektu.
To jest
type ()
klasy

metaklasa

.

Aby użyć metaklasy:
class Foo(object):
__metaclass__ = MyMetaClass

type
to osobna metaklasa. Klasa klasy to metaklasa; Treść klasy to argumenty przekazywane do metaklasy używanej do budowania klasy.
Tutaj
https://docs.python.org/3/refe ... asses
możesz przeczytać o tym, jak używać metaklas do dostosowywania sposobu budowania klas.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

type
to w rzeczywistości klasa
metaclass
, która tworzy inne klasy.
Większość
metaclass
to podklasy
type
.
metaclass
przyjmuje klasę
new
jako pierwszy argument i uwidacznia obiekt klasy z szczegóły w następujący sposób:
>>> class MetaClass(type):... def __init__(cls, name, bases, attrs):... print ('class name: %s' %name )... print ('Defining class %s' %cls)... print('Bases %s: ' %bases)... print('Attributes')... for (name, value) in attrs.items():... print ('%s :%r' %(name, value))... >>> class NewClass(object, metaclass=MetaClass):... get_choch='dairy'... 
class name: NewClass
Bases <class 'object'>:
Defining class <class 'NewClass'>
get_choch :'dairy'
__module__ :'builtins'
__qualname__ :'NewClass'

Note:
Pamiętaj, że klasa nie została nigdy utworzona; prosta czynność tworzenia klasy wyzwoliła wykonanie
metaclass
.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Klasy Pythona same w sobie są obiektami - tak jak w przykładzie - ich metaklasy.
Domyślna metaklasa używana podczas definiowania klas jako:
class foo:
...

meta-klasa służy do zastosowania jakiejś reguły do ​​całego zestawu klas. Na przykład, załóżmy, że tworzysz ORM w celu uzyskania dostępu do bazy danych i chcesz, aby rekordy z każdej tabeli należały do ​​klasy skojarzonej z tą tabelą (na podstawie pól, reguł biznesowych itp.), Możliwe użycie metaklasy jest przykład logiki puli połączeń, która jest wspólna dla wszystkich klas rekordów ze wszystkich tabel. Innym zastosowaniem jest logika do obsługi kluczy obcych, która obejmuje wiele klas rekordów.
definiując metaklasę, wprowadzasz typ podklasy i możesz zastąpić następujące magiczne metody, aby wstrzyknąć swoją logikę.
class somemeta(type):
__new__(mcs, name, bases, clsdict):
"""
mcs: is the base metaclass, in this case type.
name: name of the new class, as provided by the user.
bases: tuple of base classes
clsdict: a dictionary containing all methods and attributes defined on class you must return a class object by invoking the __new__ constructor on the base metaclass.
ie:
return type.__call__(mcs, name, bases, clsdict). in the following case: class foo(baseclass):
__metaclass__ = somemeta an_attr = 12 def bar(self):
... @classmethod
def foo(cls):
... arguments would be : ( somemeta, "foo", (baseclass, baseofbase,..., object), {"an_attr":12, "bar": <function>, "foo": <bound class method>} you can modify any of these values before passing on to type
"""
return type.__call__(mcs, name, bases, clsdict)
def __init__(self, name, bases, clsdict):
"""
called after type has been created. unlike in standard classes, __init__ method cannot modify the instance (cls) - and should be used for class validaton.
"""
pass
def __prepare__():
"""
returns a dict or something that can be used as a namespace.
the type will then attach methods and attributes from class definition to it. call order : somemeta.__new__ -> type.__new__ -> type.__init__ -> somemeta.__init__
"""
return dict() def mymethod(cls):
""" works like a classmethod, but for class objects. Also, my method will not be visible to instances of cls.
"""
pass

jeśli już, te dwa haczyki są najczęściej używane. Metaklasa jest potężna, a powyższa lista nie jest kompletną listą zastosowań metaklasy.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

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ć tej metody z klasą Hi (object):
def func(self, name='mike'):
print('Hi, %s.' % name)Hi = type('Hi', (object,), dict(hi=func))
h = Hi()
h.hi()
Hi, mike.type(Hi)
typetype(h)
__main__.Hi

Oprócz używania type () do dynamicznego tworzenia klas, możesz kontrolować zachowanie tworzenia klas i używać metaklasy.
Zgodnie z modelem obiektowym Pythona klasa jest obiektem, więc klasa musi być instancją innej określonej klasy.
Domyślnie klasa Pythona jest instancją klasy typu. Oznacza to, że typ jest metaklasą większości wbudowanych klas i metaklasą klas niestandardowych.
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)class CustomList(list, metaclass=ListMetaclass):
passlst = CustomList()
lst.add('custom_list_1')
lst.add('custom_list_2')lst
['custom_list_1', 'custom_list_2']

Magia działa, gdy przekazujemy kluczowe argumenty do metaklasy, mówi ona interpreterowi Pythona, aby utworzył CustomList za pośrednictwem ListMetaclass.

new

(), w tym miejscu możemy na przykład zmienić definicję klasy i dodać nową metodę, a następnie zwrócić poprawioną definicję.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Oprócz opublikowanych odpowiedzi mogę powiedzieć, że
metaclass
definiuje zachowanie klasy. W ten sposób możesz jawnie ustawić metaklasę. Za każdym razem, gdy Python otrzyma słowo kluczowe
class
, zaczyna szukać
metaclass
. Jeśli nie zostanie znaleziony, do utworzenia obiektu klasy zostanie użyty domyślny typ metaklasy. Korzystając z atrybutu
__metaclass__
, możesz ustawić
metaclass
swojej klasy:
class MyClass:
__metaclass__ = type
# write here other method
# write here one more methodprint(MyClass.__metaclass__)

To da taki wynik:
class 'type'

I oczywiście możesz utworzyć własną
metaclass
, aby zdefiniować zachowanie dowolnej klasy utworzonej za pomocą Twojej klasy.
Aby to zrobić, klasa typu
metaclass
musi być domyślnie dziedziczona, ponieważ jest to główna klasa
metaclass
:
class MyMetaClass(type):
__metaclass__ = type
# you can write here any behaviour you wantclass MyTestClass:
__metaclass__ = MyMetaClassObj = MyTestClass()
print(Obj.__metaclass__)
print(MyMetaClass.__metaclass__)

Wynik będzie taki:
class '__main__.MyMetaClass'
class 'type'
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

W programowaniu obiektowym metaklasa to klasa, której instancjami są klasy. Podobnie jak zwykła klasa definiuje zachowanie pewnych obiektów, metaklasa definiuje zachowanie określonej klasy i jej instancji.
Termin metaklasa oznacza po prostu coś używanego do tworzenia klas. Innymi słowy, jest to klasa klasy. Metaklasa służy do tworzenia klasy w taki sam sposób, w jaki obiekt jest instancją klasy, a klasa jest instancją metaklasy. W Pythonie klasy są również uważane za obiekty.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Oto kolejny przykład tego, do czego można go użyć:
  • Możesz użyć
    metaclass
    , aby zmienić funkcję swojej instancji (klasy).

class MetaMemberControl(type):
__slots__ = () @classmethod
def __prepare__(mcs, f_cls_name, f_cls_parents, # f_cls means: future class
meta_args=None, meta_options=None): # meta_args and meta_options is not necessarily needed, just so you know.
f_cls_attr = dict()
if not "do something or if you want to define your cool stuff of dict...":
return dict(make_your_special_dict=None)
else:
return f_cls_attr def __new__(mcs, f_cls_name, f_cls_parents, f_cls_attr,
meta_args=None, meta_options=None): original_getattr = f_cls_attr.get('__getattribute__')
original_setattr = f_cls_attr.get('__setattr__') def init_getattr(self, item):
if not item.startswith('_'): # you can set break points at here
alias_name = '_' + item
if alias_name in f_cls_attr['__slots__']:
item = alias_name
if original_getattr is not None:
return original_getattr(self, item)
else:
return super(eval(f_cls_name), self).__getattribute__(item) def init_setattr(self, key, value):
if not key.startswith('_') and ('_' + key) in f_cls_attr['__slots__']:
raise AttributeError(f"you can't modify private members:_{key}")
if original_setattr is not None:
original_setattr(self, key, value)
else:
super(eval(f_cls_name), self).__setattr__(key, value) f_cls_attr['__getattribute__'] = init_getattr
f_cls_attr['__setattr__'] = init_setattr cls = super().__new__(mcs, f_cls_name, f_cls_parents, f_cls_attr)
return cls
class Human(metaclass=MetaMemberControl):
__slots__ = ('_age', '_name') def __init__(self, name, age):
self._name = name
self._age = age def __getattribute__(self, item):
"""
is just for IDE recognize.
"""
return super().__getattribute__(item) """ with MetaMemberControl then you don't have to write as following
@property
def name(self):
return self._name @property
def age(self):
return self._age
"""
def test_demo():
human = Human('Carson', 27)
# human.age = 18 # you can't modify private members:_age <-- this is defined by yourself.
# human.k = 18 # 'Human' object has no attribute 'k' <-- system error.
age1 = human._age # It's OK, although the IDE will show some warnings. (Access to a protected member _age of a class) age2 = human.age # It's OK! see below:
"""
if you do not define `__getattribute__` at the class of Human,
the IDE will show you: Unresolved attribute reference 'age' for class 'Human'
but it's ok on running since the MetaMemberControl will help you.
"""
if __name__ == '__main__':
test_demo()

metaclass
jest potężny, jest wiele rzeczy (np. magia małp), które możesz z nią zrobić, ale bądź ostrożny, tylko Ty możesz to wiedzieć.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Zauważ, że w Pythonie 3.6 wprowadzono nową metodę Dunder
__init_subclass __ (cls, ** kwargs)
, aby zastąpić wiele typowych przypadków użycia metaklas. Jest to wywoływane, gdy tworzona jest podklasa, która definiuje klasę. Widzieć

python docs
https://docs.python.org/3.6/re ... .html
.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Klasa w Pythonie to obiekt i jak każdy inny obiekt jest instancją „czegoś”. To jest coś, co nazywa się metaklasą. Ta metaklasa to specjalny typ klasy, który tworzy obiekty innej klasy. Stąd metaklasa jest odpowiedzialna za tworzenie nowych klas. Pozwala to programiście dostosować sposób generowania klas.
Aby utworzyć metaklasę, metody są zwykle nadpisywane

new

() i

init

().

Nowy

() można zastąpić, aby zmienić sposób tworzenia obiektów, kiedy

inicjalizacja

() można zastąpić, aby zmienić sposób inicjalizacji obiektu. Metaklasę można utworzyć na kilka sposobów. Jednym ze sposobów jest użycie funkcji type (). Funkcja type () wywołana z 3 parametrami tworzy metaklasę. Parametry są następujące: -
  • Nazwa klasy
  • Krotka mająca klasy bazowe dziedziczone z klasy
  • Słownik zawierający wszystkie metody klas i zmienne klas

Innym sposobem tworzenia metaklasy jest użycie słowa kluczowego „metaklasa”. Zdefiniuj metaklasę jako prostą klasę. W parametrach odziedziczonej klasy podaj metaclass = metaclass_name
Metaclass może być szczególnie używany w następujących sytuacjach: -
  • kiedy określony efekt należy zastosować do wszystkich podklas
  • Wymaga automatycznej zmiany klasy (po utworzeniu)
  • Przez programistów API
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:


Doskonały

jakość obrazu:

Metaklasa to klasa, której instancje są klasami. Tak jak „zwykła” klasa definiuje zachowanie instancji klasy, metaklasa definiuje zachowanie klas i ich instancji.

Metaklasy nie są obsługiwane przez wszystkie języki programowania zorientowanego obiektowo. Języki programowania obsługujące metaklasy różnią się znacznie pod względem sposobu ich implementacji. Python je obsługuje.
Niektórzy programiści postrzegają metaklasy w Pythonie jako „rozwiązania oczekujące lub szukające problemu”.
Metaklasy mają wiele zastosowań.

logging and profiling
interface checking
registering classes at creation time
automatically adding new methods
automatic property creation
proxies
automatic resource locking/synchronization.

Definicja metaklasy

:

wypisze zawartość swoich argumentów do

Nowy

metoda i zwraca wyniki typu.

nowe wyzwanie

:

class LittleMeta(type):
def __new__(cls, clsname, superclasses, attributedict):
print("clsname: ", clsname)
print("superclasses: ", superclasses)
print("attributedict: ", attributedict)
return type.__new__(cls, clsname, superclasses, attributedict)

W poniższym przykładzie użyjemy metaklasy „LittleMeta”:
class S:
pass
class A(S, metaclass=LittleMeta):
pass
a = A()


Wynik

:
clsname: A
superclasses: (<class '__main__.S'>,)
attributedict: {'__module__': '__main__', '__qualname__': 'A'}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

metaclass
w Pythonie to klasa, która definiuje zachowanie klasy. Sama klasa jest instancją klasy
metaclass
. Klasa w Pythonie definiuje, jak będzie się zachowywać instancja klasy. Możemy dostosować proces tworzenia klasy, przekazując słowo kluczowe
metaclass
do definicji klasy. Można to również zrobić poprzez dziedziczenie z klasy, która już przekazała to słowo kluczowe.
class MyMeta(type):
passclass MyClass(metaclass=MyMeta):
passclass MySubclass(MyClass):
pass

Widzimy, że typ klasy
MyMeta
to
type
, a typ
MyClass
i
MySubClass
to
MyMeta
.
print(type(MyMeta))
print(type(MyClass))
print(type(MySubclass))<class 'type'>
<class '__main__.MyMeta'>
<class '__main__.MyMeta'>

Jeśli klasa jest zdefiniowana i brakuje elementu
metaclass
, zostanie użyty domyślny typ
metaclass
. Jeśli określono
metaclass
i nie jest on wystąpieniem
type ()
, jest używany bezpośrednio jako
metaclass
.
Metaklasy mogą być używane w szczególności do logowania, rejestrowania klas podczas tworzenia i profilowania. Wydają się być dość abstrakcyjnymi pojęciami i możesz się zastanawiać, czy w ogóle powinieneś ich używać.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Metaklasa to rodzaj klasy, która definiuje, jak klasa będzie się zachowywać, lub możemy powiedzieć, że sama klasa jest instancją metaklasy.

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się