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

为何sum结合列表推导式比生成器表达式更快?附性能测试数据

为什么列表推导式在sum中比生成器表达式更快?

这是个非常有意思的观察!我来帮你拆解一下背后的原因——核心在于两者在Python内部的执行机制差异,尤其是字节码层面的操作、迭代开销以及缓存效率的区别。

1. 字节码执行流程的本质差异

我们可以用Python的dis模块直接查看两种写法的字节码,就能直观看到区别:

对于列表推导式 sum([ch in A for ch in B]),字节码会先执行BUILD_LIST指令,一次性把所有ch in A的布尔结果打包成一个列表,再把这个列表传给sum函数去遍历求和。

而生成器表达式 sum(ch in A for ch in B),字节码会先执行GEN_EXPR指令创建一个生成器对象,然后sum函数每次都要通过NEXT指令去生成器里“取”下一个元素。这每一次NEXT操作都相当于一次函数调用,带来了额外的开销。

举个简化的字节码对比(截取关键部分):

  • 列表推导式关键字节码:
    BUILD_LIST              0
    LOAD_FAST                0 (B)
    GET_ITER
    FOR_ITER                12 (to 18)
    ...(计算ch in A)
    LIST_APPEND              2
    JUMP_ABSOLUTE            6
    LOAD_NAME                2 (sum)
    CALL_FUNCTION            1
    
  • 生成器表达式关键字节码:
    LOAD_NAME                2 (sum)
    LOAD_CONST               0 (<code object <genexpr> at ...>)
    MAKE_FUNCTION            0
    LOAD_FAST                0 (B)
    GET_ITER
    CALL_FUNCTION            1
    CALL_FUNCTION            1
    

2. 迭代器的函数调用开销

生成器本质是一个迭代器,每次获取下一个元素都要调用它的__next__()方法。这个方法调用本身就有额外的开销:比如创建栈帧、处理StopIteration异常、销毁栈帧等等。

而列表是一个直接的序列对象,sum遍历列表时,直接通过索引访问内存里的元素,不需要每次都触发函数调用,自然省去了这部分开销。

3. CPU缓存的局部性优势

列表推导式生成的列表是一块连续的内存空间,CPU在访问时可以利用缓存局部性原理——一次性把相邻的多个元素加载到CPU缓存中,后续访问时直接从缓存读取,速度远快于从主存读取。

而生成器是逐个计算并返回元素,没有连续内存的特性,每次获取元素都可能需要从主存读取,缓存命中率低,这也会拖慢整体速度。

从你的测试数据看趋势

你提供的测试结果正好印证了这些差异:当B的字符数从10增加到10万时,两者的时间差从17µs(112-94.6)扩大到68ms(908-840)。这是因为迭代器的开销是累积的,数据量越大,额外的函数调用和缓存低效带来的影响就越明显。

小补充:时间与空间的权衡

不过要注意,这种速度优势是有代价的:列表推导式会一次性把所有结果存入内存,如果B的规模特别大(比如百万级以上),列表会占用大量内存,甚至导致内存不足。这时候生成器的内存优势就会凸显——它不会一次性生成所有元素,而是按需计算,内存占用极低。所以实际使用中要根据场景选择:追求速度且数据量不大时用列表推导式,追求内存效率时用生成器表达式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 15:47:32