https://docs.python.org/3/reference/datamodel.html
PEP 3115 - Metaclasses in Python 3000
https://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/
http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python
W Pythonie mamy do czynienia z obiektami. Funkcja jest obiektem, klasa jest obiektem, itp. Obiekt instancji jest instancją obiektu klasy, obiekt klasy jest instacją obiektu metaklasy.
Funkcjonalność metaklas częściowo pokrywa się z funkcjonalnością
dekoratorów klas.
Metaklasy są typowo używane jako fabryki klas (class-factory),
ale badano różne inne zastosowania. Wg dokumentacji Pythona:
Some ideas that have been explored include enum, logging,
interface checking, automatic delegation, automatic property creation,
proxies, frameworks, and automatic resource locking/synchronization.
Kiedy wykonywana jest instrukcja 'class', wykonywane są następujące kroki:
Metaklasy najczęściej implementują dwie metody (__init__, __new__), przejmując kontrolę nad procedurą tworzenia i inicjalizacji nowej instancji klasy. Klasy otrzymują nową warstwę logiki.
# Domyślnie klasy są konstruowane przy użyciu type(). # Nazwa klasy jest łączona z wynikiem type(name, bases, clsdict), np. # MyClass = type("MyClass", (), {}) # class MyClass: pass # równoważnym kod MyClass = type("MyClass", (BaseClass,), {'attribute' : 42}) # To jest równoważne z kodem: class MyClass(BaseClass): attribute = 42
# Metaklasy najczęściej implementują dwie metody. class Meta(type): # dziedziczenie z 'type' w klasach w nowym stylu def __init__ ... # opcjonalne def __new__(mcls, name, bases, clsdict): clsdict['foo'] = 'Meta was here' # Dodajemy atrybut 'foo' do słownika klasy # zanim zostanie utworzona nowa klasa. return type.__new__(mcls, name, bases, clsdict) #class C(object): # klasy w nowym stylu # __metaclass__ = Meta # składnia Py2.6+ # Teraz zamiast type(name, bases, clsdict) # do budowy klasy będzie użyte Meta. class C(metaclass=Meta): pass # składnia Py3 (PEP 3115) print(dir(C)) # znajdziemy klucz 'foo'
https://docs.python.org/3/library/abc.html
https://docs.python.org/3/library/collections.abc.html
https://www.geeksforgeeks.org/abstract-base-class-abc-in-python/
Abstract Base Class (abc) in Python.
https://realpython.com/python-interface/
Implementing an Interface in Python.
PEP 3119 - Introducing Abstract Base Classes (Py3)
PEP 3141 - A Type Hierarchy for Numbers.
Number (the root of the numeric hierarchy),
Complex (real, imag, conversions to complex and bool),
Real (conversion to float, comparisons),
Rational (numerator, denominator, conversion to float),
Integral (conversion to int).
Abstrakcyjne klasy bazowe (ABC) dostarczają standardowego sposobu sprawdzania, czy obiekt spełnia daną specyfikację. Próba utworzenia podklasy bez nadpisania przygotowanych metod z klasy bazowej nie uda się.
Moduł 'abc' dostarcza infrastrukturę do definiowania ABCs. W module 'collections' znajdują się pewne konkretne klasy wyprowadzone z ABCs. W podmodule 'collections.abc' znajdują się pewne ABCs, które można użyć do testowania interfejsów.
Abstrakcyjne klasy bazowe mają zestaw metod abstrakcyjnych.
from abc import ABCMeta class MyABC(object): # tworzenie abstrakcyjnej klasy bazowej __metaclass__ = ABCMeta # Py2.7 instrukcje
from abc import ABCMeta class MyABC(metaclass=ABCMeta): # Py3.4+ instrukcje
# Klasa pomocnicza 'ABC' ma 'ABCMeta' jako metaklasę. # Klasę wprowadzono, aby uniknąć niezręcznej składni metaklas. # Teraz można zrobić zwykłe dziedziczenie po 'ABC'. from abc import ABC # Py3.4+ from abc import abstractmethod # dekorator wskazujący na metodę abstrakcyjną class MyABC(ABC): # tworzenie abstrakcyjnej klasy bazowej @abstractmethod def my_abstract_method(self, arg1): instrukcje @classmethod # ważna jest kolejność dekoratorów @abstractmethod def my_abstract_classmethod(cls, arg2): instrukcje @staticmethod @abstractmethod def my_abstract_staticmethod(arg3): instrukcje @property @abstractmethod def my_abstract_property(self): instrukcje @my_abstract_property.setter @abstractmethod def my_abstract_property(self, val): instrukcje
# Przyłączamy do naszej abstrakcyjne klasy bazowej wbudowaną klasę tuple. # register(subclass) # Register subclass as a "virtual subclass" of this ABC. MyABC.register(tuple) assert issubclass(tuple, MyABC) assert isinstance(tuple(), MyABC)
# Tworzenie nowej klasy przez bezpośrednie dziedziczenie # po abstrakcyjne klasie bazowej. from collections.abc import Sequence class C(Sequence): # bezpośrednie dziedziczenie def __init__(self): # dodatkowa metoda nie wymagana przez ABC instrukcje def __getitem__(self, index): # wymagana metoda abstrakcyjna instrukcje def __len__(self): # wymagana metoda abstrakcyjna instrukcje def count(self, value): # mixin method (opcjonalnie nadpisana) instrukcje def index(self, value): # mixin method (opcjonalnie nadpisana) instrukcje assert issubclass(C, Sequence) assert isinstance(C(), Sequence)
# Rejestracja istniejącej klasy jako 'wirtualnej podklasy' # pewnej abstrakcyjnej klasy bazowej. from collections.abc import Sequence class D: def __init__(self): # dodatkowa metoda nie wymagana przez ABC instrukcje def __getitem__(self, index): # metoda abstrakcyjna instrukcje def __len__(self): # metoda abstrakcyjna instrukcje def count(self, value): # mixin method instrukcje def index(self, value): # mixin method instrukcje Sequence.register(D) # rejestracja zamiast dziedziczenia assert issubclass(D, Sequence) assert isinstance(D(), Sequence)
# Możemy pytać klasę lub instancję o pewną funkcjonalność. import collections size = None #if isinstance(my_variable, collections.Sized): # Py2.7 if isinstance(my_variable, collections.abc.Sized): # Py3.3+ size = len(my_variable) # wiemy, że istnieje __len__