¿Cómo se podría crear una función iterativa (u objeto iterador) en python?Crear un iterador básico de Python
Respuesta
Los objetos Iterator en python se ajustan al protocolo del iterador, lo que básicamente significa que proporcionan dos métodos: __iter__()
y next()
. El __iter__
devuelve el objeto iterador y se llama implícitamente al inicio de los bucles. El método next()
devuelve el siguiente valor y se invoca implícitamente en cada incremento de bucle. next()
genera una excepción StopIteration cuando no hay más valor para devolver, que es implícitamente capturado por las construcciones de bucle para detener la iteración.
Aquí está un ejemplo sencillo de un contador:
class Counter:
def __init__(self, low, high):
self.current = low
self.high = high
def __iter__(self):
return self
def next(self): # Python 3: def __next__(self)
if self.current > self.high:
raise StopIteration
else:
self.current += 1
return self.current - 1
for c in Counter(3, 8):
print c
Esto imprimirá:
3
4
5
6
7
8
Esto es más fácil escribir utilizando un generador, como se explica en la respuesta anterior:
def counter(low, high):
current = low
while current <= high:
yield current
current += 1
for c in counter(3, 8):
print c
La salida impresa será la misma. Debajo del capó, el objeto del generador admite el protocolo del iterador y hace algo más o menos similar al contador de la clase.
El artículo de David Mertz, Iterators and Simple Generators, es una muy buena introducción.
Tenga en cuenta que la función 'next()' no 'yield' values, '' return's them. –
Esto no es válido en Python 3 --- tiene que ser '__next __()'. – Aerovistae
Esto es sobre todo una buena respuesta, pero el hecho de que devuelve self es un poco subóptimo. Por ejemplo, si utilizó el mismo objeto contador en un bucle doble anidado, probablemente no obtendrá el comportamiento que usted quiso decir. –
En primer lugar la itertools module es increíblemente útil para todo tipo de casos en los que un iterador sería útil, pero aquí es todo lo que necesita para crear un iterador en Python:
rendimiento
¿No es genial? El rendimiento se puede utilizar para reemplazar una normal return en una función. Devuelve el objeto exactamente igual, pero en lugar de destruir el estado y salir, guarda el estado para cuando quieras ejecutar la siguiente iteración. Aquí es un ejemplo de ello en la acción tiró directamente de la itertools function list:
def count(n=0):
while True:
yield n
n += 1
Como se indica en la descripción funciones (es la count() función del módulo itertools ...), que produce un iterador que devuelve enteros consecutivos comenzando con n.
Generator expressions son otras latas de gusanos (¡gusanos increíbles!). Se pueden usar en lugar de List Comprehension para ahorrar memoria (las listas de comprensión crean una lista en la memoria que se destruye después del uso si no está asignada a una variable, pero las expresiones del generador pueden crear un Objeto generador ... que es una forma elegante de decir Iterador). Este es un ejemplo de una definición de la expresión del generador:
gen = (n for n in xrange(0,11))
Esto es muy similar a nuestra definición iterador anteriormente, excepto la gama completa está predeterminada a estar entre 0 y 10.
acabo de encontrar xrange() (me sorprendió no haberlo visto antes ...) y lo agregué al ejemplo anterior. xrange() es una versión iterativa de range() que tiene la ventaja de no preconstruir la lista. Sería muy útil si tuviera un corpus de datos gigantes para iterar y solo tuviera tanta memoria para hacerlo.
Hay cuatro maneras de construir una función iterativa:
- crear un generador (utiliza el yield keyword)
- utilizar una expresión generador (genexp)
- crear un iterador (define
__iter__
and__next__
(onext
en Python 2.x)) - crear una función que Python puede iterar por sí mismo (defines
__getitem__
)
Ejemplos:
# generator
def uc_gen(text):
for char in text:
yield char.upper()
# generator expression
def uc_genexp(text):
return (char.upper() for char in text)
# iterator protocol
class uc_iter():
def __init__(self, text):
self.text = text
self.index = 0
def __iter__(self):
return self
def __next__(self):
try:
result = self.text[self.index].upper()
except IndexError:
raise StopIteration
self.index += 1
return result
# getitem method
class uc_getitem():
def __init__(self, text):
self.text = text
def __getitem__(self, index):
result = self.text[index].upper()
return result
para ver los cuatro métodos de acción:
for iterator in uc_gen, uc_genexp, uc_iter, uc_getitem:
for ch in iterator('abcde'):
print ch,
print
que se traduce en:
A B C D E
A B C D E
A B C D E
A B C D E
Nota:
Los dos generador tipos (uc_gen
y uc_genexp
) no pueden ser reversed()
; el iterador simple (uc_iter
) necesitaría el método mágico __reversed__
(que debe devolver un nuevo iterador que vaya hacia atrás); y la GetItem iterables (uc_getitem
) debe tener el método __len__
mágica:
# for uc_iter
def __reversed__(self):
return reversed(self.text)
# for uc_getitem
def __len__(self)
return len(self.text)
Para responder a la pregunta secundaria del Coronel de pánico sobre un infinito iterador con pereza evaluado, aquí están los ejemplos, utilizando cada uno de los cuatro métodos anteriores:
# generator
def even_gen():
result = 0
while True:
yield result
result += 2
# generator expression
def even_genexp():
return (num for num in even_gen()) # or even_iter or even_getitem
# not much value under these circumstances
# iterator protocol
class even_iter():
def __init__(self):
self.value = 0
def __iter__(self):
return self
def __next__(self):
next_value = self.value
self.value += 2
return next_value
# getitem method
class even_getitem():
def __getitem__(self, index):
return index * 2
import random
for iterator in even_gen, even_genexp, even_iter, even_getitem:
limit = random.randint(15, 30)
count = 0
for even in iterator():
print even,
count += 1
if count >= limit:
break
print
que se traduce en (al menos para mi análisis de la muestra):
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32
Me gusta este resumen porque está completo. Esas tres formas (rendimiento, expresión del generador e iterador) son esencialmente las mismas, aunque algunas son más convenientes que otras. El operador de rendimiento captura la "continuación" que contiene el estado (por ejemplo, el índice que estamos haciendo). La información se guarda en el "cierre" de la continuación. El modo de iterador guarda la misma información dentro de los campos del iterador, que es esencialmente lo mismo que un cierre. El método __getitem__ es un poco diferente porque se indexa en los contenidos y no es de naturaleza iterativa. – Ian
No está incrementando el índice en su último enfoque, 'uc_getitem()'.En realidad, en la reflexión, no debería incrementar el índice, porque no lo está manteniendo. Pero tampoco es una forma de iteración abstracta. –
@metaperl: En realidad, lo es. En los cuatro casos anteriores, puede usar el mismo código para iterar. –
Veo que algunos de ustedes están haciendo return self
en __iter__
. Sólo quería señalar que __iter__
sí mismo puede ser un generador (eliminando así la necesidad de __next__
y elevar StopIteration
excepciones)
class range:
def __init__(self,a,b):
self.a = a
self.b = b
def __iter__(self):
i = self.a
while i < self.b:
yield i
i+=1
Por supuesto que aquí uno puede así realizar directamente un generador, pero para las clases más complejas que puede sé útil.
¡Genial! Es tan aburrido escribir 'return self' en' __iter__'. Cuando iba a intentar usar 'yield', encontré tu código haciendo exactamente lo que quiero probar. – Ray
Pero en este caso, ¿cómo se implementaría 'next()'? 'return iter (self) .next()'? – Lenna
@Lenna, ya está "implementado" porque iter (self) devuelve un iterador, no una instancia de rango. – Manux
Esta es una función iterable sin yield
. Se hace uso de la función iter
y un cierre que mantiene su estado en un mutable (list
) en el ámbito circundante para el pitón 2.
def count(low, high):
counter = [0]
def tmp():
val = low + counter[0]
if val < high:
counter[0] += 1
return val
return None
return iter(tmp, None)
Para Python 3, el estado de cierre se mantiene en una inmutable en el ámbito circundante y nonlocal
se usa en el ámbito local para actualizar la variable de estado.
def count(low, high):
counter = 0
def tmp():
nonlocal counter
val = low + counter
if val < high:
counter += 1
return val
return None
return iter(tmp, None)
Prueba;
for i in count(1,10):
print(i)
1
2
3
4
5
6
7
8
9
Siempre aprecio un uso inteligente de two-arg 'iter', pero solo para ser claro: esto es más complejo y menos eficiente que simplemente usar una función de generador basada en' yield'; Python tiene un montón de soporte de intérprete para las funciones del generador basadas en el rendimiento que no puede aprovechar aquí, lo que hace que este código sea mucho más lento. Up-votado no obstante. – ShadowRanger
Esta pregunta se trata de objetos iterables, no de iteradores. En Python, las secuencias son iterables también, por lo que una forma de hacer una clase iterable es hacer que se comporte como una secuencia, es decir, darle los métodos __getitem__
y __len__
. He probado esto en Python 2 y 3.
class CustomRange:
def __init__(self, low, high):
self.low = low
self.high = high
def __getitem__(self, item):
if item >= len(self):
raise IndexError("CustomRange index out of range")
return self.low + item
def __len__(self):
return self.high - self.low
cr = CustomRange(0, 10)
for i in cr:
print(i)
- 1. ¿Cómo crear un servidor Java básico?
- 2. iterador perezosa de Python
- 3. importaciones básico Python cuestionar
- 4. Python, más allá de lo básico
- 5. python - tamaño del iterador invocable?
- 6. Cuándo escribir un iterador?
- 7. Equivalente a Numpy.argsort() en python básico?
- 8. Sitio web de Django, juego básico de Python en 2d
- 9. Cómo restringir un iterador a ser un iterador directo?
- 10. Hacer que un iterador de python vaya hacia atrás?
- 11. archivo Python iterador sobre un archivo binario con nuevos modismo
- 12. ¿readlines() devuelve una lista o un iterador en Python 3?
- 13. Scala convertir un iterador [Opción [T]] en un iterador [T]
- 14. ¿Cómo implementaría un bucle de evento básico?
- 15. ¿Cómo configurar un proyecto básico de rubí?
- 16. División de una cadena en un iterador
- 17. ¿Cómo usar un iterador?
- 18. Escribiendo un depurador muy básico
- 19. Cómo clonar un iterador?
- 20. Enhebrado básico
- 21. ¿Cómo sobrescribir el comportamiento de la lista de Python (iterador)?
- 22. Histograma básico en JFreeChart
- 23. En caso de que un objeto implemente un iterador u otro objeto que implemente un iterador
- 24. Ejemplo básico de pySQLite?
- 25. Trig básico: Problema de math.atan()
- 26. básico de Java tarea
- 27. Uso básico de Waypoint
- 28. Flujo básico de Struts
- 29. de iterador de salida
- 30. ¿Cómo puedo alimentar la entrada estándar de un subproceso desde un iterador de Python?
Aquí hay dos preguntas, ambas importantes. ¿Cómo hacer que una clase sea iterable (es decir, con la que pueda pasar el bucle)? ¿Y cómo hacer una función que devuelve una secuencia con evaluación perezosa? –
Un buen ejercicio, creo, es escribir una clase que represente los números pares (una secuencia infinita). –
@ColonelPanic: De acuerdo, agregué el número infinito de ejemplos a [mi respuesta] (http://stackoverflow.com/a/7542261/208880). –