元组推导式生成生成器,列表推导式立即执行的原因探究
元组推导式与列表推导式的执行差异原因
我在写探索性数据分析的临时脚本时,为了保持Spyder变量管理器整洁,想用推导式替代普通循环来省去删除迭代变量的del语句。下面是三种清除Matplotlib图形的写法:
# Generate the figures import matplotlib as mpl import matplotlib.pyplot as plt plt.close('all') plt.scatter([1,2,3],[4,5,6]) plt.figure() plt.scatter([1,2,3],[6,5,4]) # 3 ways to clear the figures if True: # 误以为是元组推导式,实际是生成器表达式 ( plt.figure(iFig).clf() for iFig in plt.get_fignums() ) elif True: # 列表推导式 [ plt.figure(iFig).clf() for iFig in plt.get_fignums() ] else: # 普通循环 for iFig in plt.get_fignums(): plt.figure(iFig).clf() del iFig # 想省去的额外代码
实际测试发现:圆括号的推导式返回生成器对象,必须迭代它才会执行clf();而列表推导式会立即执行所有clf()操作。想知道为什么会有这种差异,有没有Python规则可以提前预见?
核心解释
首先要纠正一个认知:你写的(expr for var in iter)不是元组推导式,它是生成器表达式——Python里没有原生的元组推导式语法,若要生成元组,必须显式调用tuple()包裹生成器表达式,比如tuple(expr for var in iter)。
两者的执行差异完全由Python的求值规则决定:
- 生成器表达式(圆括号推导式)是惰性求值:它返回的是一个生成器对象,不会立刻遍历可迭代对象或执行表达式里的代码,只有当你迭代这个生成器(比如用
for循环、next()函数、转成列表/元组)时,才会逐个计算表达式的值、执行副作用代码(比如clf())。这种设计是为了节省内存,适合处理大数据集。 - 列表推导式是立即求值:Python会立刻遍历整个可迭代对象,执行每一次的表达式计算,把结果收集成列表返回,所以里面的副作用操作会一次性全部执行。
针对你的场景的建议
- 如果想用类似"元组推导式"的写法且执行所有
clf(),可以写tuple(plt.figure(iFig).clf() for iFig in plt.get_fignums()),但这会生成一个全是None的元组(因为clf()返回None),不过确实不会留下迭代变量iFig。 - 列表推导式其实已经能满足你的需求:只要不把推导式的结果赋值给变量,这个临时的
None列表会被Python自动垃圾回收,Spyder的变量管理器里也不会留下多余变量,同时省去了del iFig的代码。
内容的提问来源于stack exchange,提问作者user2153235
相关产品推荐
相关产品推荐

