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:

Python 2.x 2.7

Een speciaal kenmerk op __metaclass__ kan worden gebruikt om de metaclass op te geven.

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

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
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

Een metaclass gebruiken

Syntaxis van de metaclass

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



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow