ProgrammationDéveloppeur Python Senior

Qu'est-ce que les descripteurs en Python ? Comment sont-ils réalisés, à quoi servent-ils, quelle est la différence avec property et quelles sont les erreurs typiques lors de leur utilisation ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

Un descripteur est un objet qui implémente l'une des méthodes get, set et delete. Il permet de contrôler l'accès aux attributs de la classe.

Méthodes standard du descripteur :

  • __get__(self, instance, owner)
  • __set__(self, instance, value)
  • __delete__(self, instance)

Les descripteurs peuvent être :

  • Classiques (data descriptors) — implémentent set, ont la priorité sur les attributs normaux ;
  • Non-classiques (non-data descriptors) — n'implémentent que get, passent après un attribut normal.

Exemple d'utilisation :

class OnlyPositive: def __init__(self): self._name = '_value' def __get__(self, instance, owner): return instance.__dict__[self._name] def __set__(self, instance, value): if value < 0: raise ValueError('Value must be >= 0') instance.__dict__[self._name] = value class Account: value = OnlyPositive() def __init__(self, value): self.value = value acc = Account(10) acc.value = -1 # ValueError!

property n'est qu'un sucre syntaxique pour créer des descripteurs au niveau de la classe.

Question piégeuse

Que se passe-t-il si l'on crée un descripteur en tant qu'attribut d'instance et non de classe ?

Beaucoup pensent que le descripteur fonctionnera, mais ce n'est pas le cas.

Réponse correcte :

Le descripteur fonctionne uniquement s'il est défini directement en tant qu'attribut de la classe. Si le descripteur est défini en tant qu'attribut d'instance, les méthodes get/set ne seront pas appelées — la logique normale d'accès à l'attribut s'applique.

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet


Histoire

Utilisation de property au lieu d'un vrai descripteur

Pour la validation et d'autres attributs liés, le développeur a utilisé une simple property, ce qui entraînait un doublonnage fréquent de la logique et l'impossibilité de réutiliser le code entre différentes classes.


Histoire

Non-respect de l'immuabilité lors de la réutilisation du stockage

Le descripteur stockait des données dans un attribut interne de sa propre classe (self.x), et non dans le stockage interne de l'instance, ce qui rendait l'attribut "commun", permettant à différentes instances de la classe de se réécrire mutuellement des valeurs — des données "fuiteraient" entre les objets.


Histoire

Confusion entre data descriptor et non-data descriptor

Dans une hiérarchie complexe, l'implémentation de set a été omise, ce qui a entraîné l'écrasement du descripteur par un attribut normal de l'instance, brisant tout le mécanisme de validation — le bug ne se manifestait pas toujours, rendant le débogage difficile.