Métaclasses en Python : guide complet

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Les métaclasses en Python sont des classes spéciales qui créent et gèrent d'autres classes. Puisque tout est objet en Python, y compris les classes, les métaclasses peuvent être considérées comme des "classes pour les classes". Elles offrent un mécanisme pour contrôler le processus de création des classes et modifier leur comportement au moment de leur définition.

Principe de fonctionnement des métaclasses

Lorsque Python rencontre une définition de classe, il utilise automatiquement la métaclasse type pour créer cette classe. Le processus se déroule comme suit :

class MaClasse:
    pass

En interne, Python exécute :

MaClasse = type('MaClasse', (), {})

Où :

  • 'MaClasse' est le nom de la classe
  • () est le tuple des classes parentes
  • {} est le dictionnaire des attributs et méthodes de la classe

La métaclasse intégrée type est responsable de la création de toutes les classes en Python par défaut.

Création de métaclasses personnalisées

Les métaclasses personnalisées sont créées en héritant de type et en redéfinissant ses méthodes :

class MaMeta(type):
    def __new__(cls, nom, bases, attrs):
        print(f"Création de la classe {nom}")
        attrs['cree_par_metaclasse'] = True
        return super().__new__(cls, nom, bases, attrs)

class MaClasse(metaclass=MaMeta):
    pass

print(MaClasse.cree_par_metaclasse)  # True

Méthodes clés des métaclasses

__new__

Gère la création de la classe. Appelée avant __init__, elle retourne la nouvelle classe :

class MetaCreation(type):
    def __new__(cls, nom, bases, attrs):
        # Modification des attributs avant la création de la classe
        attrs['id_classe'] = id(cls)
        return super().__new__(cls, nom, bases, attrs)

__init__

Initialise la classe déjà créée :

class MetaInit(type):
    def __init__(cls, nom, bases, attrs):
        print(f"Initialisation de la classe {nom}")
        super().__init__(nom, bases, attrs)

__call__

Définit le comportement lors de la création d'instances de la classe :

class MetaAppel(type):
    def __call__(cls, *args, **kwargs):
        print(f"Création d'une instance de {cls.__name__}")
        return super().__call__(*args, **kwargs)

Exemples pratiques d'utilisation

Conversion automatique des attributs

class MetaMajuscule(type):
    def __new__(cls, nom, bases, attrs):
        attrs_maj = {}
        for nom_attr, valeur_attr in attrs.items():
            if not nom_attr.startswith('__'):
                attrs_maj[nom_attr.upper()] = valeur_attr
            else:
                attrs_maj[nom_attr] = valeur_attr
        return super().__new__(cls, nom, bases, attrs_maj)

class MaClasse(metaclass=MetaMajuscule):
    attr1 = 'valeur1'
    attr2 = 'valeur2'

print(hasattr(MaClasse, 'ATTR1'))  # True
print(hasattr(MaClasse, 'attr1'))  # False

Implémentation du pattern Singleton

class MetaSingleton(type):
    _instances = {}
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class ConnexionBase(metaclass=MetaSingleton):
    def __init__(self):
        print("Création de la connexion à la base de données")

db1 = ConnexionBase()
db2 = ConnexionBase()
print(db1 is db2)  # True

Validation de la structure des classes

class MetaValidee(type):
    def __new__(cls, nom, bases, attrs):
        # Validation : s'assurer qu'une méthode spécifique est présente
        if 'methode_requise' not in attrs:
            raise TypeError(f"La classe {nom} doit définir 'methode_requise'")
        return super().__new__(cls, nom, bases, attrs)

class MaClasseValidee(metaclass=MetaValidee):
    def methode_requise(self):
        pass

Blogs

Book Recommendations