对python的再认识-基于数据结构进行-a002-列表-列表推导式
Python 列表推导式-与list()拓展
本文聚焦 Python 列表推导式(List Comprehension)的完整用法,包含基础语法、高级技巧、与 list() 的对比及最佳实践。
我也将给出个人思维导图

一、基础语法
1.1 基本形式
列表推导式用一行代码替代传统的 for 循环,语法结构为:
[表达式 for 变量 in 可迭代对象]
示例对比:
# 传统写法
squares = []
for x in range(5):
squares.append(x ** 2)
# 列表推导式
squares = [x ** 2 for x in range(5)]
# 结果: [0, 1, 4, 9, 16]
1.2 带条件的推导式
添加 if 子句进行过滤:
# 只保留偶数
evens = [x for x in range(10) if x % 2 == 0]
# 结果: [0, 2, 4, 6, 8]
# 过滤字符串
words = ['hello', 'world', 'python', 'hi']
long_words = [w for w in words if len(w) > 3]
# 结果: ['hello', 'world', 'python']
# 多条件过滤
nums = [1, 2, 3, 4, 5, 6]
result = [x for x in nums if x > 2 and x < 6]
# 结果: [3, 4, 5]
注意:条件
if放在for之后,只用于过滤,不用于分支选择。
1.3 带分支的推导式
使用 if-else 三元表达式进行分支选择:
# 根据条件返回不同值
result = ['偶数' if x % 2 == 0 else '奇数' for x in range(5)]
# 结果: ['偶数', '奇数', '偶数', '奇数', '偶数']
# 数值转换
nums = [1, -2, 3, -4, 5]
abs_nums = [x if x >= 0 else -x for x in nums]
# 结果: [1, 2, 3, 4, 5]
# 复杂分支
scores = [85, 92, 78, 65, 95]
grades = ['A' if s >= 90 else 'B' if s >= 80 else 'C' for s in scores]
# 结果: ['B', 'A', 'C', 'C', 'A']
注意:
if-else放在for之前,用于表达式的分支选择。
二、高级用法
2.1 双重循环
处理嵌套可迭代对象:
# 生成所有坐标组合
coords = [(x, y) for x in range(3) for y in range(2)]
# 结果: [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
# 等价于:
coords = []
for x in range(3):
for y in range(2):
coords.append((x, y))
# 双重循环带条件
pairs = [(x, y) for x in range(3) for y in range(3) if x != y]
# 结果: [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
2.2 嵌套列表扁平化
将二维列表展开为一维:
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# 扁平化
flat = [num for row in matrix for num in row]
# 结果: [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 等价于:
flat = []
for row in matrix:
for num in row:
flat.append(num)
# 带条件过滤
even_flat = [num for row in matrix for num in row if num % 2 == 0]
# 结果: [2, 4, 6, 8]
注意:循环顺序与书写顺序一致,外层循环在前,内层循环在后。
2.3 结合函数调用
在表达式中调用函数:
# 字符串处理
names = ['alice', 'bob', 'charlie']
capitalized = [name.title() for name in names]
# 结果: ['Alice', 'Bob', 'Charlie']
# 数学运算
import math
rounded = [round(math.sqrt(x), 2) for x in range(1, 6)]
# 结果: [1.0, 1.41, 1.73, 2.0, 2.24]
# 自定义函数
def process(x):
return x * 2 + 1
result = [process(x) for x in range(5)]
# 结果: [1, 3, 5, 7, 9]
2.4 与 enumerate/range 组合
# 带索引的推导式
words = ['apple', 'banana', 'cherry']
indexed = [f"{i}: {w}" for i, w in enumerate(words)]
# 结果: ['0: apple', '1: banana', '2: cherry']
# 带索引的条件
indexed_even = [w for i, w in enumerate(words) if i % 2 == 0]
# 结果: ['apple', 'cherry']
# 步长范围
evens = [x for x in range(0, 10, 2)]
# 结果: [0, 2, 4, 6, 8]
三、与 list() 构造函数对比
3.1 list() 基础用法
list() 是列表的构造函数,用于创建列表:
# 创建空列表
empty = list()
# 等同于: empty = []
# 从可迭代对象创建
nums = list(range(5))
# 结果: [0, 1, 2, 3, 4]
# 从字符串创建
chars = list('hello')
# 结果: ['h', 'e', 'l', 'l', 'o']
# 从元组创建
tup = (1, 2, 3)
lst = list(tup)
# 结果: [1, 2, 3]
3.2 list() 转换场景
list() 主要用于类型转换:
# 字典转列表(只保留键)
d = {'a': 1, 'b': 2}
keys = list(d)
# 结果: ['a', 'b']
# 字典 items() 转列表
items = list(d.items())
# 结果: [('a', 1), ('b', 2)]
# 集合转列表
s = {1, 2, 3}
lst = list(s)
# 结果: [1, 2, 3](顺序可能不同)
# 迭代器转列表
it = iter([1, 2, 3])
lst = list(it)
# 结果: [1, 2, 3]
3.3 列表推导式 vs list() + map
# 使用 list() + map
nums = [1, 2, 3, 4]
squares = list(map(lambda x: x ** 2, nums))
# 结果: [1, 4, 9, 16]
# 使用列表推导式(更清晰)
squares = [x ** 2 for x in nums]
# 结果: [1, 4, 9, 16]
# 带条件的过滤
# map 需要配合 filter
evens_squared = list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, nums)))
# 列表推导式(更简洁)
evens_squared = [x ** 2 for x in nums if x % 2 == 0]
四、性能对比
4.1 速度测试
import timeit
# 测试代码:生成 0-999999 的平方
TEST_COMP = "[x ** 2 for x in range(1000000)]"
TEST_FOR = """
result = []
for x in range(1000000):
result.append(x ** 2)
"""
TEST_MAP = "list(map(lambda x: x ** 2, range(1000000)))"
| 方法 | 相对速度 | 说明 |
|---|---|---|
| 列表推导式 | 1.0x | 基准,最快 |
| map + lambda | 1.3x | 略慢,lambda 有开销 |
| 传统 for 循环 | 1.5x | append 调用有额外开销 |
4.2 性能优势原因
- 字节码优化:列表推导式使用专用字节码指令
LIST_APPEND - 减少方法查找:避免
append方法每次调用时的查找 - C 层执行:部分循环逻辑在 C 层高效执行
4.3 内存对比
# 列表推导式 - 立即创建完整列表
lst = [x ** 2 for x in range(1000000)] # 占用 ~8MB 内存
# list() + map - 同样创建完整列表
lst = list(map(lambda x: x ** 2, range(1000000))) # 占用 ~8MB 内存
# 生成器表达式 - 惰性计算(注意是圆括号)
gen = (x ** 2 for x in range(1000000)) # 几乎不占用内存
sum(gen) # 需要时才计算
注意:生成器表达式
(x for x in ...)不是列表推导式,它返回生成器对象而非列表。
五、最佳实践
5.1 何时使用列表推导式
推荐场景:
# 简单转换
doubled = [x * 2 for x in range(10)]
# 简单过滤
positives = [x for x in nums if x > 0]
# 组合操作
result = [x.title() for x in names if len(x) > 3]
# 提取属性
ages = [person.age for person in people]
5.2 何时不使用
不推荐场景:
# 过于复杂 - 可读性差
# 不推荐
bad = [(x, y, x*y) for x in range(10) for y in range(10) if x != y if x * y > 10]
# 推荐 - 使用传统循环
good = []
for x in range(10):
for y in range(10):
if x != y and x * y > 10:
good.append((x, y, x * y))
# 多重嵌套分支
# 不推荐
bad = [x ** 2 if x % 2 == 0 else x ** 3 if x < 5 else x for x in range(10)]
# 推荐
good = []
for x in range(10):
if x % 2 == 0:
good.append(x ** 2)
elif x < 5:
good.append(x ** 3)
else:
good.append(x)
5.3 代码风格建议
| 原则 | 好的示例 | 不好的示例 |
|---|---|---|
| 保持简短 | [x*2 for x in nums] | [very_long_expression for x in nums] |
| 避免嵌套过深 | 单层或双层循环 | 三层及以上循环 |
| 有意义的变量名 | [s.title() for s in names] | [i for i in j] |
| 优先可读性 | 复杂逻辑用传统循环 | 强行用推导式 |
六、常见陷阱
6.1 副作用
# 不推荐:推导式只用于创建列表,不应有副作用
results = [print(x) for x in range(5)]
# 结果: [None, None, None, None, None] - 打印了 5 次
# 推荐:分离逻辑
for x in range(5):
print(x)
results = [x for x in range(5)]
6.2 可变对象引用
# 错误:创建多个引用同一列表
matrix = [[0] * 3] * 3
matrix[0][1] = 5
# matrix: [[0, 5, 0], [0, 5, 0], [0, 5, 0]] - 所有行都被修改
# 正确:每行是独立列表
matrix = [[0] * 3 for _ in range(3)]
matrix[0][1] = 5
# matrix: [[0, 5, 0], [0, 0, 0], [0, 0, 0]] - 只修改第一行
6.3 混淆 if 位置
# if 在 for 之后:过滤
[x for x in range(10) if x % 2 == 0] # [0, 2, 4, 6, 8]
# if-else 在 for 之前:分支
[x if x % 2 == 0 else -1 for x in range(5)] # [0, -1, 2, -1, 4]
# 常见错误:把过滤 if 放在前面
# [x if x % 2 == 0 for x in range(10)] # SyntaxError
七、总结
| 方面 | 要点 |
|---|---|
| 核心语法 | [表达式 for 变量 in 可迭代对象 if 条件] |
| if 位置 | 过滤用 for ... if,分支用 ... if ... else ... for |
| 优势 | 简洁、快速、可读性强 |
| 局限 | 复杂逻辑降低可读性 |
| 最佳实践 | 简单操作用推导式,复杂逻辑用循环 |
| vs list() | list() 用于类型转换,推导式用于生成新列表 |









