ProgrammationDéveloppeur Backend

Comment l'interface de séquence (Sequence) est-elle implémentée en Python ? Pourquoi est-il nécessaire de mettre en œuvre des méthodes spéciales __getitem__, __len__, et quels pièges existent ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Historique de la question :

Les séquences sont l'un des concepts les plus anciens et fondamentaux en Python. Les exemples classiques incluent les listes (list), les chaînes (str), et les tuples (tuple). Un protocole spécial a été établi pour interagir avec les séquences : il est nécessaire d'implémenter les méthodes __getitem__ et __len__. Cela permet à l'objet de se comporter "comme une séquence" — supportant l'indexation, les tranches, le travail dans des boucles et même certaines fonctions standard.

Problème :

Sans la bonne implémentation de ces méthodes, une classe utilisateur ne pourra pas fonctionner avec des opérations d'indexation, des boucles for, ou des fonctions comme len(). Souvent, les développeurs débutants n'implémentent qu'une seule des méthodes, ne tiennent pas compte de la gestion des exceptions, ne gèrent pas le support des tranches (slice) et obtiennent un comportement incorrect ou inattendu.

Solution :

Il est nécessaire d'implémenter la méthode __getitem__(self, key) pour supporter l'indexation et les tranches, ainsi que __len__(self) pour travailler avec la fonction len() et une itérabilité correcte. Pour supporter les tranches, il faut distinguer le type de key dans __getitem__ et traiter correctement les objets de type slice.

Exemple de code :

def is_even(n): return n % 2 == 0 class EvenSequence: def __init__(self, size): self.size = size def __getitem__(self, index): if isinstance(index, slice): return [x for x in range(self.size)[index] if is_even(x)] if index < 0 or index >= self.size: raise IndexError('Index hors de portée') return index if is_even(index) else None def __len__(self): return self.size

Caractéristiques clés :

  • Interaction avec la plupart des fonctions et de la syntaxe Python, si les deux méthodes sont implémentées.
  • Pour le support des tranches, il faut traiter les objets de type slice dans getitem.
  • En n'implémentant pas len, l'objet ne fonctionnera pas avec len() et certaines fonctions standard.

Questions pièges.

Peut-on se contenter de la méthode getitem pour que l'objet fonctionne comme une séquence ?

En partie. Si l'on implémente seulement __getitem__, on peut itérer sur l'objet avec for et indexer des éléments, mais len() ne fonctionnera pas.

Exemple de code :

class SeqOnlyGetitem: def __getitem__(self, index): if index >= 10: raise IndexError return index * 2 s = SeqOnlyGetitem() for x in s: print(x) # Fonctionne (itérable) # print(len(s)) # Erreur TypeError

Comment gérer les indices négatifs et que se passe-t-il si on ne les prend pas en compte ?

Les indices négatifs sont une caractéristique clé des séquences en Python. Si on ne les gère pas, l'objet se comporte de manière inattendue pour l'utilisateur.

class NegIndex: def __init__(self, data): self.data = data def __getitem__(self, index): if index < 0: index += len(self.data) return self.data[index]

Faut-il implémenter contains ou iter pour sa classe Sequence ?

Ce n'est pas obligatoire. Si __getitem__ et __len__ sont implémentés, la fonction in fonctionnera, et for les utilisera pour l'itération. La mise en œuvre de ces méthodes directement n'est généralement pas nécessaire, mais peut améliorer les performances.

Erreurs typiques et anti-patterns

  • Ne vérifient pas la plage des indices, ce qui entraîne des IndexError ou des résultats inattendus.
  • Ne mettent pas en œuvre le support des tranches (slice), donc obj[2:10:2] génère une erreur.
  • Oublient les indices négatifs (obj[-1]).

Exemple de la vie réelle

Cas négatif

Un développeur a implémenté seulement getitem pour sa classe uniquement pour des indices positifs. Le module a passé une partie des tests unitaires, mais lors de l'utilisation réelle, en essayant de récupérer un indice inexistant ou un indice négatif, tout s'est effondré.

Avantages :

  • Mise en œuvre initiale rapide.

Inconvénients :

  • Erreurs inattendues en pratique.
  • Incommodité lors de l'utilisation (impossibilité de récupérer les derniers éléments via des indices négatifs, pas de support des tranches, len() ne fonctionne pas).

Cas positif

L'équipe a implémenté les deux méthodes (getitem et len), a pris en compte les tranches, les indices négatifs, et a levé des exceptions appropriées. La classe finale a fonctionné dans tous les cas d'utilisation standard de Python.

Avantages :

  • Comportement prévisible du point de vue de l'API Python.
  • Commodité d'utilisation, minimum de bogues.

Inconvénients :

  • Un peu plus de code, attention aux détails lors de la conception.