python iterator overloading

¿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.



Si PEP234 un vistazo a PEP234 definiendo iteradores, dice:

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().


__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:

  1. Compruebe si hay un método de __iter__ . Si existe, use el nuevo protocolo de iteración.

  2. 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.