Python Language
metaclasses
Zoeken…
Invoering
Metaclasses stellen u in staat om het gedrag van Python-klassen diepgaand aan te passen (in termen van hoe ze worden gedefinieerd, geïnstantieerd, geopend en meer) door het type
metaclass te vervangen dat nieuwe klassen standaard gebruiken.
Opmerkingen
Houd er bij het ontwerpen van uw architectuur rekening mee dat veel dingen die met metaclasses kunnen worden bereikt, ook kunnen worden bereikt met behulp van eenvoudigere semantiek:
- Traditionele erfenis is vaak meer dan voldoende.
- Klasseontwerpers kunnen functionaliteit combineren in klassen op een ad-hocbenadering.
- Python 3.6 introduceert
__init_subclass__()
waarmee een klasse kan deelnemen aan de creatie van zijn subklasse.
Basic Metaclasses
Wanneer type
met drie argumenten wordt aangeroepen, gedraagt het zich als de (meta) -klasse die het is, en maakt een nieuwe instantie, dat wil zeggen. het produceert een nieuwe klasse / type.
Dummy = type('OtherDummy', (), dict(x=1))
Dummy.__class__ # <type 'type'>
Dummy().__class__.__class__ # <type 'type'>
Het is mogelijk om subklasse type
om een aangepaste metaclass te maken.
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
Nu hebben we een nieuwe aangepaste mytype
metaclass die kan worden gebruikt om klassen op dezelfde manier als type
.
MyDummy = mytype('MyDummy', (), dict(x=2))
MyDummy.__class__ # <class '__main__.mytype'>
MyDummy().__class__.__class__ # <class '__main__.mytype'>
MyDummy.__custom_attribute__ # 2
Wanneer we een nieuwe klasse maken met behulp van het trefwoord class
de metaclass standaard gekozen op basis van de basisklassen.
>>> class Foo(object):
... pass
>>> type(Foo)
type
In het bovenstaande voorbeeld is de enige basisklasse object
dus onze metaclass is het type object
, dat is het type
. Het is mogelijk om de standaard te overschrijven, maar het hangt ervan af of we Python 2 of Python 3 gebruiken:
Een speciaal kenmerk op __metaclass__
kan worden gebruikt om de metaclass op te geven.
class MyDummy(object):
__metaclass__ = mytype
type(MyDummy) # <class '__main__.mytype'>
Een speciaal argument voor metaclass
sleutelwoorden geeft de metaclass aan.
class MyDummy(metaclass=mytype):
pass
type(MyDummy) # <class '__main__.mytype'>
Alle trefwoordargumenten (behalve de metaclass
) in de metaclass
worden doorgegeven aan de metaclass. Dus class MyDummy(metaclass=mytype, x=2)
zal x=2
als een trefwoordargument mytype
aan de constructor mytype
.
Lees deze uitgebreide beschrijving van python metaklassen voor meer informatie.
Singletons die metaclasses gebruiken
Een singleton is een patroon dat de instantiatie van een klasse beperkt tot één instantie / object. Voor meer informatie over python singleton design patterns, zie hier .
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
Een metaclass gebruiken
Syntaxis van de metaclass
class MyClass(object):
__metaclass__ = SomeMetaclass
class MyClass(metaclass=SomeMetaclass):
pass
Compatibiliteit Python 2 en 3 met six
import six
class MyClass(six.with_metaclass(SomeMetaclass)):
pass
Aangepaste functionaliteit met metaclasses
De functionaliteit in metaclasses kan worden gewijzigd, zodat telkens wanneer een klasse wordt gebouwd, een string naar standaarduitvoer wordt afgedrukt of een uitzondering wordt gegenereerd. Deze metaclass drukt de naam af van de klasse die wordt gebouwd.
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
U kunt de metaclass als volgt gebruiken:
class Spam(metaclass=VerboseMetaclass):
def eggs(self):
print("[insert example string here]")
s = Spam()
s.eggs()
De standaardoutput is:
Creating class Spam
[insert example string here]
Inleiding tot metaclasses
Wat is een metaclass?
In Python is alles een object: gehele getallen, strings, lijsten, zelfs functies en klassen zelf zijn objecten. En elk object is een instantie van een klasse.
Om de klasse van een object x te controleren, kan men type(x)
aanroepen, dus:
>>> type(5)
<type 'int'>
>>> type(str)
<type 'type'>
>>> type([1, 2, 3])
<type 'list'>
>>> class C(object):
... pass
...
>>> type(C)
<type 'type'>
De meeste klassen in python zijn exemplaren van het type
. type
zelf is ook een klasse. Dergelijke klassen waarvan de instanties ook klassen zijn, worden metaclasses genoemd.
De eenvoudigste metaclass
OK, dus er is al één metaclass in Python: type
. Kunnen we er nog een maken?
class SimplestMetaclass(type):
pass
class MyClass(object):
__metaclass__ = SimplestMetaclass
Dat voegt geen functionaliteit toe, maar het is een nieuwe metaclass, zie dat MyClass nu een instantie is van SimplestMetaclass:
>>> type(MyClass)
<class '__main__.SimplestMetaclass'>
Een metaclass die iets doet
Een metaclass die iets doet, vervangt meestal de __new__
type
om enkele eigenschappen van de aan te maken klasse te wijzigen, voordat de oorspronkelijke __new__
die de klasse maakt:
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)
De standaard metaclass
Je hebt misschien gehoord dat alles in Python een object is. Het is waar en alle objecten hebben een klasse:
>>> type(1)
int
Letterlijke 1 is een instantie van int
. Laten we een klasse aangeven:
>>> class Foo(object):
... pass
...
Laten we het nu instantiëren:
>>> bar = Foo()
Wat is de klasse van bar
?
>>> type(bar)
Foo
Nice, bar
is een exemplaar van Foo
. Maar wat is de klasse van Foo
zelf?
>>> type(Foo)
type
Ok, Foo
zelf is een type
exemplaar. Hoe zit het met het type
zelf?
>>> type(type)
type
Dus wat is een metaclass? Laten we nu net doen alsof het slechts een mooie naam is voor de klasse van een klasse. Takeaways:
- Alles is een object in Python, dus alles heeft een klasse
- De klasse van een klasse wordt een metaclass genoemd
- De standaard metaclass is
type
en verreweg de meest voorkomende metaclass
Maar waarom zou je iets moeten weten over metaclasses? Nou, Python zelf is behoorlijk "hackbaar", en het concept van metaclass is belangrijk als je geavanceerde dingen doet zoals meta-programmeren of als je wilt bepalen hoe je klassen worden geïnitialiseerd.