列表推导式(List Comprehension)是 Python 中用于生成列表的一种简洁语法,它可以把遍历、条件筛选、元素转换和结果收集写在一个表达式中,适合构造结构简单的新列表。
一、什么是列表推导式
1、基本语法
列表推导式使用方括号 [ ] 生成列表,基本形式为:
[输出表达式 for 变量 in 可迭代对象]
例如,生成 0 到 4 的平方:
squares = [x ** 2 for x in range(5)]print(squares)
输出:
[0, 1, 4, 9, 16]这段列表推导式可以分成三个部分:
x ** 2 # 对当前元素进行计算for x # 将当前元素依次绑定给 xin range(5) # 指定要遍历的数据
列表推导式会依次遍历 range(5) 中的元素,对每个元素计算 x ** 2,再把结果收集到新列表中。
2、与普通循环的对应关系
列表推导式:
squares = [x ** 2 for x in range(5)]等价于:
squares = []for x in range(5):squares.append(x ** 2)
两种写法都完成了三个步骤:
• 遍历可迭代对象
• 处理当前元素
• 将结果加入新列表
在 Python 3 中,列表推导式中的循环变量只在推导式内部有效,不会覆盖外部的同名变量。
3、输出表达式决定列表元素
列表推导式中,for 前面的表达式决定新列表中要保存什么内容。
例如,将数字及其平方组成元组:
pairs = [(x, x ** 2) for x in range(5)]print(pairs)
输出:
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16)]虽然元素可以是数字、字符串、元组、列表、字典或其他对象,但列表推导式生成的结果始终是列表。
二、带条件的列表推导式
列表推导式中的条件主要有两种用途:
• 条件筛选:决定当前遍历到的元素是否参与结果生成
• 条件表达式:根据条件选择当前要生成的新列表元素
两种写法中,if 所在的位置不同。
1、条件筛选
在 for 后面添加 if,可以只保留满足条件的元素。
基本语法:
[输出表达式 for 变量 in 可迭代对象 if 条件]
这里的 if 用于过滤元素。只有条件成立时,当前元素才会送入输出表达式。
例如,筛选偶数并计算平方:
even_squares = [x ** 2for x in range(10)if x % 2 == 0]print(even_squares)
输出:
[0, 4, 16, 36, 64]等价写法为:
even_squares = []for x in range(10):if x % 2 == 0:even_squares.append(x ** 2)print(even_squares)
2、条件表达式
条件表达式可以看作输出表达式的一种形式,用于根据条件生成不同结果。
基本语法:
[结果1 if 条件 else 结果2 for 变量 in 可迭代对象]
例如,为数字添加奇偶标签:
labels = ["even" if x % 2 == 0 else "odd"for x in range(5)]print(labels)
输出:
['even', 'odd', 'even', 'odd', 'even']等价写法为:
labels = []for x in range(5):if x % 2 == 0:labels.append("even")else:labels.append("odd")
三、与常用遍历工具组合
列表推导式经常与 range()、enumerate()、zip()等遍历工具,以及对象方法和自定义函数配合使用。
1、与 range() 配合
range() 常用于生成整数序列。
row_names = [f"第{x}行"for x in range(1, 5)]print(row_names)
输出:
['第1行', '第2行', '第3行', '第4行']2、与 enumerate() 配合
enumerate() 可以在遍历元素时,同时得到元素的索引和值。
words = ["python", "java", "c++"]indexed_words = [(index, word)for index, word in enumerate(words)]print(indexed_words)
输出:
[(0, 'python'), (1, 'java'), (2, 'c++')]如果希望编号从 1 开始,可以使用 start 参数:
enumerate(words, start=1)3、与 zip() 配合
zip() 可以同时遍历多个可迭代对象中相同位置的元素。
names = ["张三", "李四", "王五"]scores = [90, 82, 95]records = [f"{name}: {score}"for name, score in zip(names, scores)]print(records)
输出:
['张三: 90', '李四: 82', '王五: 95']需要注意,zip() 会在最短的可迭代对象结束时停止。
4、调用方法或自定义函数
列表推导式的输出表达式可以直接调用对象方法。
例如,去除字符串两端的空白并转换为小写:
words = [" Python ", " Java", "C++ "]cleaned_words = [word.strip().lower()for word in words]print(cleaned_words)
输出:
['python', 'java', 'c++']也可以调用自定义函数:
def double(value):return value * 2numbers = [1, 2, 3, 4]results = [double(number)for number in numbers]print(results)
输出:
[2, 4, 6, 8]当处理逻辑稍长时,可以先将逻辑封装为函数,再在列表推导式中调用。这样通常比直接堆叠复杂表达式更容易理解。
四、包含多个 for 子句的列表推导式
列表推导式中可以连续使用多个 for 子句,表示多层循环。
基本语法:
[输出表达式 for 变量1 in 可迭代对象1 for 变量2 in 可迭代对象2]
其中,for 子句从左到右对应普通循环由外到内的顺序。
例如,生成二维坐标点:
points = [(x, y)for x in range(2)for y in range(3)]print(points)
输出:
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]等价写法为:
points = []for x in range(2):for y in range(3):points.append((x, y))
因此,在:
[(x, y) for x in range(2) for y in range(3)]中:
• for x 是外层循环
• for y 是内层循环
多个 for 子句后面同样可以添加筛选条件。
例如,筛选出 x 与 y 之和为偶数的组合:
pairs = [(x, y)for x in range(3)for y in range(3)if (x + y) % 2 == 0]print(pairs)
输出:
[(0, 0), (0, 2), (1, 1), (2, 0), (2, 2)]这相当于:
pairs = []for x in range(3):for y in range(3):if (x + y) % 2 == 0:pairs.append((x, y))
多个 for 子句产生的结果同样会送入输出表达式,因此通常仍然会形成扁平列表。
五、嵌套列表推导式
嵌套列表推导式是指:一个列表推导式的输出表达式部分,本身又是另一个列表推导式。
基本形式为:
[
[内层输出表达式 for 内层变量 in 内层可迭代对象]
for 外层变量 in 外层可迭代对象
]
通常可用于生成二维列表或其他嵌套结构。
例如,生成 3 行、4 列的二维列表:
matrix = [[0 for column in range(4)]for row in range(3)]print(matrix)
输出:
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]等价写法为:
matrix = []for row in range(3):result_row = []for column in range(4):result_row.append(0)matrix.append(result_row)print(matrix)
外层循环每执行一次,就会生成一个完整的内层列表。
再例如,使用嵌套列表推导式实现转置:
A = [[1, 2, 3],[4, 5, 6]]column_count = len(A[0])result = [[A[row][column]for row in range(len(A))]for column in range(column_count)]print(result)
输出:
[[1, 4], [2, 5], [3, 6]]可以看出,嵌套列表推导式没有引入新的语法规则,本质上是将外层列表推导式的输出表达式写成另一个列表推导式。
六、其他推导式速览
除列表推导式外,Python 还支持集合推导式、字典推导式和生成器表达式。
1、集合推导式
集合推导式使用花括号 { } 生成集合:
values = {x % 3 for x in range(6)}结果:
{0, 1, 2}集合会自动去除重复元素。
2、字典推导式
字典推导式也使用花括号 { },并且需要在输出表达式中提供键值对:
mapping = {x: x ** 2for x in range(5)}
结果:
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}3、生成器表达式
生成器表达式使用圆括号 ( ),不会一次性保存全部结果,而是在迭代时按需产生元素。处理大量数据时通常更节省内存。
例如:
generator = (x ** 2 for x in range(5))print(next(generator))print(next(generator))
输出:
01
七、常见问题及使用建议
1、优先保证可读性
列表推导式在简单场景中可能比普通循环更紧凑或更高效,但选择写法时应优先考虑可读性。
需要多个步骤、异常处理或中间变量时,普通循环通常更合适。
2、避免堆叠过多逻辑
下面的写法虽然能够运行,但较难阅读:
result = [x * yfor x in range(5)if x % 2 == 0for y in range(5)if y % 2 == 1]
当推导式中包含多个循环、多个条件或复杂表达式时,普通循环通常更加清楚。
3、避免副作用操作
列表推导式的主要目的,是根据已有数据生成新列表。
不建议只为了执行输出或修改外部状态而使用:
[print(x) for x in range(5)]这段代码会生成一个由 None 组成的列表:
[None, None, None, None, None]如果只是需要输出,应使用普通循环:
for x in range(5):print(x)
如果主要目的是输出、写入文件或修改外部状态,应使用普通循环明确表达。
📘 小结
列表推导式可以在遍历过程中完成元素转换和条件筛选,适合生成结构简单的新列表。多个 for 子句通常将结果直接收集到同一个列表中,嵌套列表推导式则用于生成嵌套结构。使用时应优先保证代码清晰,避免堆叠过多逻辑和执行副作用操作。
