Генератор Python, исключение без глотания в сопрограмме - PullRequest
12 голосов
/ 22 октября 2010

Недавно я столкнулся с некоторым удивительным поведением в генераторах 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*,поскольку это было то, что я хотел, но также удивился, что Исключение все еще распространялось на верхний уровень.Я ожидал использовать ключевое слово * (1008 *) (прокомментированное в этом примере), чтобы получить наблюдаемое поведение.

Кто-нибудь может объяснить, почему эта функциональность работает так, как она работает, и почему except в генераторене глотает исключение?

Это единственный экземпляр в Python, где except не глотает исключение?

Ответы [ 2 ]

14 голосов
/ 22 октября 2010

Ваш код не делает то, что вы думаете, что он делает.Вы не можете поднять исключения в сопрограмме, как это.Вместо этого вы ловите исключение 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

Поскольку это все еще вызывает откаты, вот как вы создаете исключение в генераторе:

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.

6 голосов
/ 22 октября 2010

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, но в этом случае мы заставили генератор вызвать исключение, чтобы цикл не заметил их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...