Lorsqu’un nom d’attribut est connu seulement au moment de l’exécution, l’accès classique avec le point ne suffit plus. La fonction getattr() permet alors de lire dynamiquement un attribut, une méthode ou une valeur de configuration, tout en contrôlant le comportement si cet élément n’existe pas. Je détaille ici sa syntaxe, ses usages concrets, ses différences avec hasattr() et les pièges qui peuvent rendre le code difficile à maintenir.
Retenir l’essentiel sur getattr en Python
- getattr() récupère un attribut à partir de son nom sous forme de chaîne.
- Sa forme complète est getattr(objet, nom, valeur_par_défaut).
- Sans valeur par défaut, un attribut absent provoque une AttributeError.
- La fonction peut aussi récupérer une méthode, qu’il est ensuite possible d’appeler.
- Elle est utile pour les configurations, les plugins et la sérialisation, mais doit rester lisible et contrôlée.
Comprendre ce que fait réellement getattr
En Python, getattr() est une fonction intégrée qui cherche un attribut sur un objet à partir d’un nom texte. La syntaxe minimale est simple :
getattr(objet, "nom_attribut")
Dans cet exemple, Python cherche l’attribut appelé nom_attribut et renvoie sa valeur. Le résultat peut être une chaîne, un nombre, une liste, une propriété ou même une méthode. C’est cette souplesse qui rend la fonction intéressante pour le code dynamique.
class Utilisateur:
def __init__(self):
self.nom = "Claire"
self.age = 31
utilisateur = Utilisateur()
print(getattr(utilisateur, "nom"))
print(getattr(utilisateur, "age"))
Le programme affiche ici Claire puis 31. L’écriture directe utilisateur.nom serait plus courte, mais elle ne fonctionnerait pas si le nom de l’attribut était stocké dans une variable.
attribut = "nom"
valeur = getattr(utilisateur, attribut)
print(valeur)
Dans mes propres scripts, c’est généralement le critère qui guide le choix. Si le nom est fixe, l’accès avec le point reste plus clair. Si le nom vient d’un fichier de configuration, d’un formulaire ou d’une table de correspondance, getattr devient beaucoup plus pertinent.
Ajouter une valeur par défaut pour éviter AttributeError
La forme la plus pratique accepte un troisième argument :
getattr(objet, "nom_attribut", valeur_par_defaut)
Si l’attribut existe, Python renvoie sa valeur. S’il est absent, la valeur par défaut est utilisée au lieu de lever une exception.
ville = getattr(utilisateur, "ville", "Ville inconnue")
print(ville)
Dans ce cas, l’objet ne possède pas forcément d’attribut ville, mais le programme continue avec Ville inconnue. Cette approche convient très bien aux données optionnelles et aux versions différentes d’une configuration.
Il faut cependant distinguer un attribut absent d’un attribut qui vaut déjà None. La valeur par défaut n’est utilisée que lorsque la recherche échoue.
class Profil:
langue = None
profil = Profil()
print(getattr(profil, "langue", "français"))
Le résultat sera None, et non français, car l’attribut existe. C’est une nuance importante lorsque None représente une valeur volontaire. Si l’application doit traiter None comme une absence, il faut ajouter une vérification séparée.
Sans troisième argument, une faute de frappe comme getattr(utilisateur, "nmo") produit une AttributeError. Je préfère souvent laisser cette exception apparaître dans le code interne, car elle révèle immédiatement un bug, et réserver la valeur par défaut aux données réellement facultatives.

