Python Language
Metaklasy
Szukaj…
Wprowadzenie
Metaklasy pozwalają na głęboką modyfikację zachowania klas Python (pod względem sposobu ich definiowania, tworzenia instancji, uzyskiwania dostępu itp.) Poprzez zastąpienie metaklasy type
domyślnie używanej przez nowe klasy.
Uwagi
Projektując architekturę, należy wziąć pod uwagę, że wiele rzeczy, które można osiągnąć za pomocą metaklas, można również osiągnąć za pomocą prostszej semantyki:
- Tradycyjne dziedzictwo jest często więcej niż wystarczające.
- Dekoratorzy klas mogą łączyć funkcje w klasy w trybie ad hoc.
- Python 3.6 wprowadza
__init_subclass__()
który pozwala klasie uczestniczyć w tworzeniu jej podklasy.
Podstawowe metaklasy
Gdy type
jest wywoływany z trzema argumentami, zachowuje się jak klasa (meta) i tworzy nową instancję, tj. produkuje nową klasę / typ.
Dummy = type('OtherDummy', (), dict(x=1))
Dummy.__class__ # <type 'type'>
Dummy().__class__.__class__ # <type 'type'>
Jest to możliwe do podklasy type
, aby utworzyć niestandardowy metaklasa.
class mytype(type):
def __init__(cls, name, bases, dict):
# call the base initializer
type.__init__(cls, name, bases, dict)
# perform custom initialization...
cls.__custom_attribute__ = 2
Teraz mamy nową niestandardową metaklasę typu mytype
której można używać do tworzenia klas w taki sam sposób jak type
.
MyDummy = mytype('MyDummy', (), dict(x=2))
MyDummy.__class__ # <class '__main__.mytype'>
MyDummy().__class__.__class__ # <class '__main__.mytype'>
MyDummy.__custom_attribute__ # 2
Kiedy tworzymy nową klasę za pomocą słowa kluczowego class
metaklasa jest domyślnie wybierana na podstawie class
podstawowych.
>>> class Foo(object):
... pass
>>> type(Foo)
type
W powyższym przykładzie jedyną klasą podstawową jest object
więc nasza metaklasa będzie typem object
, który jest type
. Możliwe jest zastąpienie wartości domyślnej, jednak zależy to od tego, czy używamy Python 2, czy Python 3:
Do określenia metaklasy można użyć specjalnego atrybutu na poziomie klasy __metaclass__
.
class MyDummy(object):
__metaclass__ = mytype
type(MyDummy) # <class '__main__.mytype'>
Specjalny argument słowa kluczowego metaclass
określa metaklasę.
class MyDummy(metaclass=mytype):
pass
type(MyDummy) # <class '__main__.mytype'>
Wszelkie argumenty słów kluczowych (oprócz metaclass
) w deklaracji klasy zostaną przekazane do metaklasy. Zatem class MyDummy(metaclass=mytype, x=2)
przekaże x=2
jako argument słowa kluczowego do konstruktora mytype
.
Przeczytaj szczegółowy opis meta-klas Pythona, aby uzyskać więcej informacji.
Singletony wykorzystujące metaklasy
Singleton to wzorzec, który ogranicza tworzenie instancji klasy do jednej instancji / obiektu. Aby uzyskać więcej informacji na temat wzorców projektowych singletona w języku Python, zobacz tutaj .
class SingletonType(type):
def __call__(cls, *args, **kwargs):
try:
return cls.__instance
except AttributeError:
cls.__instance = super(SingletonType, cls).__call__(*args, **kwargs)
return cls.__instance
class MySingleton(object):
__metaclass__ = SingletonType
class MySingleton(metaclass=SingletonType):
pass
MySingleton() is MySingleton() # True, only one instantiation occurs
Korzystanie z metaklasy
Składnia metaklasy
class MyClass(object):
__metaclass__ = SomeMetaclass
class MyClass(metaclass=SomeMetaclass):
pass
Zgodność Pythona 2 i 3 z six
import six
class MyClass(six.with_metaclass(SomeMetaclass)):
pass
Niestandardowa funkcjonalność z metaklasami
Funkcjonalność w metaklasach można zmienić, tak aby za każdym razem, gdy budowana jest klasa, ciąg znaków był drukowany na standardowe wyjście lub zgłaszany był wyjątek. Ta metaklasa wypisze nazwę budowanej klasy.
class VerboseMetaclass(type):
def __new__(cls, class_name, class_parents, class_dict):
print("Creating class ", class_name)
new_class = super().__new__(cls, class_name, class_parents, class_dict)
return new_class
Możesz użyć metaklasy w następujący sposób:
class Spam(metaclass=VerboseMetaclass):
def eggs(self):
print("[insert example string here]")
s = Spam()
s.eggs()
Standardowe wyjście będzie:
Creating class Spam
[insert example string here]
Wprowadzenie do metaklasy
Co to jest metaklasa?
W Pythonie wszystko jest przedmiotem: liczby całkowite, ciągi, listy, a nawet same funkcje i klasy są obiektami. I każdy obiekt jest instancją klasy.
Aby sprawdzić klasę obiektu x, można wywołać type(x)
, więc:
>>> type(5)
<type 'int'>
>>> type(str)
<type 'type'>
>>> type([1, 2, 3])
<type 'list'>
>>> class C(object):
... pass
...
>>> type(C)
<type 'type'>
Większość klas w Pythonie jest instancjami type
. sam type
jest również klasą. Takie klasy, których instancje są również klasami, nazywane są metaklasami.
Najprostsza metaklasa
OK, więc w Pythonie jest już jedna metaklasa: type
. Czy możemy stworzyć kolejny?
class SimplestMetaclass(type):
pass
class MyClass(object):
__metaclass__ = SimplestMetaclass
To nie dodaje żadnej funkcjonalności, ale jest to nowa metaklasa, zobacz, że MyClass jest teraz instancją SimplestMetaclass:
>>> type(MyClass)
<class '__main__.SimplestMetaclass'>
Metaklasa, która coś robi
Metaklasa, która robi coś zwykle nadpisuje type
__new__
, aby zmodyfikować niektóre właściwości tworzonej klasy, przed wywołaniem oryginalnej __new__
która tworzy klasę:
class AnotherMetaclass(type):
def __new__(cls, name, parents, dct):
# cls is this class
# name is the name of the class to be created
# parents is the list of the class's parent classes
# dct is the list of class's attributes (methods, static variables)
# here all of the attributes can be modified before creating the class, e.g.
dct['x'] = 8 # now the class will have a static variable x = 8
# return value is the new class. super will take care of that
return super(AnotherMetaclass, cls).__new__(cls, name, parents, dct)
Domyślna metaklasa
Być może słyszałeś, że wszystko w Pythonie jest przedmiotem. To prawda, a wszystkie obiekty mają klasę:
>>> type(1)
int
Dosłowne 1 jest instancją int
. Deklarujmy klasę:
>>> class Foo(object):
... pass
...
Teraz pozwala na utworzenie go:
>>> bar = Foo()
Jaka jest klasa bar
?
>>> type(bar)
Foo
Fajnie, bar
jest przykładem Foo
. Ale jaka jest sama klasa Foo
?
>>> type(Foo)
type
Ok, sam Foo
jest instancją type
. Jak o type
się?
>>> type(type)
type
Czym jest metaklasa? Na razie udawajmy, że to tylko fantazyjne imię dla klasy klasy. Na wynos:
- W Pythonie wszystko jest przedmiotem, więc wszystko ma swoją klasę
- Klasa klasy nazywana jest metaklasą
- Domyślna metaklasa to
type
i zdecydowanie jest to najczęstsza metaklasa
Ale dlaczego warto wiedzieć o metaklasach? Cóż, sam Python jest dość „hakowalny”, a koncepcja metaklasy jest ważna, jeśli robisz zaawansowane rzeczy, takie jak metaprogramowanie lub chcesz kontrolować sposób inicjowania klas.