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__