【问题标题】:Generators as iterator member functions生成器作为迭代器成员函数
【发布时间】:2020-03-10 08:52:24
【问题描述】:

所以我明白,有时不是在一个应该是可迭代的类中定义 iternext 方法,而是使用一个 iter包含 yield 语句的方法就足够了。其实为什么?只是避免样板代码?

但是,我不明白为什么下面的 sn-p 会产生三个迭代

class BoundedRepeater:
    def __init__(self, value, max_repeats):
        self.value = value
        self.max_repeats = max_repeats
        self.count = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.count >= self.max_repeats:
            raise StopIteration
        self.count += 1
        return self.value

如果这样调用

for item in BoundedRepeater("Hello", 3):
    print(item)

但如果我将方法更改为

类有界中继器: def init(self, value, max_repeats): 自我价值=价值 self.max_repeats = max_repeats self.count = 0

class BoundedRepeater:
    def __init__(self, value, max_repeats):
        self.value = value
        self.max_repeats = max_repeats
        self.count = 0

    def __iter__(self):
        if self.count >= self.max_repeats:
            raise StopIteration
        self.count += 1
        yield self.value

我只得到一个迭代而不是三个

【问题讨论】:

标签: python iterator


【解决方案1】:

第二个例子只给你一次迭代,因为yield 语句只被调用一次,因为__iter__ 只被调用一次。 __iter__ 方法不返回下一个值(因此不会对每个值调用一次),它返回一个包含所有值的迭代器。

你可以写

class SmallPrimes:
    def __iter__(self):
        yield 2
        yield 3
        yield 5
        yield 7

这表明对iter 的一次调用包含所有值。

在您的情况下,BoundedRepeater 类会将yield 放入for-loop:

class BoundedRepeater:
    def __init__(self, value, max_repeats):
        self.value = value
        self.max_repeats = max_repeats

    def __iter__(self):
        for _ in range(self.max_repeats):
            yield self.value

__iter__ 的执行在每次 yield 期间暂停,然后在需要下一个值时恢复。在暂停期间,所有上下文都被保留。请注意,无需跟踪计数变量(在您的第一个示例中为self.count)。

对于您的具体示例,即关于将值 V 重复 N 次,您不需要编写自己的类。你可以只写一个函数:

def bounded_repeat(V, N):
    for _ in range(N):
        yield V

for v in bounded_repeat(V, N):
    y = do_something(v)

但您不需要编写该函数,因为它已经存在:

import itertools
for v in itertools.repeat(V, N):
    y = do_something(v)

但是,除非 V 很重且 N 很大,否则请坚持使用一些好的旧 Python:

for v in [V] * N:
    y = do_something(v)

【讨论】:

    【解决方案2】:

    您可以使用生成器函数,但您应该在循环中生成结果,在您的情况下,以数字 max_repeat 为界

    我觉得你的__iter__方法应该写成这样:

        def __iter__(self):
            for i in range(self.max_repeats):
                yield self.value
            return
    

    我试过了,它会生成与 max_occurences 中指定的一样多的项目。

    br = BoundedRepeater('tagada', 5)
    for item in br:
        print(item)
    
    # ->tagada
    # ->tagada
    # ->tagada
    # ->tagada
    # ->tagada
    

    【讨论】:

    • 谢谢你们。我的问题更多地针对低级细节,我想知道为什么在第一个实现的下一个方法中不需要内部循环,而在第二个版本中我确实需要一个。我仍然难以理解这一点。
    • 所以如果我理解正确的话,iter 只有在使用 iterable 时才会被调用一次,例如在 for 循环中。但是第一个实现的下一个被调用,直到引发异常。全部隐藏在循环语法中。对于具有正确循环语法的 yield 版本,在 iter 被调用一次且仅一次后,控制权会暂时传递回调用站点。然而,在下一次循环迭代时,控制权被交还给 iter 方法,即使没有被显式调用,以产生下一个值。我说对了吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-02
    • 2019-02-27
    • 2018-05-16
    • 1970-01-01
    • 2017-10-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多