Python 推导式
目录
学习目标
- 掌握列表推导式、字典推导式、集合推导式的语法
- 理解推导式的执行原理
- 学会使用带条件的推导式
- 了解生成器表达式
一、什么是推导式
推导式(Comprehension)是 Python 提供的一种简洁、高效的创建容器(列表、字典、集合)的语法。它将循环和条件判断压缩到一行中,代码更简洁、执行效率更高。
二、列表推导式
2.1 基本语法
# 语法:[表达式 for 变量 in 可迭代对象]
squares = [x**2 for x in range(10)]
print(squares)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 等价于普通循环
squares_loop = []
for x in range(10):
squares_loop.append(x**2)2.2 带条件的列表推导式
# 语法:[表达式 for 变量 in 可迭代对象 if 条件]
evens = [x for x in range(20) if x % 2 == 0]
print(evens)
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# 等价循环
evens_loop = []
for x in range(20):
if x % 2 == 0:
evens_loop.append(x)
# if-else 条件(表达式中)
labels = ["偶数" if x % 2 == 0 else "奇数" for x in range(5)]
print(labels)
# ['偶数', '奇数', '偶数', '奇数', '偶数']2.3 多重循环的列表推导式
# 语法:[表达式 for 变量1 in 可迭代对象1 for 变量2 in 可迭代对象2]
# 笛卡尔积
pairs = [(x, y) for x in [1, 2] for y in ['a', 'b']]
print(pairs)
# [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]
# 等价循环
pairs_loop = []
for x in [1, 2]:
for y in ['a', 'b']:
pairs_loop.append((x, y))
# 带条件的多重循环
products = [x * y for x in range(1, 5) for y in range(1, 5) if x != y]
print(products)
# [2, 3, 4, 2, 6, 8, 3, 6, 12, 4, 8, 12]2.4 嵌套列表推导式
# 矩阵转置
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
print(transposed)
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
# 扁平化嵌套列表
nested = [[1, 2], [3, 4], [5, 6]]
flat = [x for sublist in nested for x in sublist]
print(flat)
# [1, 2, 3, 4, 5, 6]2.5 实用示例
# 提取字符串中的数字
s = "abc123def456"
numbers = [int(c) for c in s if c.isdigit()]
print(numbers) # [1, 2, 3, 4, 5, 6]
# 过滤列表中的空值
items = [0, 1, None, 2, "", 3, [], 4]
filtered = [x for x in items if x]
print(filtered) # [1, 2, 3, 4]
# 字符串处理
words = ["Hello", "WORLD", "Python"]
lower_words = [w.lower() for w in words]
print(lower_words) # ['hello', 'world', 'python']
# 获取文件列表(仅 .py 文件)
import os
# py_files = [f for f in os.listdir('.') if f.endswith('.py')]三、字典推导式
3.1 基本语法
# 语法:{键: 值 for 变量 in 可迭代对象}
squares = {x: x**2 for x in range(6)}
print(squares)
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}3.2 带条件的字典推导式
# 只保留偶数键
evens = {x: x**2 for x in range(10) if x % 2 == 0}
print(evens)
# {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
# 交换键值(值必须可哈希)
original = {"a": 1, "b": 2, "c": 3}
swapped = {v: k for k, v in original.items()}
print(swapped)
# {1: 'a', 2: 'b', 3: 'c'}3.3 实用示例
# 从两个列表创建字典
keys = ["name", "age", "city"]
values = ["Alice", 25, "Beijing"]
user = {k: v for k, v in zip(keys, values)}
print(user)
# {'name': 'Alice', 'age': 25, 'city': 'Beijing'}
# 统计字符出现次数(更简洁写法用 Counter)
text = "hello world"
char_count = {char: text.count(char) for char in set(text) if char != ' '}
print(char_count)
# {'h': 1, 'e': 1, 'l': 3, 'o': 2, 'w': 1, 'r': 1, 'd': 1}
# 过滤字典
scores = {"Alice": 85, "Bob": 92, "Charlie": 78, "David": 95}
passed = {name: score for name, score in scores.items() if score >= 80}
print(passed)
# {'Alice': 85, 'Bob': 92, 'David': 95}四、集合推导式
4.1 基本语法
# 语法:{表达式 for 变量 in 可迭代对象}
squares = {x**2 for x in range(10)}
print(squares)
# {0, 1, 64, 4, 36, 9, 16, 49, 81, 25}
# 自动去重
unique_lengths = {len(w) for w in ["apple", "banana", "pear", "peach"]}
print(unique_lengths)
# {4, 5, 6}4.2 带条件的集合推导式
# 提取文本中的不重复元音
text = "Hello Python World"
vowels = {c.lower() for c in text if c.lower() in 'aeiou'}
print(vowels)
# {'o', 'e'}
# 找出两个列表中的共同数字的平方
a = [1, 2, 3, 4, 5]
b = [3, 4, 5, 6, 7]
common_squares = {x**2 for x in a if x in b}
print(common_squares)
# {16, 25, 9}五、生成器表达式
生成器表达式与列表推导式语法类似,但使用圆括号 (),它不会一次性生成所有元素,而是惰性求值,节省内存。
# 列表推导式(占用内存)
squares_list = [x**2 for x in range(1000000)]
print(type(squares_list)) # <class 'list'>
# 生成器表达式(节省内存)
squares_gen = (x**2 for x in range(1000000))
print(type(squares_gen)) # <class 'generator'>
# 逐个获取
print(next(squares_gen)) # 0
print(next(squares_gen)) # 1
print(next(squares_gen)) # 4
# 用于循环
for val in squares_gen:
if val > 100:
break
print(val, end=" ")
# 生成器可转换为列表
first_10 = list(x**2 for x in range(10))
print(first_10)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]生成器表达式的优势
import sys
# 列表推导式
list_comp = [x**2 for x in range(10000)]
print(f"列表大小: {sys.getsizeof(list_comp)} bytes")
# 生成器表达式
gen_exp = (x**2 for x in range(10000))
print(f"生成器大小: {sys.getsizeof(gen_exp)} bytes")
# 生成器大小远小于列表
# 处理大数据时的优势
# 求 1 到 1000 万所有偶数的平方和
# 列表推导式可能内存不足
result = sum(x**2 for x in range(10000000) if x % 2 == 0)
print(result)六、推导式 vs 循环的性能
import time
# 测试列表推导式 vs 普通循环
n = 1000000
# 普通循环
start = time.time()
result_loop = []
for i in range(n):
result_loop.append(i * 2)
time_loop = time.time() - start
# 列表推导式
start = time.time()
result_comp = [i * 2 for i in range(n)]
time_comp = time.time() - start
print(f"循环耗时: {time_loop:.4f} 秒")
print(f"推导式耗时: {time_comp:.4f} 秒")
# 推导式通常更快七、何时不用推导式
推导式虽然简洁,但过度使用会降低可读性。
# 过于复杂的推导式(不推荐)
# result = [x * y + z for x in range(10) for y in range(10) if x > 5 for z in range(5) if z < 3]
# 上述情况使用普通循环更清晰
result = []
for x in range(10):
if x > 5:
for y in range(10):
for z in range(5):
if z < 3:
result.append(x * y + z)
# 有副作用的操作不用推导式
# 不推荐:[_ for _ in [print(x) for x in range(5)]]
# 推荐:
for x in range(5):
print(x)
# 嵌套太深的推导式(最多两层)
# 超过两层建议用循环八、综合示例
"""
使用推导式处理数据
"""
# 原始数据
students = [
{"name": "Alice", "scores": [85, 92, 78]},
{"name": "Bob", "scores": [76, 88, 95]},
{"name": "Charlie", "scores": [92, 90, 88]},
{"name": "David", "scores": [60, 65, 70]},
]
# 计算平均分
averages = {s["name"]: sum(s["scores"]) / len(s["scores"]) for s in students}
print("平均分:", averages)
# 找出优秀学生(平均分 >= 85)
excellent = {name: avg for name, avg in averages.items() if avg >= 85}
print("优秀学生:", excellent)
# 所有成绩
all_scores = [score for s in students for score in s["scores"]]
print("所有成绩:", all_scores)
print(f"最高分: {max(all_scores)}, 最低分: {min(all_scores)}")
# 按成绩等级分类
grades = {
"A": [s["name"] for s in students if sum(s["scores"]) / len(s["scores"]) >= 90],
"B": [s["name"] for s in students if 80 <= sum(s["scores"]) / len(s["scores"]) < 90],
"C": [s["name"] for s in students if sum(s["scores"]) / len(s["scores"]) < 80],
}
print("等级分布:", grades)小结
| 类型 | 语法 | 结果 |
|---|---|---|
| 列表推导式 | [x for x in iterable] |
列表 |
| 字典推导式 | {k: v for k, v in iterable} |
字典 |
| 集合推导式 | {x for x in iterable} |
集合 |
| 生成器表达式 | (x for x in iterable) |
生成器 |
- 推导式比等价的循环更快、更简洁
- 生成器表达式适合处理大数据,惰性求值节省内存
- 过于复杂的逻辑不要使用推导式,保持代码可读性
- 嵌套推导式最多两层,否则使用循环更清晰
练习
- 使用列表推导式生成 1-100 中能被 7 整除但不能被 3 整除的数。
- 有一个字符串列表
words = ["apple", "banana", "cherry", "date"],使用字典推导式创建{单词: 长度}的字典。 - 使用集合推导式找出两个字符串
a = "hello"和b = "world"中共同出现的字母。 - 使用生成器表达式计算 1 到 100 万所有能被 3 或 5 整除的数的和。
- 将以下嵌套循环改写成推导式:
result = [] for x in range(5): for y in range(5): if x + y > 4: result.append((x, y))