第六章:推导式与生成器表达式
本章目标
完成本章学习后,你将能够:
- 熟练使用列表推导式、字典推导式、集合推导式
- 理解生成器表达式的原理和用法
- 掌握嵌套推导式
- 理解推导式与循环的性能差异
- 在合适的场景选择最佳实现方式
列表推导式
基本语法
# 传统写法 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]'
带条件的列表推导式
# 只保留偶数 evens = [x for x in range(20) if x % 2 == 0] print(evens) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] # 处理符合条件的元素 double_evens = [x * 2 for x in range(20) if x % 2 == 0] print(double_evens) # [0, 4, 8, 12, 16, 20, 24, 28, 32, 36] # if-else表达式 labels = ["even" if x % 2 == 0 else "odd" for x in range(10)] print(labels) # ['even', 'odd', 'even', 'odd', ...]
嵌套列表推导式
# 矩阵转置 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]] # 扁平化矩阵 flattened = [x for row in matrix for x in row] print(flattened) # [1, 2, 3, 4, 5, 6, 7, 8, 9] # 组合两个列表 combinations = [(x, y) for x in [1, 2, 3] for y in [3, 1, 4] if x != y] print(combinations) # [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), ...]
字典推导式
基本语法
# 创建平方数字典 squares = {x: x ** 2 for x in range(10)} print(squares) # {0: 0, 1: 1, 2: 4, 3: 9, ...} # 从两个列表创建字典 keys = ['a', 'b', 'c'] values = [1, 2, 3] d = {k: v for k, v in zip(keys, values)} print(d) # {'a': 1, 'b': 2, 'c': 3}
字典转换
# 交换键值 original = {'a': 1, 'b': 2, 'c': 3} swapped = {v: k for k, v in original.items()} print(swapped) # {1: 'a', 2: 'b', 3: 'c'} # 过滤字典 scores = {'Alice': 85, 'Bob': 92, 'Charlie': 78, 'David': 95} passed = {k: v for k, v in scores.items() if v >= 80} print(passed) # {'Alice': 85, 'Bob': 92, 'David': 95} # 转换值 prices = {'apple': 1.5, 'banana': 0.8, 'cherry': 2.5} prices_yen = {k: v * 150 for k, v in prices.items()} # 转换为日元
集合推导式
# 基本集合推导式 squares = {x ** 2 for x in range(20)} print(squares) # {0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, ...} # 注意:自动去重,因为集合不允许重复 # 带条件的集合推导式 even_squares = {x ** 2 for x in range(20) if x % 2 == 0} print(even_squares) # 从字符串创建集合 unique_chars = {c.lower() for c in "Hello World!" if c.isalpha()} print(unique_chars) # {'h', 'e', 'l', 'o', 'w', 'r', 'd'}
生成器表达式
什么是生成器
生成器是惰性求值的迭代器,只在需要时生成值,节省内存。
# 列表推导式 - 立即计算,占用内存 squares_list = [x ** 2 for x in range(1000000)] # 占用大量内存 # 生成器表达式 - 惰性计算,节省内存 squares_gen = (x ** 2 for x in range(1000000)) # 几乎不占用内存 print(squares_gen) # <generator object <genexpr> at 0x...> # 逐个获取值 print(next(squares_gen)) # 0 print(next(squares_gen)) # 1 print(next(squares_gen)) # 4
生成器的使用
# 遍历生成器 gen = (x ** 2 for x in range(10)) for value in gen: print(value) # 转换为列表(会消耗生成器) gen = (x ** 2 for x in range(10)) print(list(gen)) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] # print(list(gen)) # [],生成器已耗尽 # 生成器只能遍历一次 gen = (x for x in range(5)) print(sum(gen)) # 10 print(sum(gen)) # 0,生成器已耗尽
生成器的优势
import sys # 内存占用对比 list_comp = [x * 2 for x in range(10000)] gen_expr = (x * 2 for x in range(10000)) print(f"列表推导式: {sys.getsizeof(list_comp)} bytes") print(f"生成器表达式: {sys.getsizeof(gen_expr)} bytes") # 处理大数据集 # 假设我们有一个大文件 def process_lines(filename): # 生成器逐行读取,不占用大量内存 return (line.strip().upper() for line in open(filename)) # 查找包含特定词的行 def find_lines(filename, word): return (line for line in open(filename) if word in line)
推导式高级技巧
复杂条件
# 多条件筛选 numbers = range(100) result = [x for x in numbers if x % 2 == 0 if x % 3 == 0] # 等效于:if x % 2 == 0 and x % 3 == 0 print(result) # [0, 6, 12, 18, ...] # 嵌套if-else grades = [85, 92, 78, 65, 55, 95] categories = [ "优秀" if g >= 90 else "良好" if g >= 80 else "中等" if g >= 70 else "及格" if g >= 60 else "不及格" for g in grades ]
与函数结合
# 在推导式中调用函数 def process(x): return x ** 2 + 1 results = [process(x) for x in range(10)] # 嵌套函数调用 words = ["hello", "WORLD", "PyThOn"] normalized = [word.strip().lower().capitalize() for word in words] print(normalized) # ['Hello', 'World', 'Python']
性能比较
import timeit # 性能测试 list_time = timeit.timeit('[x**2 for x in range(1000)]', number=1000) loop_time = timeit.timeit(''' result = [] for x in range(1000): result.append(x**2) ''', number=1000) map_time = timeit.timeit('list(map(lambda x: x**2, range(1000)))', number=1000) print(f"列表推导式: {list_time:.4f}s") print(f"for循环: {loop_time:.4f}s") print(f"map函数: {map_time:.4f}s")
通常:列表推导式 ≈ map > for循环(列表推导式往往更快且更易读)
何时使用推导式
使用推导式的情况
- 简单的转换和过滤
- 代码简洁且易读
- 结果需要全部存储在内存中(列表)或惰性计算(生成器)
# 好的使用场景 squares = [x**2 for x in range(100)] evens = [x for x in items if x % 2 == 0] char_freq = {c: text.count(c) for c in set(text)}
避免使用推导式的情况
- 逻辑过于复杂
- 需要副作用(如打印、修改外部变量)
- 需要多行注释说明
# 避免:过于复杂的推导式 result = [f(x) if x > 0 else g(x) if x < 0 else h(x) for x in items if x not in excluded] # 更好:使用普通循环 result = [] for x in items: if x in excluded: continue if x > 0: result.append(f(x)) elif x < 0: result.append(g(x)) else: result.append(h(x))
本章练习
1. 列表推导式练习:使用一行代码生成1到100中能被3或5整除的数的平方 2. 字典转换:给定一个字符串列表,创建字典{单词: 长度} 3. 集合去重:给定嵌套列表1,2,2], [3,3,4], [1,2,获取所有唯一元素 4. 生成器实战:创建一个生成器产生斐波那契数列 5. 矩阵操作:使用嵌套推导式实现矩阵乘法
本章小结
本章我们学习了:
- 列表、字典、集合推导式的语法
- 嵌套推导式的使用
- 生成器表达式的惰性求值特性
- 推导式的性能优势
- 何时使用推导式,何时使用普通循环
列表推导式是Python的标志性特性,简洁而高效。但记住:可读性第一,不要为了使用推导式而牺牲代码清晰度。
下一章:第七章:字符串深度解析