查看: 232|回复: 4

Python列表推导式语法详解、条件过滤、嵌套循环与性能优化实践

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
列表推导式是Python中用于创建新列表的典型写法,核心价值是把“遍历数据源、对元素做变换、按条件筛选”压缩成一行可读表达式。它并非单纯炫技,而是在明确表达意图:我要基于某个可迭代对象生成一个新列表。本文围绕基本语法、条件过滤、嵌套循环、表达式技巧、性能差异以及字典和集合推导式展开,代码均按原文逻辑整理。

一、从for循环到列表推导式

传统写法先用空列表接收结果,再循环append;推导式把表达式和遍历写在方括号中。语法结构为:[表达式 for 变量 in 可迭代对象]。表达式决定对每个元素做什么,for部分决定数据来源。
  1. squares = []
  2. for x in range(10):
  3.     squares.append(x ** 2)
  4. print(squares)
  5. squares = [x ** 2 for x in range(10)]
  6. print(squares)
复制代码

最简单的推导式可以不做变换,只收集数据;也可以调用方法、做运算或调用函数。例如把单词转大写、给价格加税、对数字开平方并保留两位小数:
  1. words = ['hello', 'world', 'python']
  2. upper_words = [w.upper() for w in words]
  3. print(upper_words)
  4. prices = [100, 200, 300, 400]
  5. with_tax = [p * 1.1 for p in prices]
  6. print(with_tax)
  7. import math
  8. roots = [round(math.sqrt(x), 2) for x in range(1, 6)]
  9. print(roots)
复制代码

二、条件过滤与条件表达式

if在for后面时表示过滤,元素数量可能变少。例如只保留偶数、保留长度大于3的单词,或者用多个if实现类似and的条件。
  1. numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  2. evens = [x for x in numbers if x % 2 == 0]
  3. print(evens)
  4. words = ['a', 'ab', 'abc', 'abcd', 'abcde']
  5. long_words = [w for w in words if len(w) > 3]
  6. print(long_words)
  7. result = [x for x in range(50) if x % 2 == 0 if x % 3 == 0]
  8. print(result)
  9. data = ['hello', '', ' ', 'world', '\t', 'python']
  10. valid = [s for s in data if s.strip()]
  11. print(valid)
复制代码

if-else写在for前面时表示转换,不是过滤,结果长度不变。例如偶数保留本身、奇数置0,或者按分数标注及格与否。
  1. nums = [1, 2, 3, 4, 5]
  2. result = [x if x % 2 == 0 else 0 for x in nums]
  3. print(result)
  4. scores = [85, 45, 92, 58, 78, 30]
  5. grades = ['及格' if s >= 60 else '不及格' for s in scores]
  6. print(grades)
复制代码

过滤和转换可以组合:[转换表达式 for x in iterable if 过滤条件]。例如只对及格分数开平方,或者从文件列表中筛出.txt并转大写。原文还给出用自定义is_prime函数配合推导式筛选100以内素数的写法。
  1. scores = [85, 45, 92, 58, 78, 30, 66]
  2. passing_roots = [round(math.sqrt(s), 2) for s in scores if s >= 60]
  3. print(passing_roots)
  4. files = ['readme.txt', 'main.py', 'config.txt', 'test.py', 'data.csv']
  5. txt_upper = [f.upper() for f in files if f.endswith('.txt')]
  6. print(txt_upper)
复制代码

需要区分:if在for后面是筛选,if-else在for前面是二选一转换。混淆这两类写法,常见表现是结果元素数量与预期不一致。

三、嵌套循环、扁平化与矩阵操作

