您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

如何编写一个生成器类?

如何编写一个生成器类?

您快要写完 Iterator 类了(我在答案末尾显示了Generator),但是__next__每次使用调用对象时都会被调用next,并返回一个generator对象。相反,要使您的代码以最少的更改和最少的代码行工作,请使用__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(),它为我们提供了一个 迭代器

>>> 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只返回实例本身,因此我们无需调用它:

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

这是带有我的注释的原始代码

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

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

您仍然需要实现一个send和throw方法 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模块中的抽象基类(请参见其实现的源代码),这意味着我们只需要免费实现sendthrow-免费给我们close__iter__(返回自身)和__next__(与.send(None)) (请参阅协程Python数据模型):

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标准库复制而未进行任何修改

其他 2022/1/1 18:38:10 有388人围观

撰写回答


你尚未登录,登录后可以

和开发者交流问题的细节

关注并接收问题和回答的更新提醒

参与内容的编辑和改进,让解决方法与时俱进

请先登录

推荐问题


联系我
置顶