wordcloud tag python generator fibonacci

python - tag - ¿Cómo escribir una clase de generador?



python tag cloud (4)

¿Cómo escribir una clase de generador?

Ya casi está ahí, escribiendo una clase de iterador (muestro un Generador al final de la respuesta), pero a __next__ se le llama cada vez que llama al objeto con next , devolviendo un objeto generador. En su lugar, use __iter__ :

>>> class Fib: ... def __init__(self): ... self.a, self.b = 0, 1 ... def __iter__(self): ... while True: ... yield self.a ... self.a, self.b = self.b, self.a+self.b ... >>> f = iter(Fib()) >>> for i in range(3): ... print(next(f)) ... 0 1 1

Para hacer de la clase misma un iterador:

class Fib: def __init__(self): self.a, self.b = 0, 1 def __next__(self): return_value = self.a self.a, self.b = self.b, self.a+self.b return return_value def __iter__(self): return self

Y ahora:

>>> f = iter(Fib()) >>> for i in range(3): ... print(next(f)) ... 0 1 1

¿Por qué el valor self.a no se imprime?

Aquí está tu código original con mis comentarios:

class Fib: def __init__(self): self.a, self.b = 0, 1 def __next__(self): yield self.a # yield makes .__next__() return a generator! self.a, self.b = self.b, self.a+self.b f = Fib() for i in range(3): print(next(f))

Así que cada vez que llama a la next(f) , obtiene el objeto generador que __next__ devuelve:

<generator object __next__ at 0x000000000A3E4F68> <generator object __next__ at 0x000000000A3E4F68> <generator object __next__ at 0x000000000A3E4F68>

Además, ¿cómo escribo unittest para generadores?

Aún necesita implementar un método de envío y lanzamiento para un Generator

from collections import Iterator, Generator import unittest class Test(unittest.TestCase): def test_Fib(self): f = Fib() self.assertEqual(next(f), 0) self.assertEqual(next(f), 1) self.assertEqual(next(f), 1) self.assertEqual(next(f), 2) #etc... def test_Fib_is_iterator(self): f = Fib() self.assertIsInstance(f, Iterator) def test_Fib_is_generator(self): f = Fib() self.assertIsInstance(f, Generator)

Y ahora:

>>> unittest.main(exit=False) ..F ====================================================================== FAIL: test_Fib_is_generator (__main__.Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "<stdin>", line 7, in test_Fib_is_generator AssertionError: <__main__.Fib object at 0x00000000031A6320> is not an instance of <class ''collections.abc.Generator''> ---------------------------------------------------------------------- Ran 3 tests in 0.001s FAILED (failures=1) <unittest.main.TestProgram object at 0x0000000002CAC780>

Entonces, implementemos un objeto generador y aprovechemos la clase base abstracta del Generator desde el módulo de colecciones (ver la fuente para su implementation ), lo que significa que solo necesitamos implementar el send y el throw , dándonos el close , __iter__ (devuelve self) y __next__ (igual que .send(None) ) gratis (consulte el modelo de datos de Python en coroutines ):

class Fib(Generator): def __init__(self): self.a, self.b = 0, 1 def send(self, ignored_arg): return_value = self.a self.a, self.b = self.b, self.a+self.b return return_value def throw(self, type=None, value=None, traceback=None): raise StopIteration

y utilizando las mismas pruebas anteriores:

>>> unittest.main(exit=False) ... ---------------------------------------------------------------------- Ran 3 tests in 0.002s OK <unittest.main.TestProgram object at 0x00000000031F7CC0>

Python 2

ABC Generator solo está en Python 3. Para hacer esto sin Generator , debemos escribir al menos close , __iter__ , y __next__ además de los métodos que definimos anteriormente.

class Fib(object): def __init__(self): self.a, self.b = 0, 1 def send(self, ignored_arg): return_value = self.a self.a, self.b = self.b, self.a+self.b return return_value def throw(self, type=None, value=None, traceback=None): raise StopIteration def __iter__(self): return self def next(self): return self.send(None) def close(self): """Raise GeneratorExit inside generator. """ try: self.throw(GeneratorExit) except (GeneratorExit, StopIteration): pass else: raise RuntimeError("generator ignored GeneratorExit")

Tenga en cuenta que copié directamente desde la biblioteca estándar de Python 3, sin modificaciones.

Veo muchos ejemplos de funciones de generador, pero quiero saber cómo escribir generadores para las clases. Digamos que quería escribir la serie de Fibonacci como una clase.

class Fib: def __init__(self): self.a, self.b = 0, 1 def __next__(self): yield self.a self.a, self.b = self.b, self.a+self.b f = Fib() for i in range(3): print(next(f))

Salida:

<generator object __next__ at 0x000000000A3E4F68> <generator object __next__ at 0x000000000A3E4F68> <generator object __next__ at 0x000000000A3E4F68>

¿Por qué el valor self.a no se imprime? Además, ¿cómo escribo unittest para generadores?


No use el yield en la función __next__ y aplique a next también para compatibilidad con python2.7 +

Código

class Fib: def __init__(self): self.a, self.b = 0, 1 def __next__(self): a = self.a self.a, self.b = self.b, self.a+self.b return a def next(self): return self.__next__()


Si le da a la clase un __iter__() implementado como generador , devolverá automáticamente un objeto generador cuando se le llame, de modo que los métodos __iter__ y __next__ del objeto serán los utilizados.

Esto es lo que quiero decir:

class Fib: def __init__(self): self.a, self.b = 0, 1 def __iter__(self): while True: value, self.a, self.b = self.a, self.b, self.a+self.b yield value f = Fib() for i, value in enumerate(f, 1): print(value) if i > 5: break

Salida:

0 1 1 2 3 5


__next__ debe devolver un artículo, no cederlo.

Puede escribir lo siguiente, en el que Fib.__iter__ devuelve un iterador adecuado:

class Fib: def __init__(self, n): self.n = n self.a, self.b = 0, 1 def __iter__(self): for i in range(self.n): yield self.a self.a, self.b = self.b, self.a+self.b f = Fib(10) for i in f: print i

o haga que cada instancia sea un iterador definiendo __next__ .

class Fib: def __init__(self): self.a, self.b = 0, 1 def __iter__(self): return self def __next__(self): x = self.a self.a, self.b = self.b, self.a + self.b return x f = Fib() for i in range(10): print next(f)