第 9 课 - 列表、字典与集合推导式
课程:Python 编程:从零基础到精通 模块:模块二:控制流与循环 难度:beginner 标签:comprehension, list-comprehension 上一课:For 循环与迭代器 下一课:函数定义与调用
学习目标
完成本课学习后,你将能够:
- 理解并熟练使用列表推导式,用简洁的一行代码创建列表。
- 掌握字典推导式的语法,快速从可迭代对象生成字典。
- 了解集合推导式,用于创建无重复元素的集合。
- 对比传统循环与推导式,理解推导式在代码简洁性与可读性上的平衡。
- 识别并避免初学者在使用推导式时的常见错误。
核心概念
想象一下,你有一堆原材料(比如数字1到10),想把它们加工成一组产品(比如它们的平方数)。用传统 for 循环,你需要"一步步走":准备好空列表 -> 逐个加工 -> 添加到列表里。推导式就像是一个高效的"自动化生产线",你只需在一条生产指令里描述好"原材料"和"加工规则",就能直接得到成品列表。
列表推导式
这是最常用的推导式。它的基本结构是:
[表达式 for 变量 in 可迭代对象 if 条件]
表达式:就是对每个元素进行的"加工规则"(如x**2)。for 变量 in 可迭代对象:定义了原材料从哪里来(如for x in range(10))。if 条件(可选):用于筛选,只有满足条件的元素才会被加工。
字典推导式
用于创建字典,结构类似列表推导式,但用花括号 {},且表达式是 键:值 对。
{键表达式: 值表达式 for 变量 in 可迭代对象 if 条件}
集合推导式
与列表推导式几乎一样,只是用花括号 {},并且结果会自动去除重复元素。
{表达式 for 变量 in 可迭代对象 if 条件}
代码示例
# ===== 1. 列表推导式 vs 传统循环 =====
# 任务:创建一个包含1到10所有平方数的列表
# 方法一:传统 for 循环
squares_loop = []
for x in range(1, 11): # 生成1到10
squares_loop.append(x ** 2)
print("循环法得到的平方数列表:", squares_loop)
# 方法二:列表推导式(一行搞定!)
squares_comp = [x ** 2 for x in range(1, 11)]
print("推导式得到的平方数列表:", squares_comp)
# 输出完全相同: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
print("-" * 30)
# ===== 2. 带条件的列表推导式 =====
# 任务:从1到20中,只挑选偶数,并计算它们的平方
even_squares = [x**2 for x in range(1, 21) if x % 2 == 0] # `if x % 2 == 0` 就是筛选条件
print("1-20中偶数的平方:", even_squares)
# 输出: [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]
print("-" * 30)
# ===== 3. 字典推导式 =====
# 任务:创建一个字典,键是数字1-5,值是它的立方
cubes_dict = {num: num**3 for num in range(1, 6)}
print("数字-立方字典:", cubes_dict)
# 输出: {1: 1, 2: 8, 3: 27, 4: 64, 5: 125}
# 任务:颠倒原有字典的键值对
original = {'a': 1, 'b': 2, 'c': 3}
reversed_dict = {value: key for key, value in original.items()}
print("颠倒后的字典:", reversed_dict)
# 输出: {1: 'a', 2: 'b', 3: 'c'}
print("-" * 30)
# ===== 4. 集合推导式 =====
# 任务:从一个包含重复元素的列表中,得到所有不重复元素的平方集合
numbers = [1, 2, 2, 3, 3, 3, 4, 4, 5]
unique_squares = {x**2 for x in numbers} # 自动去重
print("不重复元素的平方集合:", unique_squares)
# 输出: {16, 1, 4, 9, 25} (集合元素顺序不固定)
print("-" * 30)
# ===== 5. 复杂一点的例子:嵌套循环在推导式中 =====
# 任务:生成一个列表,包含所有两个元组(a, b)的和,其中a来自[1,2],b来自[10,20,30]
sums = [a + b for a in [1, 2] for b in [10, 20, 30]]
print("两层循环的推导式结果:", sums)
# 输出: [11, 21, 31, 12, 22, 32]
实践练习
练习 1 (入门)
使用列表推导式,生成一个包含数字 0 到 9 中所有奇数的列表。
预期输出: [1, 3, 5, 7, 9]
练习 2 (进阶)
有一个句子 "Hello Python World, Hello List Comprehension",请使用列表推导式,提取句子中所有长度大于 4 的单词,并将它们转换为大写后放入一个新列表。
提示:先用 .split() 方法将句子拆分为单词列表。
预期输出: ['HELLO', 'PYTHON', 'WORLD,', 'HELLO', 'COMPREHENSION']
练习 3 (挑战)
使用字典推导式,从下面两个列表创建一个字典,其中 keys 中的元素是键,values 中对应的元素是值。
keys = ['name', 'age', 'city']
values = ['Alice', 25, 'New York']
然后,再用一个字典推导式,将上一步得到的字典中所有字符串类型的值转换为小写(即 'New York' 变为 'new york')。
预期输出:
第一步字典: {'name': 'Alice', 'age': 25, 'city': 'New York'}
第二步字典: {'name': 'alice', 'age': 25, 'city': 'new york'}
常见错误
-
括号混淆:列表推导式用
[],字典和集合推导式用{}。写错会导致语法错误或生成错误的数据类型。# 错误示例 wrong_list = {x: x*2 for x in range(3)} # 这其实是字典推导式,得到 {0:0, 1:2, 2:4} wrong_dict = [key: value for key, value in {'a':1}.items()] # 语法错误 -
滥用推导式,导致可读性差:推导式虽简洁,但如果逻辑太复杂(如多层嵌套、多个条件),应该拆分成传统的
for循环,保持代码清晰。# 难以阅读的推导式(不推荐) messy = [x*y for x in range(5) if x>2 for y in [10,20] if y<15] # 改写为循环会更清晰 clear = [] for x in range(5): if x > 2: for y in [10, 20]: if y < 15: clear.append(x*y) -
忘记
for前面的"表达式":推导式的核心是表达式 for ...。如果只想简单地筛选元素而不变换,表达式就是变量本身。# 正确:筛选偶数 evens = [x for x in range(10) if x % 2 == 0] # 错误:缺少表达式 # wrong = [for x in range(10) if x % 2 == 0] -
变量作用域混淆:在推导式中定义的循环变量(如
for x in ...中的x)在推导式执行完毕后,依然会在当前作用域中存在(在Python 3中)。但这不是最佳实践,应避免在推导式外依赖这个变量。
小结
- 列表推导式
[x for x in iterable]是快速创建列表的"语法糖",比循环更简洁。 - 字典推导式
{k: v for ...}是生成字典的利器,尤其适合转换现有数据结构。 - 集合推导式
{x for ...}在创建不重复集合时非常方便。 - 推导式中可以包含
if条件语句,用于筛选元素。 - 使用推导式时,要在代码简洁性和可读性之间取得平衡。当逻辑复杂时,传统的
for循环可能是更好的选择。
掌握了推导式,你的Python代码会变得更加"Pythonic"(符合Python风格),编程效率也会大大提升!