土薯工具 Toolshu.com 登录 用户注册

Python 列表推导式比 for 循环快多少,什么时候根本没差?

作者:bhnw 于 2026-04-25 10:26 发布 5次浏览 收藏 (0)

列表推导式"更快"——但快在哪里,什么时候快,什么时候根本没差?

网上随便搜,都能找到"列表推导式比 for 循环快"这个说法。大部分文章给你跑个 timeit,展示列表推导式快了 2 倍,然后说用列表推导式吧,完事。

但实际写代码的时候经常会发现:换成列表推导式,压根看不出快了多少。这不是错觉,是有原因的。


先说结论,再讲原因

  • 简单操作(乘以2、加1、类型转换这类):列表推导式比 for 循环快 20%-50%,有时接近 2 倍
  • 复杂操作(函数调用、正则匹配、IO、数据库查询这类):基本没有差别,快不了几个毫秒
  • 数据量很大、内存吃紧:列表推导式反而是问题,生成器表达式才是正确选择

为什么简单操作时列表推导式更快?

dis 模块看一下字节码就清楚了:

import dis

# for 循环版本
def for_loop():
    result = []
    for i in range(10):
        result.append(i * 2)
    return result

# 列表推导式版本
def list_comp():
    return [i * 2 for i in range(10)]

dis.dis(for_loop)
dis.dis(list_comp)

for 循环的字节码里,每次迭代都要做:

  1. LOAD_FAST:找到 result 变量
  2. LOAD_ATTR:查找 .append 属性——这步最贵
  3. CALL_FUNCTION:调用 append

列表推导式用的是专门的字节码指令 LIST_APPEND,绕过了属性查找,直接在 C 层面操作列表。循环10万次,省了10万次属性查找,差距就出来了。

用 timeit 验证一下:

import timeit

# 简单操作:每个数乘以2
setup = "data = list(range(1_000_000))"

t_for = timeit.timeit(
    "[result.append(x * 2) for _ in [None] if False] or [x * 2 for x in data]",
    setup=setup, number=10
)

# 更直观的对比
def for_loop(data):
    result = []
    for x in data:
        result.append(x * 2)
    return result

def list_comp(data):
    return [x * 2 for x in data]

data = list(range(1_000_000))

t1 = timeit.timeit(lambda: for_loop(data), number=50)
t2 = timeit.timeit(lambda: list_comp(data), number=50)

print(f"for loop:  {t1:.3f}s")
print(f"list comp: {t2:.3f}s")
print(f"快了: {t1/t2:.2f}x")
# 典型输出:
# for loop:  2.341s
# list comp: 1.487s
# 快了: 1.57x

为什么复杂操作时没有差别?

道理很简单:当操作本身耗时远超循环开销,那点属性查找的时间就是九牛一毛。

import math
import timeit

def heavy_for(data):
    result = []
    for x in data:
        result.append(math.sqrt(x) ** 2.5 + math.log(x + 1))
    return result

def heavy_comp(data):
    return [math.sqrt(x) ** 2.5 + math.log(x + 1) for x in data]

data = list(range(1, 100_001))

t1 = timeit.timeit(lambda: heavy_for(data), number=20)
t2 = timeit.timeit(lambda: heavy_comp(data), number=20)

print(f"for loop:  {t1:.3f}s")
print(f"list comp: {t2:.3f}s")
# 差距往往在 5% 以内,基本可以忽略

如果循环体里有网络请求、数据库查询、文件读写,那 for 循环和列表推导式的性能差距就更加可以忽略——网络延迟动辄几十毫秒,append 的开销根本不值一提。


内存:列表推导式的隐患

列表推导式是"饿汉",执行的瞬间就把所有结果放进内存。

# 这行代码会立刻占用几百 MB 内存
big_list = [x * 2 for x in range(10_000_000)]

# 生成器表达式是"懒汉",几乎不占内存
big_gen = (x * 2 for x in range(10_000_000))

import sys
print(sys.getsizeof(big_list))  # ~85 MB
print(sys.getsizeof(big_gen))   # 200 Bytes 左右

如果你只是要遍历结果一次(比如求和、找最大值、传给另一个函数),根本不需要列表推导式,直接用生成器表达式:

# 不需要建完整列表
total = sum(x * 2 for x in range(10_000_000))      # 内存友好
maximum = max(x ** 2 for x in range(10_000_000))   # 内存友好

# 而不是
total = sum([x * 2 for x in range(10_000_000)])    # 先建完整列表再求和,浪费

什么时候不该用列表推导式

逻辑复杂的时候。 列表推导式超过两层条件或者嵌套两层以上,可读性会急剧下降,这时候老老实实写 for 循环反而更好:

# 这已经很难看懂了
result = [x for sublist in matrix for x in sublist if x > 0 if x % 2 == 0]

# 换成 for 循环,起码能加注释
result = []
for sublist in matrix:
    for x in sublist:
        if x > 0 and x % 2 == 0:
            result.append(x)

需要中途 break 的时候。 列表推导式没法 break,找到第一个满足条件的元素就停下来,只能用 for 循环或者 next()

# 找第一个大于100的数,列表推导式做不到高效
data = range(1_000_000)

# 错误的做法:把100万个元素全遍历了
first = [x for x in data if x > 100][0]

# 正确的做法
first = next(x for x in data if x > 100)
# 或者
for x in data:
    if x > 100:
        first = x
        break

需要处理异常的时候。 列表推导式里没办法 try-except:

# 有些字符串可能转换失败,只能用 for 循环
def safe_int(s):
    try:
        return int(s)
    except ValueError:
        return None

data = ["1", "abc", "3", "xyz"]
result = [safe_int(x) for x in data]  # 这样可以,把异常处理封进函数
# 但不能在推导式内部直接写 try-except

一张表总结

场景 推荐写法
简单变换,结果要放进列表 列表推导式
只需遍历一次结果(求和/最大值等) 生成器表达式
数据量超大,内存有限 生成器表达式
操作复杂(函数调用、IO) 随意,可读性优先
逻辑复杂,需要注释 for 循环
需要 break 或 continue for 循环
需要 try-except for 循环,或把异常处理封进函数

想直接跑这些代码验证结果,粘到 Python 在线运行工具 里就行,Python 3.12 环境,timeit 和 dis 模块都支持。

发现周边 发现周边
评论区

加载中...