加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

如何编写生成器类?

发布时间:2020-12-20 10:32:03 所属栏目:Python 来源:网络整理
导读:我看到很多生成器函数的例子,但我想知道如何为类编写生成器.可以说,我想把斐波纳契系列写成一个类. 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.bf = Fib()for i in range(3)
我看到很多生成器函数的例子,但我想知道如何为类编写生成器.可以说,我想把斐波纳契系列写成一个类.

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没有打印?另外,我如何为发电机编写单元测试?

解决方法

How to write a generator class?

你几乎就在那里,编写一个Iterator类(我在答案的最后显示一个Generator),但每次用next调用对象时都会调用__next__,返回一个生成器对象.相反,使用__iter__:

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

要使类本身成为迭代器:

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

现在:

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

Why is the value self.a not getting printed?

这是您的原始代码和我的评论:

class Fib:
    def __init__(self):
        self.a,1

    def __next__(self):
        yield self.a          # yield makes .__next__() return a generator!
        self.a,self.a+self.b

f = Fib()

for i in range(3):
    print(next(f))

因此,每次调用next(f)时,都会得到__next__返回的生成器对象:

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

Also,how do I write unittest for generators?

您仍然需要为Generator实现send和throw方法

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),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抽象基类(参见其implementation的源代码),这意味着我们只需要实现send和throw – 给我们close,__ iter __(返回self)和__next__ (与.send(无)相同)免费(见Python data model on coroutines):

class Fib(Generator):
    def __init__(self):
        self.a,1        
    def send(self,ignored_arg):
        return_value = self.a
        self.a,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>

Python 2

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

class Fib(object):
    def __init__(self):
        self.a,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 standard library中复制了一下,没有修改.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读