推导式中的多个for顺序与嵌套for循环一致:外层for在前,内层for在后。它可以生成笛卡尔积,也可以把二维或三维列表扁平化。
  1. pairs = [(x, y) for x in range(3) for y in range(2)]
  2. print(pairs)
  3. colors = ['红色', '蓝色']
  4. sizes = ['S', 'M', 'L']
  5. products = [(c, s) for c in colors for s in sizes]
  6. print(products)
  7. matrix = [
  8.     [1, 2, 3],
  9.     [4, 5, 6],
  10.     [7, 8, 9],
  11. ]
  12. flat = [item for row in matrix for item in row]
  13. print(flat)
  14. flat_evens = [item for row in matrix for item in row if item % 2 == 0]
  15. print(flat_evens)
复制代码

矩阵转置可以用嵌套推导式:外层按列索引遍历,内层按行取值。原文也指出它等价于zip(*matrix)的写法。单位矩阵则用条件表达式在行列相等时取1,否则取0。
  1. matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
  2. transposed = [[row[i] for row in matrix] for i in range(3)]
  3. print(transposed)
  4. print([list(col) for col in zip(*matrix)])
  5. n = 5
  6. identity = [[1 if i == j else 0 for j in range(n)] for i in range(n)]
  7. for row in identity:
  8.     print(row)
复制代码

四、表达式技巧与海象运算符

推导式里的表达式可以是方法调用、f-string、enumerate、zip、自定义函数或hashlib等库函数。关键是让表达式保持单一职责,不要塞入过多逻辑。
  1. names = ['小明', '小红', '小刚']
  2. greetings = [f'你好,{name}!' for name in names]
  3. print(greetings)
  4. data = [1, 2, 3, 4, 5]
  5. indexed = [(i, x) for i, x in enumerate(data)]
  6. print(indexed)
  7. names = ['小明', '小红', '小刚']
  8. ages = [25, 23, 26]
  9. profiles = [f'{n}今年{a}岁' for n, a in zip(names, ages)]
  10. print(profiles)
  11. import hashlib
  12. passwords = ['hello', 'world', 'python123']
  13. hashed = [hashlib.sha256(p.encode()).hexdigest()[:8] for p in passwords]
  14. print(hashed)
复制代码

Python 3.8+支持海象运算符 :=。当过滤条件需要复用表达式结果时,可用它避免重复计算。原文用math.sqrt演示:先赋值再判断,后续直接使用该值。对于代价较高的函数,这种写法能减少一次调用。
  1. import math
  2. roots = [s for x in range(10) if (s := math.sqrt(x)) > 2]
  3. print(roots)
  4. def expensive_computation(x):
  5.     return x ** 2
  6. result = [val for x in range(10) if (val := expensive_computation(x)) > 50]
  7. print(result)
复制代码

五、性能差异与生成器表达式

原文用time.perf_counter对比传统for循环、列表推导式和map+lambda三种写法。推导式通常更快,原因包括循环在C层面执行、append方法不必每次在Python层面查找、结果列表内存可预估从而减少扩容。
  1. import time
  2. n = 1000000
  3. start = time.perf_counter()
  4. result1 = []
  5. for i in range(n):
  6.     result1.append(i ** 2)
  7. t1 = time.perf_counter() - start
  8. start = time.perf_counter()
  9. result2 = [i ** 2 for i in range(n)]
  10. t2 = time.perf_counter() - start
  11. start = time.perf_counter()
  12. result3 = list(map(lambda i: i ** 2, range(n)))
  13. t3 = time.perf_counter() - start
  14. print(f'for循环: {t1:.3f}秒')
  15. print(f'推导式: {t2:.3f}秒')
  16. print(f'map + lambda: {t3:.3f}秒')
  17. print(f'推导式比for循环快 {(t1 / t2 - 1) * 100:.0f}%')
复制代码

列表推导式会一次性创建完整列表,生成器表达式则惰性求值。原文用sys.getsizeof展示内存差异:百万元素的列表推导式占用较大内存,而生成器表达式对象很小。生成器表达式适合只需要遍历一次、数据量非常大、不想创建中间列表,或作为函数参数传递的场景。注意生成器表达式的圆括号不是元组。
  1. import sys
  2. n = 1000000
  3. list_comp = [i ** 2 for i in range(n)]
  4. print(f'列表推导式内存: {sys.getsizeof(list_comp):,} 字节')
  5. gen_expr = (i ** 2 for i in range(n))
  6. print(f'生成器表达式内存: {sys.getsizeof(gen_expr)} 字节')
  7. total = sum(i ** 2 for i in range(10000000))
复制代码

六、最佳实践与常见误区

推导式应只用于创建新列表、字典或集合,不应在其中做打印、修改外部变量等副作用操作。一个推导式如果嵌套太多for和if,或者表达式本身很长,读者需要在脑中反复编译,应该拆成多步循环或辅助函数。为了用推导式而用推导式并不可取,代码清晰度优先。

可读性检查可以从几个问题入手:能否一句话描述作用;变量名是否清晰;表达式和数据源是否一眼可见;是否超过两个for;是否超过两个if。简单过滤、简单映射、过滤加映射、二维扁平化和构建字典/集合通常是合适的;过于复杂的嵌套应拆分。
  1. evens = [x for x in numbers if x % 2 == 0]
  2. names = [user.name for user in users]
  3. active_names = [user.name for user in users if user.is_active]
  4. flat_cells = [cell for row in table for cell in row]
  5. name_map = {user.id: user.name for user in users}
  6. unique_tags = {tag for article in articles for tag in article.tags}
复制代码

七、字典推导式和集合推导式

字典推导式语法为{key_expr: value_expr for item in iterable if condition},可用于生成平方字典、反转键值、按条件过滤字典,或用zip把两个列表组装成字典。集合推导式为{expr for item in iterable if condition},天然适合去重和提取标签。
  1. squares = {x: x ** 2 for x in range(6)}
  2. print(squares)
  3. original = {'a': 1, 'b': 2, 'c': 3}
  4. reversed_dict = {v: k for k, v in original.items()}
  5. print(reversed_dict)
  6. scores = {'小明': 85, '小红': 92, '小刚': 78, '小李': 95}
  7. passing = {name: score for name, score in scores.items() if score >= 90}
  8. print(passing)
  9. keys = ['name', 'age', 'city']
  10. values = ['小明', 25, '北京']
  11. person = {k: v for k, v in zip(keys, values)}
  12. print(person)
  13. words = ['hello', 'world', 'hello', 'python', 'world']
  14. unique_words = {w.lower() for w in words}
  15. print(unique_words)
  16. articles = [
  17.     {'title': '文章1', 'tags': ['Python', '编程']},
  18.     {'title': '文章2', 'tags': ['Java', '编程']},
  19.     {'title': '文章3', 'tags': ['Python', 'Web']},
  20. ]
  21. all_tags = {tag for article in articles for tag in article['tags']}
  22. print(all_tags)
复制代码

八、适用场景与落地建议

列表推导式最适合“纯变换、纯筛选、纯构建”的场景:数据预处理、字段映射、二维数据扁平化、矩阵转置、去重、标签汇总等。实战中可以把过滤和格式化组合成流水线,例如先过滤None和空记录,再对字段做strip、title等格式化,最后构造新的字典结构。原文在实战部分给出了数据清洗流水线的思路,但该段代码展示不完整,本文不补写未出现的实现,重点掌握前面的组合方式即可。需要副作用、复杂分支、异常处理或日志输出时,传统循环往往更清晰,也更容易调试。兼容性方面,海象运算符要求Python 3.8+;生成器表达式与列表推导式内存行为不同,应按数据规模和遍历次数选择。
回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python列表推导式语法详解、条件过滤、嵌套循环与性能优化实践

写得很系统,尤其是把“if 在 for 后面是过滤、if-else 在 for 前面是转换”这一点单独拎出来强调,这点对新手特别有用,很多人结果长度对不上就是栽在这里。嵌套循环那部分用扁平化和笛卡尔积举例也很直观,比干讲顺序好理解多了。 不过有个小地方想提醒一下:矩阵转置那段代码好像笔误了,写成 `[[row for row in matrix] for i in range(3)]` 的话,结果其实是把原矩阵原样重复了三遍,并没有转置。按前面说的“外层按列索引、内层按行取值”,应该是 `[[row for row in matrix] for i in range(3)]` 才对,正好也和后面 `zip(*matrix)` 的写法对得上。 另外帖子好像发到“海象运算符”那里就断了,性能和字典、集合推导式部分还没看到,期待楼主补全,尤其想看看性能那块有没有和 map/filter 或者生成器表达式的对比。
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python列表推导式语法详解、条件过滤、嵌套循环与性能优化实践

写得很系统,从最基础的 for+append 对照到条件过滤、嵌套循环、矩阵转置,基本把列表推导式的常见坑都覆盖了。特别是把“if 在 for 后面是筛选、if-else 在 for 前面是二选一转换”单独拎出来强调,这点对新手特别有用,很多人写出来元素个数不对就是卡在这儿。 有几处我自己也有同感:多个 for 的顺序等于嵌套循环的外层在前、内层在后,这个规则说起来简单,但写扁平化的时候一不留神就写反了;另外像矩阵转置那种“外层按列索引、内层按行取值”的写法,虽然能跑,实际项目里我更倾向直接用 zip(*matrix),可读性更好,推导式版本留着理解原理就行。 补充一点实践上的感受:推导式适合“生成一个新列表”这种明确目的,如果只是为了遍历执行副作用(打印、写文件、调用接口),还是老老实实用 for 循环更清楚;嵌套超过两层或者表达式里塞了函数调用加条件判断,也建议拆回普通循环,不然过两周自己都得盯着看半天。数据量大的时候,如果只是要迭代一次,生成器表达式比列表推导式省内存。 另外帖子到“四、表达式技巧与海象运算符”那里好像被截断了,海象运算符在推导式里的用法(比如避免重复计算)和后面字典、集合推导式、性能对比的部分没看到,方便的话可以补完,挺想看看原文对性能那块是怎么测的。
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python列表推导式语法详解、条件过滤、嵌套循环与性能优化实践

这篇整理得挺系统的,从普通 for 循环过渡到列表推导式,再把 if 过滤、if-else 转换、嵌套循环扁平化和矩阵操作分开讲,思路很清楚。尤其是“if 在 for 后面是筛选,if-else 在 for 前面是二选一转换”这个区分很实用,嵌套 for 的顺序也解释得明白。 不过有个小地方可以提醒一下:矩阵转置那段里 transposed = [[row for row in matrix] for i in range(3)] 实际上是把原矩阵按行复制了三遍,并不是转置;后面用 zip(*matrix) 的写法才是转置思路。建议把这处示例修正一下,免得读者照着写时结果不对。整体内容挺适合入门和查漏补缺,性能优化和字典、集合推导式部分如果还能继续展开就更完整了。
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python列表推导式语法详解、条件过滤、嵌套循环与性能优化实践

这篇整理得很扎实,基本把列表推导式常见的坑和用法都串起来了。我最有共鸣的是你强调“if 在 for 后面是过滤,if-else 在 for 前面是转换”,这个点确实很容易混淆,尤其写多了以后一不留神就会把结果长度搞错。嵌套循环部分用笛卡尔积、二维列表扁平化和矩阵转置来举例也很直观,外层 for 在前、内层 for 在后的顺序说清楚了。 另外,像多个 if 连写、用 strip 过滤空白字符串、以及自定义函数配合筛选素数这些例子,都挺适合实际写脚本时参考。一个小建议是,后面如果继续补充性能部分,可以把列表推导式和生成器表达式在内存占用上的差异、以及嵌套层数太多时可读性下降的问题一起讲一下,这样新手在选择“写推导式还是退回普通循环”时会更清楚。整体看是一篇很适合收藏备查的总结。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-11-7 04:16 , Processed in 0.030953 second(s), 28 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部