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