Introspection et Réflexion en Python : Accès Dynamique aux Attributs et Méthodes

En Python, la réflexion (ou introspection) permet d'inspecter et de manipuler dynamiquement les entités du langage à l'exécution. Cette capacité est particulièrement utile lorsque les identifiants des attributs, méthodes ou fonctions ne sont connus qu'au moment de l'exécution, sous forme de chaînes de caractères. Elle constitue le fondement des interfaces dynamiques, des routeurs HTTP et des architectures orientées plugins.

  1. Manipulation dynamique des instances

Les primitives intégrées getattr(), hasattr(), setattr() et delattr() forment l'API principale de cette approche. Elles résolvent une chaîne de caractères vers un pointeur mémoire valide au sein de l'objet cible, permettant ainsi une interrogation flexible de l'espace de noms.

Récupération et invocation ciblée

L'utilisation de getattr() permet de traverser les membres d'une instance sans connaître l'identifiant à l'avance. Si la cible correspond à une méthode, l'appelant reçoit une référence appelable directement exécutable.

class Client:
    def __init__(self, identifiant, score):
        self.ref = identifiant
        self.niveau = score

    def generer_etiquette(self):
        return f"{self.ref}_{self.niveau}"

instance = Client("user_99", 42)
valeur = getattr(instance, "ref")
print(valeur)  # user_99

fonction_dyn = getattr(instance, "generer_etiquette")
resultat = fonction_dyn()
print(resultat)  # user_99_42

Vérification d'existence et mutation

Pour éviter les exceptions AttributeError, hasattr() valide la présence du membre. L'injection et la suppression s'effectuent via setattr() et delattr(), modifiant directement le dictionnaire interne de l'instance.

entite = Client("dev_ops", 3)

if hasattr(entite, "niveau"):
    print("Attribut valide détecté")

setattr(entite, "zone", "eu-west-2")
print(entite.zone)  # eu-west-2

delattr(entite, "zone")
# Un accès ultérieur à entite.zone lèverait une AttributeError

  1. Introspection au niveau des modules

Les modules Python étant eux-mêmes des objets, les mêmes mécanismes s'appliquent à l'espace de noms global d'un fichier source. Cette technique permet de charger des constantes, des routinnes ou des constructeurs sans importation explicite préalable, ce qui optimise les chargements conditionnels.

# Fichier: endpoints.py
# API_KEY = "sk_123"
# def calculer(): return "OK"
# class Processeur: pass

import endpoints

constante = getattr(endpoints, "API_KEY")
print(constante)  # sk_123

routine = getattr(endpoints, "calculer")
print(routine())  # OK

constructeur = getattr(endpoints, "Processeur")
objet = constructeur()

  1. Architecture par dispatching dynamique

Dans les applications web, il est courant de mapper une URL à une fonction spécifique. Remplacer une cascade de conditions if/elif par un routage réflexif simplifie considérablement la maintenance et l'extension du code.

L'implémentation suivante intercepte une entrée utilisateur, vérifie son existence dans le module cible, et délègue l'exécution si la ressource est disponible, simulant le comportement d'un framework léger.

import endpoints

def routeur(requete):
    if hasattr(endpoints, requete):
        cible = getattr(endpoints, requete)
        if callable(cible):
            return cible()
    return "Erreur 404"

# Simulation d'appels
print(routeur("calculer"))  # OK
print(routeur("inexistant"))  # Erreur 404

Étiquettes: python-reflection Introspection getattr hasattr dynamic-dispatch

Publié le 28 septembre à 03h35