Lire des méthodes, des modules et des options dynamiques
getattr() ne se limite pas aux variables d’instance. Il peut aussi récupérer une méthode. Il suffit ensuite d’ajouter les parenthèses pour l’exécuter.
class Message:
def envoyer(self):
return "Message envoyé"
message = Message()
nom_methode = "envoyer"
methode = getattr(message, nom_methode)
print(methode())
Cette technique permet de choisir une action à partir d’un texte. Elle apparaît dans les systèmes de plugins, les routeurs simples, les commandes administratives ou les fonctions qui sélectionnent un traitement selon un type d’événement.
actions = {
"creation": "creer",
"suppression": "supprimer"
}
action = "creation"
nom_methode = actions[action]
getattr(service, nom_methode)()
Le dictionnaire rend ici la correspondance visible et contrôlable. Je déconseille en revanche d’exécuter directement n’importe quel nom reçu d’un utilisateur. Un nom d’attribut externe doit être comparé à une liste blanche, sinon le programme risque de donner accès à des méthodes qui n’étaient pas prévues.
La fonction fonctionne aussi avec des modules et des classes :
import math
nom_fonction = "sqrt"
racine = getattr(math, nom_fonction)
print(racine(81))
Ce mécanisme est utile lorsque le choix d’une fonction dépend d’un paramètre de configuration. Pour quelques options seulement, un dictionnaire de fonctions reste souvent plus lisible. Pour une architecture extensible, l’accès dynamique peut toutefois éviter une longue suite de conditions.
Choisir entre getattr, accès direct, hasattr et try except
Ces solutions répondent à des besoins proches, mais elles ne doivent pas être utilisées indifféremment. Le choix dépend surtout de la manière dont le nom de l’attribut est connu et de l’importance de l’erreur.
| Solution | Quand l’utiliser | Point fort | Limite |
|---|---|---|---|
objet.nom |
Le nom est connu dans le code | Très lisible | Pas dynamique |
getattr() |
Le nom arrive dans une variable | Valeur par défaut intégrée | Peut masquer une faute de frappe |
hasattr() |
Il faut seulement tester l’existence | Test court | Ne renvoie pas la valeur |
try/except |
L’absence est une situation exceptionnelle | Gestion précise de l’erreur | Plus verbeux |
Un piège courant consiste à écrire hasattr(), puis à appeler immédiatement getattr(). Entre les deux opérations, une propriété peut produire un effet ou changer de valeur. Dans la plupart des cas, il est plus propre de récupérer directement l’attribut avec une valeur par défaut ou de gérer une seule exception.
try:
valeur = getattr(objet, nom)
except AttributeError:
valeur = valeur_par_defaut
Cette forme est intéressante lorsque l’absence doit être distinguée d’une autre erreur interne. À l’inverse, si l’on veut simplement afficher une option facultative, getattr(objet, nom, None) sera plus compact.
Éviter les erreurs fréquentes et les effets inattendus
Ne pas confondre une propriété avec une simple variable
La recherche d’attribut respecte le fonctionnement normal de Python. Elle peut donc déclencher une propriété définie avec @property, ou une logique personnalisée dans __getattribute__() et __getattr__().
class Compte:
@property
def solde(self):
print("Calcul du solde")
return 150
compte = Compte()
print(getattr(compte, "solde"))
L’appel affiche le message avant de renvoyer le montant. Cela signifie que getattr n’est pas toujours une lecture passive. Une propriété peut effectuer un calcul, consulter une ressource ou lever une exception particulière.
Protéger les noms venant de l’extérieur
Un nom d’attribut provenant d’une requête HTTP, d’un fichier ou d’une entrée utilisateur ne devrait pas être utilisé sans contrôle. Même si getattr() ne permet pas à lui seul d’exécuter du code arbitraire, il peut exposer des attributs internes ou des méthodes sensibles.
methodes_autorisees = {
"resume": service.resume,
"statut": service.statut
}
nom = "resume"
resultat = methodes_autorisees[nom]()
Cette version est plus sûre lorsqu’il existe peu d’actions possibles. Elle documente aussi clairement l’interface publique. Pour une application exposée sur Internet, je privilégie ce type de liste blanche explicite plutôt qu’un accès libre à tous les attributs.
Lire aussi : Python - Comment vérifier si un nombre est pair facilement ?
Surveiller les fautes de frappe et les performances
Une valeur par défaut peut rendre l’application trop silencieuse. Si "username" est écrit à la place de "user_name", le programme continuera peut-être avec une valeur vide et le problème apparaîtra beaucoup plus tard.
Pour quelques appels isolés, le coût de getattr() ne pose pas de problème. Dans une boucle très chaude exécutée des millions de fois, un accès direct ou une référence mise en cache sera généralement plus simple et légèrement plus rapide. La lisibilité du code doit rester le premier critère, car l’écart de performance est rarement décisif dans une application classique.
Utiliser getattr avec des classes bien conçues
Dans une classe, il est possible de définir __getattr__() pour fournir un comportement de secours lorsque l’attribut n’existe pas. Cette méthode ne doit pas être confondue avec la fonction intégrée getattr() utilisée par le code appelant.
class Parametres:
def __init__(self):
self.langue = "fr"
def __getattr__(self, nom):
return f"Option inconnue : {nom}"
parametres = Parametres()
print(getattr(parametres, "langue"))
print(getattr(parametres, "theme"))
Le premier appel renvoie fr. Le second déclenche __getattr__() et renvoie un message personnalisé. Ce modèle peut être utile pour des objets qui représentent une configuration flexible, mais je l’emploie avec retenue. Un attribut absent qui ressemble à une vraie faute de programmation ne devrait pas être transformé en résultat valide sans avertissement.
Pour les projets avec analyse statique et annotations de type, un accès dynamique est aussi moins facile à comprendre pour les outils comme les vérificateurs de types. Quand les attributs possibles sont connus à l’avance, une classe clairement annotée ou un dictionnaire typé offrira souvent une meilleure expérience de maintenance.
Le bon réflexe pour intégrer getattr dans un code fiable
La règle que je retiens est simple. Utilisez l’accès direct quand le nom est connu, getattr() quand le nom est réellement dynamique, et une valeur par défaut seulement lorsque l’absence est normale.
Pour un code robuste, vérifiez trois points avant de l’adopter : la source du nom, le comportement attendu si l’attribut manque et la possibilité qu’une propriété exécute une logique supplémentaire. Cette petite discipline évite la majorité des usages confus.
Bien employée, cette fonction devient un outil discret mais puissant pour les configurations, les extensions et les systèmes pilotés par des noms. Elle ne remplace pas une structure claire, mais elle permet à Python de rester flexible sans multiplier les conditions inutiles.