Recherche…


Introduction

Les métaclasses vous permettent de modifier en profondeur le comportement des classes Python (en termes de définition, d’instanciation, d’accès, etc.) en remplaçant la métaclasse de type utilisée par défaut par les nouvelles classes.

Remarques

Lors de la conception de votre architecture, considérez que de nombreuses choses peuvent être accomplies avec des métaclasses en utilisant une sémantique plus simple:

  • L'héritage traditionnel est souvent plus que suffisant.
  • Les décorateurs de classes peuvent intégrer des fonctionnalités dans une classe selon une approche ad hoc.
  • Python 3.6 introduit __init_subclass__() qui permet à une classe de participer à la création de sa sous-classe.

Métaclasses de base

Lorsque type est appelé avec trois arguments, il se comporte comme la classe (méta) et crée une nouvelle instance, c.-à-d. il produit une nouvelle classe / type.

Dummy = type('OtherDummy', (), dict(x=1))
Dummy.__class__              # <type 'type'> 
Dummy().__class__.__class__  # <type 'type'> 

Il est possible de sous-classer le type pour créer une métaclasse personnalisée.

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

Maintenant, nous avons une nouvelle métaclasse personnalisée mytype qui peut être utilisée pour créer des classes de la même manière que le type .

MyDummy = mytype('MyDummy', (), dict(x=2))
MyDummy.__class__              # <class '__main__.mytype'>
MyDummy().__class__.__class__  # <class '__main__.mytype'>
MyDummy.__custom_attribute__   # 2

Lorsque nous créons une nouvelle classe en utilisant le mot-clé class la métaclasse est choisie par défaut en fonction des classes de base.

>>> class Foo(object):
...     pass

>>> type(Foo)
type

Dans l'exemple ci-dessus, la seule classe de base est object donc notre métaclasse sera le type d' object , qui est le type . Il est possible de remplacer la valeur par défaut, mais cela dépend si nous utilisons Python 2 ou Python 3:

Python 2.x 2.7

Un attribut spécial au niveau de la classe __metaclass__ peut être utilisé pour spécifier la métaclasse.

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

Un argument spécial de mot-clé de metaclass spécifie la métaclasse.

class MyDummy(metaclass=mytype):
    pass
type(MyDummy)  # <class '__main__.mytype'>

Tout argument de mot-clé (sauf la metaclass ) dans la déclaration de classe sera transmis à la métaclasse. Ainsi, la class MyDummy(metaclass=mytype, x=2) transmettra x=2 comme argument mot-clé au constructeur mytype .

Lisez cette description détaillée des méta-classes de python pour plus de détails.

Singletons utilisant des métaclasses

Un singleton est un modèle qui limite l'instanciation d'une classe à une instance / objet. Pour plus d'informations sur les modèles de conception de singleton python, voir ici .

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

Utiliser une métaclasse

La syntaxe de la métaclasse

Python 2.x 2.7
class MyClass(object):
    __metaclass__ = SomeMetaclass
Python 3.x 3.0
class MyClass(metaclass=SomeMetaclass):
    pass

Compatibilité Python 2 et 3 avec six

import six

class MyClass(six.with_metaclass(SomeMetaclass)):
    pass

Fonctionnalité personnalisée avec des métaclasses

La fonctionnalité dans les métaclasses peut être modifiée de sorte que chaque fois qu'une classe est construite, une chaîne est imprimée sur la sortie standard ou une exception est levée. Cette métaclasse affichera le nom de la classe en cours de construction.

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

Vous pouvez utiliser la métaclasse comme ceci:

class Spam(metaclass=VerboseMetaclass):
    def eggs(self):
        print("[insert example string here]")
s = Spam()
s.eggs()

La sortie standard sera:

Creating class Spam
[insert example string here]

Introduction aux Métaclasses

Qu'est-ce qu'une métaclasse?

En Python, tout est un objet: les entiers, les chaînes de caractères, les listes, même les fonctions et les classes elles-mêmes sont des objets. Et chaque objet est une instance d'une classe.

Pour vérifier la classe d'un objet x, on peut appeler le type(x) , donc:

>>> type(5)
<type 'int'>
>>> type(str)
<type 'type'>
>>> type([1, 2, 3])
<type 'list'>

>>> class C(object):
...     pass
...
>>> type(C)
<type 'type'>    

La plupart des classes de python sont des instances de type . type lui-même est aussi une classe. Ces classes dont les instances sont aussi des classes sont appelées métaclasses.

La métaclasse la plus simple

OK, donc il y a déjà une métaclasse en Python: type . Pouvons-nous en créer un autre?

class SimplestMetaclass(type):
    pass

class MyClass(object):
    __metaclass__ = SimplestMetaclass

Cela n'ajoute aucune fonctionnalité, mais c'est une nouvelle métaclasse, voyez que MyClass est maintenant une instance de SimplestMetaclass:

>>> type(MyClass)
<class '__main__.SimplestMetaclass'>

Une métaclasse qui fait quelque chose

Une méta - classe qui fait quelque chose remplace généralement le type de __new__ , de modifier certaines propriétés de la classe à créer, avant d' appeler l'original __new__ qui crée la classe:

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)

La métaclasse par défaut

Vous avez peut-être entendu dire que tout en Python est un objet. C'est vrai, et tous les objets ont une classe:

>>> type(1)
int

Le littéral 1 est une instance de int . Permet de déclarer une classe:

>>> class Foo(object):
...    pass
...

Maintenant, permet de l'instancier:

>>> bar = Foo()

Quelle est la classe de bar ?

>>> type(bar)
Foo

Nice, le bar est une instance de Foo . Mais quelle est la classe de Foo elle-même?

>>> type(Foo)
type

Ok, Foo lui-même est une instance de type . Qu'en est-il du type lui-même?

>>> type(type)
type

Alors, quelle est une métaclasse? Pour l'instant, supposons que ce n'est qu'un nom de fantaisie pour la classe d'une classe. Plats à emporter:

  • Tout est un objet en Python, donc tout a une classe
  • La classe d'une classe s'appelle une métaclasse
  • La métaclasse par défaut est type , et de loin c'est la métaclasse la plus courante

Mais pourquoi devriez-vous connaître les métaclasses? Eh bien, Python lui-même est assez "piratable", et le concept de métaclasse est important si vous faites des choses avancées comme la méta-programmation ou si vous voulez contrôler la façon dont vos classes sont initialisées.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow