繁体   English   中英

为什么我的 except 块不能捕获 StopIteration 异常?

[英]Why can't my except block catch a StopIteration exception?

我想用这个程序做的是我希望它通过使用递归一遍又一遍地返回“是”和“否”。 请帮忙? 难道我做错了什么?

def yes_or_no():
for x in ("yes","no"):
    try:
        yield x
    except (StopIteration):
        return yes_or_no()

gen =  yes_or_no()
print(next(gen))
print(next(gen))
print(next(gen))

一旦它到达第三次打印,它就会说 StopIteration 即使我认为我在错误处理中发现了它?

yield调用中不会引发StopIteration异常,因此您不会使用该 try/except 设置来捕获它。

通过在您的 function 中有一个yield语句,您已经将它变成了一个生成器(您似乎理解)。 每次评估生成器时,它都会在上次完成的产量处重新进入,并继续进行直到下一次产量或 function 完成。 因此,在您的第三次next()中,执行将在yield处恢复,返回for循环,看到它已完成,然后继续执行,在这种情况下,这只是 function 的结束。 function 将返回,因此生成器将提高其 StopIteration。

我不建议您为此任务使用递归; 只需在 ("yes, "no") 循环周围使用外部无限循环(或者更好的是,来自 itertools 的东西)。

如果您真的想使用递归,那么您可能想尝试

def yes_or_no():
    for x in ("yes", "no"):
        yield x
    yield from yes_or_no()

我认为yield from需要 Python >= 3.3。

循环内的yield x永远不会raise StopIteration ,因此您无法捕获它。 Python for循环是使用StopIteration实现的,是的,但是在您处理它之前就捕获了那里引发的实例

使您想到的工作的方法是……让循环运行,然后产生元素的 rest。 递归:

def yes_or_no():
    for x in ("yes","no"):
        yield x
    yield from yes_or_no()

顺便说一句: yield from是您继续产生递归调用产生的元素所需要的。 生成器的return值并不能帮助您生成更多元素。 具有讽刺意味的是,事实上,它设置了导致生成器耗尽元素的异常:

>>> def example():
...     yield 1
...     return 2
...
>>> x = example()
>>> next(x)
1
>>> next(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration: 2

(当然,您的整个目标是yes_or_no()不会用完元素,所以。)

无论如何,为此使用递归是一个坏主意,因为您人为地限制了它可以工作的时间(直到您用完堆栈帧)。 只需迭代:

def yes_or_no():
    while True:
        yield 'yes'
        yield 'no'

或使用itertools

>>> import itertools
>>> x = itertools.cycle(('yes', 'no'))
>>> next(x)
'yes'
>>> next(x)
'no'
>>> next(x)
'yes'
>>> next(x)
'no'

那么如何在用户代码中充分利用StopIteration呢? 通过在迭代器实现中显式提升它(尽管最明显的方法已经为您完成了),或者在您使用next手动推进迭代器时捕获它。 例子:

class SingletonIterator:
    """Proxy iterator to allow 'iteration' over a fictitious sequence
    holding a single element, the `value`."""
    def __init__(self, value):
        self._value = value
        self._yielded = False

    def __iter__(self):
        return self

    def __next__(self):
        if self._yielded:
            raise StopIteration
        self._yielded = True
        return self._value


def first(predicate, sequence):
    """The first element `e` of `sequence` satisfying `predicate(e)`.
    Raises ValueError for an empty sequence."""
    try:
        return next(e for e in sequence if predicate(e))
    except StopIteration:
        raise ValueError('empty sequence')

这是一个简单的递归解决方案:

def yes_or_no():
    yield from ('yes', 'no')
    yield from yes_or_no()

编辑:

itertools模块具有 function cycle ,该循环接收一个迭代器并返回一个循环通过其输入的生成器。

import itertools
def yes_or_no():
    yield from itertools.cycle(("yes", "no"))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM