Pourquoi la définition de __getitem__ sur une classe la rend-elle itérable?
Par exemple, si j'écris:
class b:
def __getitem__(self, k):
return k
cb = b()
for k in cb:
print k
J'obtiens la sortie:
0
1
2
3
4
5
6
7
8
...
Je m'attendrais vraiment à voir une erreur renvoyée par "for k in cb:"
Si vous jetez un œil à PEP234 définissant les itérateurs, il dit:
1. An object can be iterated over with "for" if it implements
__iter__() or __getitem__().
2. An object can function as an iterator if it implements next().
Prise en charge d'itération pour __getitem__
peut être considéré comme une "fonction héritée" qui a permis une transition plus fluide lorsque PEP234 a introduit l'itérabilité comme concept principal. Il s'applique uniquement aux classes sans __iter__
dont __getitem__
accepte les entiers 0, 1, & c et lève IndexError
une fois que l'index est trop élevé (si jamais), généralement des classes "séquence" codées avant __iter__
est apparu (bien que rien ne vous empêche de coder de nouvelles classes de cette façon aussi).
Personnellement, je préfère ne pas m'appuyer sur cela dans le nouveau code, bien qu'il ne soit pas obsolète ni ne disparaisse (fonctionne très bien dans Python 3 aussi), donc c'est juste une question de style et de goût ("explicite vaut mieux qu'implicite", je préfère donc explicitement prendre en charge l'itérabilité plutôt que de compter sur __getitem__
le soutenant implicitement pour moi - mais pas un bigge).
__getitem__
est antérieur au protocole itérateur, et était dans le passé le moyen niquement de rendre les choses itérables. En tant que tel, il est toujours pris en charge comme méthode d'itération. Essentiellement, le protocole d'itération est:
Recherchez un __iter__
méthode. S'il existe, utilisez le nouveau protocole d'itération.
Sinon, essayez d'appeler __getitem__
avec des valeurs entières successivement plus grandes jusqu'à ce qu'il déclenche IndexError.
(2) était le seul moyen de le faire, mais avait l'inconvénient qu'il supposait plus que ce qui était nécessaire pour prendre en charge juste l'itération. Pour prendre en charge l'itération, vous deviez prendre en charge l'accès aléatoire, qui était beaucoup plus cher pour des choses comme les fichiers ou les flux réseau où aller en avant était facile, mais revenir en arrière nécessiterait de tout stocker. __iter__
a permis l'itération sans accès aléatoire, mais comme l'accès aléatoire autorise généralement l'itération de toute façon, et parce que rompre la compatibilité descendante serait mauvais, __getitem__
est toujours pris en charge.
Méthodes spéciales telles que __getitem__
ajoute des comportements spéciaux aux objets, y compris l'itération.
http://docs.python.org/reference/datamodel.html#object. getitem
"pour les boucles, attendez qu'une IndexError soit levée pour les index illégaux afin de permettre une détection correcte de la fin de la séquence."
Soulevez IndexError pour signaler la fin de la séquence.
Votre code est fondamentalement équivalent à:
i = 0
while True:
try:
yield object[i]
i += 1
except IndexError:
break
Où objet est ce que vous êtes en train d'itérer dans la boucle for.
Il en est ainsi pour des raisons historiques. Avant Python 2.2 __getitem__ était le seul moyen de créer une classe qui pouvait être répétée avec la boucle for. En 2.2, le protocole __iter__ a été ajouté mais pour conserver la compatibilité descendante, __getitem__ fonctionne toujours dans les boucles for. .
Parce que cb[0]
Est identique à cb.__getitem__(0)
. Voir la documentation python à ce sujet.