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:

Python 2.x 2.7

Do określenia metaklasy można użyć specjalnego atrybutu na poziomie klasy __metaclass__ .

class MyDummy(object):
    __metaclass__ = mytype
type(MyDummy)  # <class '__main__.mytype'>
Python 3.x 3.0

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
Python 2.x 2.7
class MySingleton(object):
    __metaclass__ = SingletonType
Python 3.x 3.0
class MySingleton(metaclass=SingletonType):
    pass
MySingleton() is MySingleton()  # True, only one instantiation occurs

Korzystanie z metaklasy

Składnia metaklasy

Python 2.x 2.7
class MyClass(object):
    __metaclass__ = SomeMetaclass
Python 3.x 3.0
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.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow