page contents

学会这个Python技巧,代码效率翻倍,新手也能轻松掌握

写Python代码的人常遇到一种情况。程序跑起来慢。查来查去没发现大问题。最后定位到一行不起眼的循环。改一个写法速度就上去了。这种技巧不涉及复杂的算法。今天讲的就是一个基础操作。

attachments-2026-09-pEcqTRNr6aab43d554937.png写Python代码的人常遇到一种情况。程序跑起来慢。查来查去没发现大问题。最后定位到一行不起眼的循环。改一个写法速度就上去了。这种技巧不涉及复杂的算法。今天讲的就是一个基础操作。             

先看一段代码。假设有一个列表存放用户年龄。需要找出所有成年人的年龄。很多人会这样写:         

普通循环写法

ages = [12, 34, 18, 22, 15, 40] adult_ages = [] for age in ages:    

if age >= 18:        

adult_ages.append(age)

这段代码没问题。逻辑清楚。可数据量变大就出事了。列表有一千万个年龄。循环跑一千万次。每次判断再加一次append。             

时间花在列表的动态扩容上。             

Python里列表不是固定数组。append操作有时会重新分配内存。数据越多这种开销越明显。         

换一种写法。用列表推导式:         

列表推导式adult_ages = [age for age in ages if age >= 18]

列表推导式不是语法糖。它在底层做了优化。Python解释器为推导式创建了一个专用字节码路径。循环体内不调用方法。没有append函数调用的开销。内存分配一次到位。因为解释器提前知道要生成多少元素。测试一下。对一千万个整数做筛选。         

⚡性能对比

循环 + append

0.8s

列表推导式

0.3s

速度提升 2 倍以上

有人问列表推导式还能更快吗。可以。当过滤条件简单时用生成器表达式配合内置函数。比如把条件换成函数调用:         

adult_ages = list(filter(lambda age: age >= 18, ages))

这个写法比列表推导式慢。因为lambda有函数调用开销。filter在C层面实现。lambda回Python层执行。整体反而拖累。所以列表推导式是首选。

再看另一种常见场景。需要把两个列表合并成字典。比如键列表和值列表。新手会写循环:         

keys = ['a', 'b', 'c'] values = [1, 2, 3] d = {} for i in range(len(keys)):     d[keys[i]] = values[i]

这个循环有索引访问。还有range生成。换成字典推导式:         

d = {k: v for k, v in zip(keys, values)}

zip在C层配对。推导式直接构造字典。没有中间列表。没有索引计算。速度提升三倍。字典推导式还有一个好处。它只遍历一次输入。而循环写法中如果数据量大会产生多次哈希计算。         

有人会问。推导式里能不能加多个条件。可以。比如筛选年龄在18到60之间的:         

adult_ages = [age for age in ages if age >= 18 if age <= 60]

两个if连写。效果等同于and。速度快过用and连接。因为每个if独立判断。解释器可以短路。用and时解释器也要做同样的事。可写成一行的两个if更直观。         

推导式还能嵌套。比如展平二维列表:         

matrix = [[1, 2], [3, 4], [5, 6]] flat = [num for row in matrix for num in row]

这个写法保持从左到右的循环顺序。外层循环先动。内层循环后动。等价于两个for循环。可嵌套推导式让代码短很多。速度也快。因为不需要在每次内层循环时重新绑定外层变量。         

内存提醒

列表推导式会把所有结果放在内存里。如果数据量极大可能撑爆内存。这时候用生成器表达式。语法把方括号换成圆括号。             

adult_ages_gen = (age for age in ages if age >= 18)

生成器表达式不立即计算。每次next才产生一个值。内存占用恒定。适合流式处理。比如从文件读大列表。可以一边读一边过滤。不会一次性加载全部数据。速度上生成器表达式比列表推导式稍慢。因为每次取值有状态保存开销。可内存省了几十倍。取舍看场景。         

还有一个坑。推导式里的变量作用域。Python 3中推导式有自己的作用域。循环变量不会泄漏到外面。比如:         

x = 10 squares = [x*x for x in range(5)] print(x)  # 输出10

循环变量x只在推导式内部有效。外面还是原来的10。这个特性避免了很多bug。用普通for循环时x会被覆盖成4。写代码时容易忘了这一点。推导式自动隔离。         

 再看集合推导式。跟列表推导式一样。只是用花括号:         

unique_ages = {age for age in ages if age >= 18}

集合推导式自动去重。底层用哈希表。比先列表推导再set快。因为不需要构造中间列表。直接边过滤边去重。哈希冲突多的时候还是会慢。可大多数场景下够用。         

字典推导式还能做键值反转:         

original = {'a': 1, 'b': 2} reversed_dict = {v: k for k, v in original.items()}

如果值有重复。后面的会覆盖前面的。这个行为要清楚。写代码时确认值唯一。         

推导式里能调用函数吗。能。可别在里面做耗时操作。比如读写文件或网络请求。推导式设计用来做纯计算。混入副作用会让代码难读。性能也不一定好。因为函数调用开销还在。         

什么时候不该用推导式

逻辑复杂时。比如循环体里有多个分支和try-except。硬写成推导式会变成一行巨长的代码。可读性差。调试也难。这种情况用普通for循环。                 

代码是给人看的。速度差不了太多时优先选清晰。

推导式跟map和filter比怎么样。map和filter是内置函数。C实现。对于简单操作map可能更快。比如把列表每个元素加1:         

result = list(map(lambda x: x+1, data))

这个写法比列表推导式慢。因为lambda是Python函数。每次调用都要走Python调用栈。换成内置函数比如str.upper可以快:         

result = list(map(str.upper, strings))

这个比列表推导式快。因为str.upper是C函数。map直接调用它。没有Python层函数调用。            

 所以规则是:如果操作是内置C函数。用map。否则用列表推导式。

还有一个隐藏技巧。列表推导式里用局部变量缓存方法。比如:         

data = ['hello', 'world'] upper = str.upper result = [upper(s) for s in data]

把str.upper赋值给局部变量upper。在推导式里调用upper。比每次写str.upper快。因为属性查找只做一次。这个技巧在循环里也适用。推导式里同样有效。         
实际案例

处理日志文件。每行格式是时间戳加消息。需要提取所有错误级别的消息。用普通循环:         

errors = [] for line in open('log.txt'):     if 'ERROR' in line:         errors.append(line.strip())

改成推导式:         

errors = [line.strip() for line in open('log.txt') if 'ERROR' in line]

代码短了。速度也快了。可注意文件句柄。推导式不会自动关闭文件。用with语句包起来。或者把open结果先赋值给变量。跑完手动关。列表推导式不适合管理资源。
性能优势来自哪里

1没有函数调用开销。循环里append是方法调用。推导式用专用字节码。             

2结果列表预分配内存。解释器知道最终大小吗。不一定知道。可它用了一种增长策略比list.append更高效。             

3局部变量访问更快。推导式内部变量存在快速局部槽里。而普通循环里变量在全局或闭包中查找更慢。             

有人做过基准测试。对一百万个整数做平方。普通for循环加append耗时0.12秒。列表推导式耗时0.06秒。快一倍。如果加上条件过滤。差距更大。普通循环0.15秒。推导式0.07秒。         

推导式还能跟条件表达式一起用。比如把负数变零:         

data = [1, -2, 3, -4] result = [x if x > 0 else 0 for x in data]

注意这里if else的位置。它在for前面。这叫条件表达式。跟过滤用的if不同。过滤的if在for后面。写错顺序结果就错了。这个细节新手常犯。

 推导式里能用海象运算符吗。Python 3.8以上可以。比如:         

data = [1, 2, 3, 4, 5] result = [y for x in data if (y := x*2) > 5]

海象运算符在推导式里赋值。可读性不好。不推荐。除非逻辑确实需要避免重复计算。         

推导式也有递归限制。嵌套层数太多会报语法错误。一般没人写超过三层的推导式。两层已经够复杂了。         

性能对比总览

处理一千万个随机整数。筛选偶数并平方。

普通循环 — 1.2秒

map + filter + lambda — 1.8秒

列表推导式 — 0.5秒

map + 内置函数 — 0.4秒

可内置函数场景有限。大多数业务逻辑用列表推导式平衡最好。

学会这个技巧不需要记复杂语法。记住一点:当你写for循环往列表里append时。先想想能不能改成推导式。改完跑一下测试。速度提升看得见。代码行数还少了。         

 新手从今天起可以试试。把你项目里的一个循环改成列表推导式。感受一下运行时间的差异。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2420 篇文章

作家榜 »

  1. Pack 2420 文章
  2. 轩辕小不懂 2403 文章
  3. 小柒 2228 文章
  4. Nen 576 文章
  5. 王昭君 216 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章