如何编写一个生成器类?

编程入门 行业动态 更新时间:2024-10-26 20:30:53
本文介绍了如何编写一个生成器类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我看到了许多生成器函数的示例,但是我想知道如何为类编写生成器.可以说,我想写斐波那契数列作为一个类.

I see lot of examples of generator functions, but I want to know how to write generators for classes. Lets say, I wanted to write Fibonacci series as a class.

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?

Why is the value self.a not getting printed? Also, how do I write unittest for generators?

推荐答案

如何编写生成器类?

您快到了,正在编写一个 Iterator 类(我在答案的末尾显示了一个Generator),但是每次您使用next调用该对象时,都会调用__next__,返回一个生成器对象.相反,要使您的代码以最少的更改和最少的代码行工作,请使用__iter__,它使您的类实例化一个 iterable (从技术上讲,它不是 generator ):

You're almost there, writing an Iterator class (I show a Generator at the end of the answer), but __next__ gets called every time you call the object with next, returning a generator object. Instead, to make your code work with the least changes, and the fewest lines of code, use __iter__, which makes your class instantiate an iterable (which isn't technically a generator):

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

当我们将迭代器传递给iter()时,它为我们提供了 iterator :

When we pass an iterable to iter(), it gives us an iterator:

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

要使类本身成为 iterator ,它确实需要__next__:

To make the class itself an iterator, it does require a __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仅返回实例本身,因此我们无需调用它:

And now, since iter just returns the instance itself, we don't need to call it:

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

为什么self.a值无法打印?

这是您的原始代码,上面有我的评论:

Here's your original code with my comments:

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__返回的生成器对象:

So every time you called next(f) you got the generator object that __next__ returns:

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

此外,我该如何为生成器编写单元测试?

您仍然需要为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>

因此,让我们实现一个生成器对象,并利用collections模块中的Generator抽象基类(请参见源代码中的实现),这意味着我们只需要实现send和throw-给我们close,__iter__(返回自身),和__next__(与.send(None)相同)免费(请参见 Python协程数据模型):

So let's implement a generator object, and leverage the Generator abstract base class from the collections module (see the source for its implementation), which means we only need to implement send and throw - giving us close, __iter__ (returns self), and __next__ (same as .send(None)) for free (see the Python data model on 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

并使用与上面相同的测试:

and using the same tests above:

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

Python 2

ABC Generator仅在Python 3中.要在没有Generator的情况下执行此操作,除了上面定义的方法外,我们还至少需要编写close,__iter__和__next__. /p>

Python 2

The ABC Generator is only in Python 3. To do this without Generator, we need to write at least close, __iter__, and __next__ in addition to the methods we defined above.

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")

请注意,我直接从Python 3 标准库复制了close ,无需修改.

Note that I copied close directly from the Python 3 standard library, without modification.

更多推荐

如何编写一个生成器类?

本文发布于:2023-11-10 09:22:16,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1574987.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:生成器

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!