列表推导式
列表推导式(List Comprehension)是 Python 中最强大、最 Pythonic 的特性之一。它用一行代码替代循环+条件+append 的多行写法,既简洁又高效。
基本语法
# 传统写法
squares = []
for x in range(10):
squares.append(x ** 2)
# 列表推导式
squares = [x ** 2 for x in range(10)]
print squares # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
语法结构:[expression for item in iterable]。先写要生成的表达式,再写循环变量和来源。
带条件的推导式
# 只保留偶数的平方
even_squares = [x ** 2 for 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)
语法结构:[expression for item in iterable if condition]。条件为真时,才执行表达式并加入结果列表。
多个循环变量
# 笛卡尔积:所有坐标对
points = [(x, y) for x in range(3) for y in range(3)]
print points
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
# 传统写法
points = []
for x in range(3):
for y in range(3):
points.append((x, y))
多个 for 等价于嵌套循环,从左到右依次嵌套。
实际应用
# 提取字符串列表的长度
words = ["apple", "banana", "cherry"]
lengths = [len(w) for w in words]
print lengths # [5, 6, 6]
# 过滤 + 转换:只保留正数并取平方根
import math
nums = [-2, 3, -5, 8, -1, 4]
roots = [math.sqrt(x) for x in nums if x > 0]
print roots # [1.732..., 2.828..., 2.0]
# 扁平化二维列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
print flat # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 字符串处理:提取大写字母
s = "HelloWorldPython"
caps = [c for c in s if c.isupper()]
print caps # ['H', 'W', 'P']
与 map/filter 的对比
Python 2 中,map 和 filter 也可以实现类似功能:
# map + lambda
squares = map(lambda x: x ** 2, range(10))
# filter + lambda
evens = filter(lambda x: x % 2 == 0, range(10))
# 列表推导式(推荐)
squares = [x ** 2 for x in range(10)]
evens = [x for x in range(10) if x % 2 == 0]
列表推导式通常比 map/filter + lambda 更易读,而且避免了 lambda 的函数调用开销。Python 社区共识是:简单场景用列表推导式,复杂逻辑用普通循环。
嵌套列表推导式
# 转置矩阵
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = [[row[i] for row in matrix] for i in range(3)]
print transposed # [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
外层循环 for i in range(3) 控制行,内层 for row in matrix 控制列。这种嵌套推导式可读性较差,超过两层时建议用普通循环。
注意事项
不要在推导式中做副作用操作:
# 错误:推导式中调用 print
[x for x in range(5) if print(x)] # 混乱且无效
# 正确:用普通循环
for x in range(5):
print x
推导式比循环快:
import timeit
# 列表推导式
t1 = timeit.timeit('[x**2 for x in range(1000)]', number=10000)
# 普通循环
t2 = timeit.timeit('''
result = []
for x in range(1000):
result.append(x**2)
''', number=10000)
print t1, t2 # 推导式通常快 1.5~2 倍
推导式在 C 层面执行循环,避免了 Python 层面的函数调用和属性查找开销。