我看到了许多生成器函数的示例,但是我想知道如何为类编写生成器。可以说,我想写斐波那契数列作为一个类。
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))
输出:
<generator object __next__ at 0x000000000A3E4F68> <generator object __next__ at 0x000000000A3E4F68> <generator object __next__ at 0x000000000A3E4F68>
为什么值self.a没有打印出来?另外,我该如何unittest为发电机编写代码?
self.a
unittest
如何编写一个生成器类?
您快要写完 Iterator 类了(我在答案末尾显示了Generator),但是__next__每次使用调用对象时都会被调用next,并返回一个generator对象。相反,要使您的代码以最少的更改和最少的代码行工作,请使用__iter__,这使您的类实例化为 可迭代的 (从技术上讲,它不是 生成器 ):
__next__
next
__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
当我们向传递一个iterable时iter(),它为我们提供了一个 迭代器 :
iter()
>>> f = iter(Fib()) >>> for i in range(3): ... print(next(f)) ... 0 1 1
为了使类本身成为 迭代器 ,它确实需要一个__next__:
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
现在,由于iter只返回实例本身,因此我们无需调用它:
iter
>>> f = Fib() >>> for i in range(3): ... print(next(f)) ... 0 1 1
为什么值self.a没有得到打印?
这是带有我的注释的原始代码:
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))
因此,每次调用next(f)时,都会生成__next__返回的生成器对象:
next(f)
另外,如何为发电机编写unittest?
您仍然需要实现一个send和throw方法 Generator
Generator
from collections.abc 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)
现在:
>>> 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>
因此,让我们实现一个生成器对象,并利用Generatorcollections模块中的抽象基类(请参见其实现的源代码),这意味着我们只需要免费实现send并throw-免费给我们close,__iter__(返回自身)和__next__(与.send(None)) (请参阅协程的Python数据模型):
send
throw
close
.send(None)
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
并使用上述相同的测试:
>>> unittest.main(exit=False) ... ---------------------------------------------------------------------- Ran 3 tests in 0.002s OK <unittest.main.TestProgram object at 0x00000000031F7CC0>
美国广播公司Generator仅在Python 3.要做到这一点没有Generator,我们需要至少写close,__iter__以及__next__除了我们上面定义的方法。
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")
请注意,我close直接从Python 3标准库复制而未进行任何修改。