为什么Python生成器中的异常没有被捕获?

Vic*_*Yan 21 python generator python-3.x

我有以下实验代码,其功能类似于zip内置.它试图做的应该是简单明了的,试图一次一个地返回压缩的元组,直到IndexError我们停止发电机时发生.

def my_zip(*args):
    i = 0
    while True:
        try:
            yield (arg[i] for arg in args)
        except IndexError:
            raise StopIteration
        i += 1
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试执行以下代码时,IndexError没有被捕获但是被生成器抛出:

gen = my_zip([1,2], ['a','b'])
print(list(next(gen)))
print(list(next(gen)))
print(list(next(gen)))


IndexError                                Traceback (most recent call last)
I:\Software\WinPython-32bit-3.4.2.4\python-3.4.2\my\temp2.py in <module>()
     12 print(list(next(gen)))
     13 print(list(next(gen)))
---> 14 print(list(next(gen)))

I:\Software\WinPython-32bit-3.4.2.4\python-3.4.2\my\temp2.py in <genexpr>(.0)
      3     while True:
      4         try:
----> 5             yield (arg[i] for arg in args)
      6         except IndexError:
      7             raise StopIteration
IndexError: list index out of range
Run Code Online (Sandbox Code Playgroud)

为什么会这样?

编辑:

感谢@thefourtheye为上面发生的事情提供了一个很好的解释.我执行时会出现另一个问题:

list(my_zip([1,2], ['a','b']))
Run Code Online (Sandbox Code Playgroud)

这条线永远不会返回,似乎挂机.现在发生了什么?

the*_*eye 13

yield收益率发电机对象每次当创建发电机有一点问题都没有.这就是为什么try...exceptmy_zip没有一无所获.第三次执行时,

list(arg[2] for arg in args)
Run Code Online (Sandbox Code Playgroud)

这就是它如何被简化为(我们的理解简化)现在,仔细观察,list迭代发电机,而不是实际的my_zip发电机.现在,list调用next生成器对象并arg[2]进行求值,只发现它2不是arg([1, 2]在这种情况下)的有效索引,因此IndexError被引发,并且list无法处理它(它无论如何都没有理由处理它)所以它失败.


根据编辑,

list(my_zip([1,2], ['a','b']))
Run Code Online (Sandbox Code Playgroud)

会像这样评估.首先,my_zip将被调用,这将为您提供一个生成器对象.然后迭代它list.它调用next它,它获得另一个生成器对象list(arg[0] for arg in args).由于没有异常或return遇到过,它将调用next,以获取另一个生成器对象list(arg[1] for arg in args)并继续迭代.请记住,生成的生成器从不迭代,所以我们永远不会得到IndexError.这就是代码无限运行的原因.

你可以这样确认一下,

from itertools import islice
from pprint import pprint
pprint(list(islice(my_zip([1, 2], ["a", 'b']), 10)))
Run Code Online (Sandbox Code Playgroud)

你会得到的

[<generator object <genexpr> at 0x7f4d0a709678>,
 <generator object <genexpr> at 0x7f4d0a7096c0>,
 <generator object <genexpr> at 0x7f4d0a7099d8>,
 <generator object <genexpr> at 0x7f4d0a709990>,
 <generator object <genexpr> at 0x7f4d0a7095a0>,
 <generator object <genexpr> at 0x7f4d0a709510>,
 <generator object <genexpr> at 0x7f4d0a7095e8>,
 <generator object <genexpr> at 0x7f4d0a71c708>,
 <generator object <genexpr> at 0x7f4d0a71c750>,
 <generator object <genexpr> at 0x7f4d0a71c798>]
Run Code Online (Sandbox Code Playgroud)

因此代码尝试构建无限的生成器对象列表.