Генератор Python, исключение без проглатывания в 'coroutine'
Недавно я встретил какое-то удивительное поведение в генераторах Python:
class YieldOne:
def __iter__(self):
try:
yield 1
except:
print '*Excepted Successfully*'
# raise
for i in YieldOne():
raise Exception('test exception')
Что дает результат:
*Excepted Successfully*
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
Exception: test exception
Я был (приятно) удивлен, что *Excepted Successfully*
получил распечатку, так как это было то, что я хотел, но также удивился тому, что Exception до сих пор распространялся до верхнего уровня. Я ожидал использовать ключевое слово raise
(прокомментированное в этом примере), чтобы получить наблюдаемое поведение.
Может кто-нибудь объяснить, почему эта функция работает так, как она есть, и почему except
в генераторе не проглатывает исключение?
Является ли это единственным экземпляром в Python, где except
не проглатывает исключение?
Ответы
Ответ 1
Ваш код не работает так, как вы думаете. Вы не можете создавать Исключения в сопрограмме, подобной этому. Вместо этого вы выбираете исключение GeneratorExit
. Посмотрите, что произойдет, если вы используете другое исключение:
class YieldOne:
def __iter__(self):
try:
yield 1
except RuntimeError:
print "you won't see this"
except GeneratorExit:
print 'this is what you saw before'
# raise
for i in YieldOne():
raise RuntimeError
По мере того как это все еще получает upvotes, вот как вы создаете исключение в генераторе:
class YieldOne:
def __iter__(self):
try:
yield 1
except Exception as e:
print "Got a", repr(e)
yield 2
# raise
gen = iter(YieldOne())
for row in gen:
print row # we are at `yield 1`
print gen.throw(Exception) # throw there and go to `yield 2`
Смотрите документы для generator.throw
.
Ответ 2
EDIT: что сказал THC4k.
Если вы действительно хотите создать произвольное исключение внутри генератора, используйте метод throw
:
>>> def Gen():
... try:
... yield 1
... except Exception:
... print "Excepted."
...
>>> foo = Gen()
>>> next(foo)
1
>>> foo.throw(Exception())
Excepted.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Вы заметите, что на верхнем уровне вы получите StopIteration
. Они поднимаются генераторами, которые исчерпали элементы; они обычно проглатываются контуром for
, но в этом случае генератор генерирует исключение, поэтому цикл их не замечает.