ProgrammationDéveloppeur de bibliothèques Python

Comment fonctionne la méthode __getitem__ en Python, pourquoi l'implémenter et quoi considérer lors du traitement des tranches (slice) ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Historique de la question

La méthode getitem a été ajoutée à Python dans le cadre du protocole des séquences et des mapping. Grâce à cette méthode magique, les collections standards (list, tuple, dict, etc.) prennent en charge l'accès par index, clé et tranche. Dans les classes utilisateur, cette méthode permet aux objets de se comporter "comme des collections".

Problème

Sans l'implémentation de getitem, la classe utilisateur ne prend pas en charge l'indexation et l'itération avec for. L'implémentation uniquement pour les index simples n'est pas applicable si l'on veut un fonctionnement complet avec les tranches (slice), et ignorer les variétés d'index entraînera des erreurs.

Solution

Implémenter getitem pour prendre en charge à la fois les index et les objets de type slice, en les séparant dans le traitement. Cela permet d'utiliser des objets personnalisés dans les constructions standard de Python (par exemple, pour prendre en charge les tranches your_obj[1:5]).

Exemple de code :

class MyRange: def __init__(self, n): self.n = n def __getitem__(self, item): if isinstance(item, int): # Index individuel if 0 <= item < self.n: return item * 2 raise IndexError('index out of range') elif isinstance(item, slice): # Tranche return [self[i] for i in range(*item.indices(self.n))] else: raise TypeError('Type d'argument invalide : {}'.format(type(item))) mr = MyRange(10) print(mr[3]) # 6 print(mr[2:5]) # [4, 6, 8]

Caractéristiques clés :

  • getitem — la base du protocole des séquences et de l'itérabilité
  • Pour prendre en charge les tranches, il faut traiter slice séparément
  • Les exceptions IndexError et TypeError sont nécessaires pour un fonctionnement correct des fonctions standard (par exemple, list(), for)

Questions pièges.

L'implémentation uniquement de getitem permet-elle d'assigner des valeurs par index ?

Non. Pour prendre en charge l'assignation (your_obj[i] = value), il faut implémenter setitem. getitem ne répond qu'à la lecture.

Doit-on nécessairement retourner une liste pour une tranche, comme le fait list ?

Non. L'essentiel est de retourner une "séquence" en tenant compte du sens de la classe (on peut retourner le même type ou, par exemple, un tuple). L'important est que cela ait un sens dans le contexte de la tâche.

Pourquoi obtient-on parfois l'erreur TypeError : 'MyClass' object is not subscriptable ?

Ce message apparaît si vous essayez d'exécuter my_obj[0], mais la classe n'implémente pas getitem. Pour que la classe soit subscriptable (prenne en charge []), cette méthode est obligatoire.

Erreurs typiques et anti-patterns

  • Ne vérifient que int, ignorant slice : les tranches échouent toujours avec une erreur
  • Retourne un type incorrect (par exemple, None pour les tranches au lieu d'une séquence)
  • Ne lance pas IndexError, ce qui rend le cycle for incorrect

Exemple de la vie

Cas négatif

Implémenté getitem uniquement pour int, oublié slice. Toute tentative my_obj[2:5] entraîne une TypeError et échoue tout l'algorithme de traitement des collections.

Avantages :

  • Simplicité

Inconvénients :

  • Les tranches ne sont pas prises en charge, le code est incompatible avec la plupart des constructions standard

Cas positif

getitem est implémenté avec un traitement séparé pour slice et int. Les tranches et les index fonctionnent, les méthodes list(), map(), et l'itération sont prises en charge sans efforts supplémentaires.

Avantages :

  • La classe est compatible avec les outils standard de Python
  • Facile à étendre pour tout type d'index (tuple, str pour les matrices, etc.)

Inconvénients :

  • L'implémentation nécessite un peu plus de code et de tests