son que palabras los iteradores generadores generador funcion explicacion creacion python object iterator

que - Construye un iterador Python básico



que es un iterable en python (9)

En primer lugar, el módulo de itertools es increíblemente útil para todo tipo de casos en los que un iterador sería útil, pero aquí está todo lo que necesita para crear un iterador en python:

rendimiento

¿No es genial? El rendimiento se puede utilizar para reemplazar un rendimiento normal en una función. Devuelve el objeto de la misma manera, pero en lugar de destruir el estado y salir, guarda el estado para cuando desea ejecutar la siguiente iteración. Este es un ejemplo de ello en acción extraído directamente de la lista de funciones de itertools :

def count(n=0): while True: yield n n += 1

Como se indica en la descripción de funciones (es la función count () del módulo itertools ...), produce un iterador que devuelve números enteros consecutivos que comienzan con n.

Las expresiones del generador son otra lata de gusanos (¡gusanos impresionantes!). Se pueden usar en lugar de una Comprensión de lista para guardar la memoria (las comprensiones de lista crean una lista en la memoria que se destruye después de su uso si no se asigna a una variable, pero las expresiones del generador pueden crear un Objeto generador ... que es una forma elegante diciendo iterador). Aquí hay un ejemplo de una definición de expresión de generador:

gen = (n for n in xrange(0,11))

Esto es muy similar a nuestra definición de iterador anterior, excepto que el rango completo está predeterminado entre 0 y 10.

Acabo de encontrar xrange () (sorprendió que no lo había visto antes ...) y lo agregué al ejemplo anterior. xrange () es una versión iterable de range () que tiene la ventaja de no pre-construir la lista. Sería muy útil si tuviera un gigantesco cuerpo de datos para iterar y solo tuviera tanta memoria para hacerlo.

¿Cómo se crearía una función iterativa (u objeto iterador) en python?


Esta es una función iterable sin yield . Hace uso de la función iter y un cierre que mantiene su estado mutable ( list ) en el ámbito de aplicación de python 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 de forma inmutable en el ámbito de cierre y nonlocal se utiliza 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


Esta pregunta es sobre objetos iterables, no sobre iteradores. En Python, las secuencias también son iterables, por lo que una forma de hacer una clase iterable es hacer que se comporte como una secuencia, es decir, __len__ 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)


Hay cuatro formas de construir una función iterativa:

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 en acción:

for iterator in uc_gen, uc_genexp, uc_iter, uc_getitem: for ch in iterator(''abcde''): print ch, print

Lo que resulta en:

A B C D E A B C D E A B C D E A B C D E

Nota :

Los dos tipos de generador ( uc_gen y uc_genexp ) no se pueden reversed() ; el iterador simple ( uc_iter ) necesitaría el método mágico __reversed__ (que debe devolver un nuevo iterador que va hacia atrás); y el objeto iteratable ( uc_getitem ) debe tener el método mágico __len__ :

# 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 Panic sobre un iterador infinitamente evaluado perezosamente, aquí están esos 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

Lo que resulta en (al menos para mi ejecución de 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


Inspirado por la respuesta de Matt Gregory, aquí hay un iterador un poco más complicado que devolverá a, b, ..., z, aa, ab, ..., zz, aaa, aab, ..., zzy, zzz

class AlphaCounter: def __init__(self, low, high): self.current = low self.high = high def __iter__(self): return self def __next__(self): # Python 3: def __next__(self) alpha = '' abcdefghijklmnopqrstuvwxyz'' n_current = sum([(alpha.find(self.current[x])* 26**(len(self.current)-x-1)) for x in range(len(self.current))]) n_high = sum([(alpha.find(self.high[x])* 26**(len(self.high)-x-1)) for x in range(len(self.high))]) if n_current > n_high: raise StopIteration else: increment = True ret = '''' for x in self.current[::-1]: if ''z'' == x: if increment: ret += ''a'' else: ret += ''z'' else: if increment: ret += alpha[alpha.find(x)+1] increment = False else: ret += x if increment: ret += ''a'' tmp = self.current self.current = ret[::-1] return tmp for c in AlphaCounter(''a'', ''zzz''): print(c)


Si buscas algo corto y simple, tal vez sea suficiente para ti:

class A(object): def __init__(self, l): self.data = l def __iter__(self): return iter(self.data)

ejemplo de uso:

In [3]: a = A([2,3,4]) In [4]: [i for i in a] Out[4]: [2, 3, 4]


Todas las respuestas en esta página son realmente geniales para un objeto complejo. Pero para aquellos que contienen tipos iterables incorporados como atributos, como str , list , set o dict , o cualquier implementación de collections.Iterable . collections.Iterable , puede omitir ciertas cosas en su clase.

class Test(object): def __init__(self, string): self.string = string def __iter__(self): # since your string is already iterable return (ch for ch in string)

Se puede utilizar como:

for x in Test("abcde"): print(x) # prints # a # b # c # d # e


Veo que algunos de ustedes se return self en __iter__ . Solo quería señalar que __iter__ sí mismo puede ser un generador (eliminando así la necesidad de __next__ y generando excepciones de StopIteration )

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, aquí también se puede hacer un generador directamente, pero para clases más complejas puede ser útil.


Los objetos de iterador 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 __iter__ implícitamente al comienzo 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 se captura implícitamente mediante construcciones de bucle para detener la iteración.

Aquí hay un ejemplo simple 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 de escribir usando un generador, como se explica en una 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. Bajo el capó, el objeto generador es compatible con el protocolo del iterador y hace algo más o menos similar a la clase Contador.

El artículo de David Mertz, Iteradores y generadores simples , es una introducción bastante buena.