简体   繁体   English

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

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

What i want to do with this program is that I want it to return "yes" and "no" over and over again by using recursion.我想用这个程序做的是我希望它通过使用递归一遍又一遍地返回“是”和“否”。 Please help?请帮忙? Am I doing something wrong?难道我做错了什么?

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

As soon as it reaches the 3rd print, it says StopIteration even though I thought I caught it in Error Handling?一旦它到达第三次打印,它就会说 StopIteration 即使我认为我在错误处理中发现了它?

The StopIteration exception is not raised in the yield call, so you won't be catching it with that try/except setup.yield调用中不会引发StopIteration异常,因此您不会使用该 try/except 设置来捕获它。

By having a yield statement in your function, you have turned it into a generator (which you seem to understand).通过在您的 function 中有一个yield语句,您已经将它变成了一个生成器(您似乎理解)。 Each time the generator is evaluated, it will re-enter at the yield where it finished last time, and carry on until the next yield or the function completes.每次评估生成器时,它都会在上次完成的产量处重新进入,并继续进行直到下一次产量或 function 完成。 So on your third next() , execution will resume at the yield , get back to the for loop, see that it is finished, and carry on after it, which is simply the end of the function in this case.因此,在您的第三次next()中,执行将在yield处恢复,返回for循环,看到它已完成,然后继续执行,在这种情况下,这只是 function 的结束。 The function will return, and thus the generator will raise its StopIteration. function 将返回,因此生成器将提高其 StopIteration。

I don't recommend you use recursion for this task;我不建议您为此任务使用递归; just use an outer infinite loop around your ("yes, "no") loop (or better yet, something from itertools).只需在 ("yes, "no") 循环周围使用外部无限循环(或者更好的是,来自 itertools 的东西)。

If you really want to use recursion, then you might want to try如果您真的想使用递归,那么您可能想尝试

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

The yield from bit requires Python >= 3.3 I think.我认为yield from需要 Python >= 3.3。

yield x inside the loop will never raise StopIteration , so you cannot catch it.循环内的yield x永远不会raise StopIteration ,因此您无法捕获它。 The Python for loop is implemented using StopIteration , true, but the instance raised there is caught before you can deal with it . Python for循环是使用StopIteration实现的,是的,但是在您处理它之前就捕获了那里引发的实例

The way to make what you have in mind work is to just... let the loop run its course, then yield the rest of the elements.使您想到的工作的方法是……让循环运行,然后产生元素的 rest。 Recursively:递归:

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

As an aside: yield from is what you need in order to continue yielding elements produced by the recursive calls.顺便说一句: yield from是您继续产生递归调用产生的元素所需要的。 The return value from a generator doesn't help you yield more elements.生成器的return值并不能帮助您生成更多元素。 Ironically, in fact, it sets up the exception that results for running the generator out of elements:具有讽刺意味的是,事实上,它设置了导致生成器耗尽元素的异常:

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

(Of course, your entire goal is for yes_or_no() not to run out of elements, so.) (当然,您的整个目标是yes_or_no()不会用完元素,所以。)

At any rate, using recursion for this is a bad idea, since you artificially limit how long it can work for (until you run out of stack frames).无论如何,为此使用递归是一个坏主意,因为您人为地限制了它可以工作的时间(直到您用完堆栈帧)。 Just iterate:只需迭代:

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

Or use itertools :或使用itertools

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

So how do you make good use of StopIteration in user code?那么如何在用户代码中充分利用StopIteration呢? Either by explicitly raising it in an iterator implementation (although the most obvious ways to do this are again already done for you), or by catching it when you manually advance an iterator using next .通过在迭代器实现中显式提升它(尽管最明显的方法已经为您完成了),或者在您使用next手动推进迭代器时捕获它。 Examples:例子:

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

Here is a simple recursive solution:这是一个简单的递归解决方案:

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

Edit:编辑:

The itertools module has the function cycle that takes in an iterator and returns a generator that cycles through its input. 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