You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么Python中all函数搭配列表推导比生成器表达式更快?

Python中all()函数里生成器表达式与列表推导式的性能差异分析

我正在研究Python的一些性能细节,重点关注all、any、max等内置函数中迭代器和生成器的使用情况。

先创建一个整数列表:

arr: list[int] = list(range(10_000))

随后测试了以下两种写法的执行耗时:

  • 生成器表达式写法:
all(isinstance(x, int) for x in arr)

执行耗时:

1.04 ms ± 79.6 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
  • 列表推导式写法:
all([isinstance(x, int) for x in arr])

执行耗时:

915 µs ± 50.1 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)

我原本以为生成器表达式的写法会更快,因为它不需要额外创建数组占用内存,但实际结果却是列表推导式的写法更快。这是为什么?

为了探究原因,我对两个函数进行了反汇编,发现列表推导式对应的指令更少,这是不是关键原因?

反汇编代码如下:

In [11]: def f():
    ...:     return all(isinstance(x, int) for x in arr)
    ...: 

In [12]: def g():
    ...:     return all([isinstance(x, int) for x in arr])
    ...: 

In [13]: from dis import dis

In [14]: dis(f)

...

Disassembly of <code object <genexpr> at 0x7f459e2537e0, file "<ipython-input-11-973ce3497c83>", line 2>:
              0 GEN_START                0

  2           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 8 (to 22)
              6 STORE_FAST               1 (x)
              8 LOAD_GLOBAL              0 (isinstance)
             10 LOAD_FAST                1 (x)
             12 LOAD_GLOBAL              1 (int)
             14 CALL_FUNCTION            2
             16 YIELD_VALUE
             18 POP_TOP
             20 JUMP_ABSOLUTE            2 (to 4)
        >>   22 LOAD_CONST               0 (None)
             24 RETURN_VALUE

In [15]: dis(g)

...

Disassembly of <code object <listcomp> at 0x7f459ebf92c0, file "<ipython-input-12-d4d6130487f9>", line 2>:
  2           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 7 (to 20)
              6 STORE_FAST               1 (x)
              8 LOAD_GLOBAL              0 (isinstance)
             10 LOAD_FAST                1 (x)
             12 LOAD_GLOBAL              1 (int)
             14 CALL_FUNCTION            2
             16 LIST_APPEND              2
             18 JUMP_ABSOLUTE            2 (to 4)
        >>   20 RETURN_VALUE

原因分析

1. 生成器的状态切换开销

生成器表达式每次迭代都要执行YIELD_VALUE指令,这个操作涉及生成器的状态切换——暂停当前执行、保存上下文,下次迭代再恢复。这种频繁的上下文切换会带来额外性能损耗,而列表推导式是一次性构建完整列表,没有这类开销。

2. 列表迭代的底层优化

Python对列表迭代有深度的底层优化,all()遍历列表时直接访问内存中的连续元素,效率远高于生成器每次生成下一个元素的过程。列表推导式的LIST_APPEND是高效的内存操作,而生成器的YIELD_VALUE需要处理更多解释器层面的逻辑。

3. 指令差异的累积效应

从反汇编结果看,生成器表达式多了GEN_START、POP_TOP指令,且FOR_ITER步长为8,列表推导式的FOR_ITER步长为7。单条指令的差异看似微小,但在一万次迭代的循环中,这些差异会被放大,最终导致整体耗时的差距。

4. 内存与性能的场景权衡

生成器的内存优势只在无需遍历全部元素的场景下体现——比如列表中存在不符合条件的元素,all()会提前终止迭代,这时生成器不需要构建完整列表,性能会优于列表推导式。但在这个测试场景中,所有元素都是int,all()必须遍历全部元素,内存节省的优势抵不过生成器的状态切换开销。


内容的提问来源于stack exchange,提问作者Alex Wignorbo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 02:05:16