¿Por qué definir__getitem__ en una clase lo hace iterable en python?
iterator overloading (6)
¿Por qué la definición de __getitem__ en una clase lo hace iterable?
Por ejemplo si escribo:
class b:
def __getitem__(self, k):
return k
cb = b()
for k in cb:
print k
Me sale la salida:
0
1
2
3
4
5
6
7
8
...
Realmente esperaría ver un error devuelto desde "para k en cb:"
El soporte de __getitem__
para __getitem__
se puede ver como una "característica heredada" que permitió una transición más suave cuando PEP234 introdujo la iterabilidad como concepto principal. Solo se aplica a las clases sin __iter__
cuyo __getitem__
acepta los números enteros 0, 1, & c, y genera IndexError
una vez que el índice se vuelve demasiado alto (si es que __iter__
), normalmente las clases de "secuencia" codificadas antes de que apareciera __iter__
(aunque nada le impide codificar nuevas clases) demasiado).
Personalmente, preferiría no confiar en esto en el nuevo código, aunque no está en desuso ni va a desaparecer (también funciona bien en Python 3), por lo que es solo una cuestión de estilo y gusto ("explícito es mejor que implícito", por lo que Prefiero explícitamente apoyar la iterabilidad en lugar de confiar en __getitem__
soportándola implícitamente para mí, pero no es un gran problema.
Esto es así por razones históricas. Antes de Python 2.2, __getitem__ era la única forma de crear una clase que pudiera repetirse con el bucle for. En 2.2 se agregó el protocolo __iter__, pero para mantener la compatibilidad con versiones anteriores, __getitem__ aún funciona para los bucles.
Métodos especiales como __getitem__
añaden comportamientos especiales a los objetos, incluida la iteración.
http://docs.python.org/reference/datamodel.html#object.getitem http://docs.python.org/reference/datamodel.html#object.getitem
"para los bucles, espere que se genere un IndexError para índices ilegales para permitir una detección adecuada del final de la secuencia".
Levante IndexError para señalar el final de la secuencia.
Su código es básicamente equivalente a:
i = 0
while True:
try:
yield object[i]
i += 1
except IndexError:
break
Donde objeto es lo que estás iterando en el bucle for.
Porque cb[0]
es lo mismo que cb.__getitem__(0)
. Consulte la documentación de python sobre esto.
__getitem__
es anterior al protocolo del iterador, y en el pasado era la única forma de hacer que las cosas fueran iterables. Como tal, todavía es compatible como un método de iteración. Esencialmente, el protocolo de iteración es:
Compruebe si hay un método de
__iter__
. Si existe, use el nuevo protocolo de iteración.De lo contrario, intente llamar a
__getitem__
con valores enteros sucesivamente mayores hasta que se genere IndexError.
(2) solía ser la única forma de hacer esto, pero tenía la desventaja de que asumía más de lo necesario para soportar solo la iteración. Para admitir la iteración, tenía que admitir el acceso aleatorio, que era mucho más costoso para cosas como archivos o transmisiones de red, donde ir hacia adelante era fácil, pero ir hacia atrás requeriría almacenar todo. __iter__
permitió la iteración sin acceso aleatorio, pero dado que el acceso aleatorio por lo general permite la iteración de todos modos, y dado que la compatibilidad con versiones anteriores sería mala, __getitem__
todavía es compatible.