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

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

然而,当我试图执行以下代码时,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

为什么会这样?

编辑:

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

list(my_zip([1,2], ['a','b']))

这条线再也回不来了,好像把机器挂起来了.现在发生了什么?

推荐答案

yield每次都会生成一个生成器对象,创建生成器时根本没有问题.这就是为什么my_zip人中有try...except人什么也抓不到.第三次执行时,

list(arg[2] for arg in args)

这就是为什么它被简化为(我们的理解过于简化),现在,仔细观察,100 is iterating the generator, not the actual 101 generator.现在,对generator对象上的list个调用nextarg[2]进行求值,结果发现2不是arg的有效索引(在本例中为[1, 2]),因此引发IndexError,而list未能处理它(无论如何,它没有理由处理它),因此失败.


根据编辑,

list(my_zip([1,2], ['a','b']))

将像这样进行判断.首先,将调用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)))

你会得到

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

因此,代码试图构建一个无限的生成器对象列表.

Python-3.x相关问答推荐

循环遍历数据框以提取特定值

如何从枚举中获取某个值?

按长度和字母数字对Pandas 数据帧列进行排序

在一行中读写一个csv文件

将数据框中的值与另一个数据框中的多列进行比较,以获取条目以有效方式匹配的列表列表

如何在python 3.10中将列表项(字符串类型)转换为模块函数

正则表达式来识别用 Python 写成单词的数字?

协议不支持地址系列在将 Scapy L3socket 与 WSL 一起使用时

Tkinter IntVar 返回 PY_VAR0 而不是值

日志(log)模块不适用于 Python3

为什么包含类的名称不被识别为返回值函数注释?

Pylint 给我最后的新行丢失

Anaconda 中的依赖项和包冲突?

为 True 相交两个布尔数组

将 args、kwargs 传递给 run_in_executor

python 3的蓝牙库

在 ubuntu 20.04 中安装 libpq-dev 时出现问题

Python:&= 运算符

Windows 下 Python 3.x 的 OpenCV

如何修复:cx_Oracle.DatabaseError:DPI-1047:找不到 64 位 Oracle 客户端库 - Python