目录

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. 使用列表推导式生成 1-100 中能被 7 整除但不能被 3 整除的数。
  2. 有一个字符串列表 words = ["apple", "banana", "cherry", "date"],使用字典推导式创建 {单词: 长度} 的字典。
  3. 使用集合推导式找出两个字符串 a = "hello"b = "world" 中共同出现的字母。
  4. 使用生成器表达式计算 1 到 100 万所有能被 3 或 5 整除的数的和。
  5. 将以下嵌套循环改写成推导式:
    result = []
    for x in range(5):
        for y in range(5):
            if x + y > 4:
                result.append((x, y))