推导式(Comprehensions)是Python中最强大的语法特性之一,它让我们能用简洁优雅的方式来创建和转换数据集合。它们不仅仅是语法糖,更是提高代码可读性和性能的利器。
“简单是可靠的先决条件” — Edsger W. Dijkstra
[表达式 for 变量 in 可迭代对象 if 条件]
列表推导式就像一个快速食品加工机:你放入原材料(可迭代对象),设置筛选条件(if语句),然后每个通过筛选的食材都经过同样的加工(表达式),最终得到一盘精心处理过的成品。
# 传统方式:计算1到10的平方
squares = []
for x in range(1, 11):
squares.append(x ** 2)
print(squares) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 列表推导式方式
squares = [x ** 2 for x in range(1, 11)]
print(squares) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 获取1到20中所有偶数的平方
even_squares = [x ** 2 for x in range(1, 21) if x % 2 == 0]
print(even_squares) # [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]
# 生成坐标对
coordinates = [(x, y) for x in range(1, 4) for y in range(1, 3)]
print(coordinates) # [(1, 1), (1, 2), (2, 1), (2, 2), (3, 1), (3, 2)]
# 等价的传统写法
coordinates = []
for x in range(1, 4):
for y in range(1, 3):
coordinates.append((x, y))
# 判断数字是奇数还是偶数
numbers = [1, 2, 3, 4, 5]
result = ["偶数" if x % 2 == 0 else "奇数" for x in numbers]
print(result) # ['奇数', '偶数', '奇数', '偶数', '奇数']
# 数据清洗:过滤并转换数据
data = ["10", "error", "20", "N/A", "30", ""]
clean_data = [int(x) for x in data if x.isdigit()]
print(clean_data) # [10, 20, 30]
# 文件处理:提取特定行
with open('example.txt', 'r') as file:
non_empty_lines = [line.strip() for line in file if line.strip()]
# 扁平化嵌套列表
nested_list = [[1, 2, 3], [4, 5], [6, 7, 8]]
flat_list = [item for sublist in nested_list for item in sublist]
print(flat_list) # [1, 2, 3, 4, 5, 6, 7, 8]
{键表达式: 值表达式 for 变量 in 可迭代对象 if 条件}
字典推导式就像一个自动标签机:你给它一堆物品(可迭代对象),它按照你的规则给每个物品贴上相应的标签(键),并可能修改物品本身(值),最终生成一个有序的标签-物品对应表。
# 传统方式:创建数字及其平方的映射
square_dict = {}
for x in range(1, 6):
square_dict[x] = x ** 2
print(square_dict) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# 字典推导式方式
square_dict = {x: x ** 2 for x in range(1, 6)}
print(square_dict) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# 价格美元转人民币 (假设汇率 1:7)
prices = {'apple': 0.5, 'banana': 0.3, 'orange': 0.6}
prices_rmb = {fruit: price * 7 for fruit, price in prices.items()}
print(prices_rmb) # {'apple': 3.5, 'banana': 2.1, 'orange': 4.2}
# 筛选价格低于0.5美元的水果
cheap_fruits = {fruit: price for fruit, price in prices.items() if price < 0.5}
print(cheap_fruits) # {'banana': 0.3}
# 合并两个列表成字典
fruits = ['apple', 'banana', 'orange']
counts = [5, 3, 2]
fruit_inventory = {fruit: count for fruit, count in zip(fruits, counts)}
print(fruit_inventory) # {'apple': 5, 'banana': 3, 'orange': 2}
# 交换键值对
original = {'a': 1, 'b': 2, 'c': 3}
swapped = {v: k for k, v in original.items()}
print(swapped) # {1: 'a', 2: 'b', 3: 'c'}
# 统计每个单词的字母出现频率
word = "hello"
letter_count = {letter: word.count(letter) for letter in set(word)}
print(letter_count) # {'h': 1, 'e': 1, 'l': 2, 'o': 1}
# 处理嵌套字典
student_scores = {
'Alice': {'math': 90, 'science': 85, 'english': 95},
'Bob': {'math': 80, 'science': 90, 'english': 85}
}
# 获取每个学生的平均分
average_scores = {
name: sum(scores.values()) / len(scores)
for name, scores in student_scores.items()
}
print(average_scores) # {'Alice': 90.0, 'Bob': 85.0}
{表达式 for 变量 in 可迭代对象 if 条件}
# 获取列表中所有偶数,并去重
numbers = [1, 2, 2, 3, 4, 4, 5, 6, 6]
even_set = {x for x in numbers if x % 2 == 0}
print(even_set) # {2, 4, 6}
# 提取文本中的所有独特字符
text = "hello world"
unique_chars = {char for char in text if char.isalpha()}
print(unique_chars) # {'e', 'd', 'h', 'l', 'o', 'r', 'w'}
# 获取两个列表的交集
list1 = [1, 2, 3, 4, 5]
list2 = [3, 4, 5, 6, 7]
intersection = {x for x in list1 if x in list2}
print(intersection) # {3, 4, 5}
生成器表达式的语法与列表推导式相似,但使用圆括号而非方括号:
(表达式 for 变量 in 可迭代对象 if 条件)
生成器表达式就像按需配送服务:不会一次性生产所有商品,而是在客户需要时才生产下一个商品,从而节省存储空间。
import sys
# 列表推导式:立即计算所有值
squares_list = [x ** 2 for x in range(1000)]
print(f"列表占用内存: {sys.getsizeof(squares_list)} 字节")
# 生成器表达式:按需计算值
squares_gen = (x ** 2 for x in range(1000))
print(f"生成器占用内存: {sys.getsizeof(squares_gen)} 字节")
# 处理大文件时节省内存
with open('large_file.txt', 'r') as file:
line_lengths = (len(line.strip()) for line in file)
total_length = sum(line_lengths) # 只遍历文件一次
# 链式操作
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
sum_of_even_squares = sum(x ** 2 for x in numbers if x % 2 == 0)
print(sum_of_even_squares) # 220
# 创建一个3x4的矩阵
matrix = [[i * 4 + j + 1 for j in range(4)] for i in range(3)]
print(matrix)
# [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
# 矩阵转置
transposed = [[row[i] for row in matrix] for i in range(4)]
print(transposed)
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
# 创建嵌套字典
students = ['Alice', 'Bob', 'Charlie']
subjects = ['Math', 'Science', 'English']
# 为每个学生创建一个字典,包含各科的初始分数
grades = {
student: {subject: 0 for subject in subjects}
for student in students
}
print(grades)
# {'Alice': {'Math': 0, 'Science': 0, 'English': 0},
# 'Bob': {'Math': 0, 'Science': 0, 'English': 0},
# 'Charlie': {'Math': 0, 'Science': 0, 'English': 0}}
import time
# 测量列表创建性能
def test_performance():
# 使用普通循环
start = time.time()
result = []
for i in range(1000000):
if i % 2 == 0:
result.append(i ** 2)
loop_time = time.time() - start
# 使用列表推导式
start = time.time()
result = [i ** 2 for i in range(1000000) if i % 2 == 0]
comp_time = time.time() - start
print(f"循环时间: {loop_time:.4f}秒")
print(f"推导式时间: {comp_time:.4f}秒")
print(f"速度提升: {loop_time/comp_time:.2f}倍")
test_performance()
# 不适合复杂逻辑
def complex_operation(x):
# 假设这是一个复杂的、多步骤的操作
result = x
for _ in range(3):
if result % 2 == 0:
result = result // 2
else:
result = 3 * result + 1
return result
# 这种情况下,传统循环更清晰
results = []
for x in range(1, 11):
results.append(complex_operation(x))
# 虽然可以写成推导式,但可读性降低
results = [complex_operation(x) for x in range(1, 11)]
✅ 适合用推导式的场景:
❌ 不适合用推导式的场景:
# 过长难读
result = [x for x in range(100) if x % 2 == 0 if x % 3 == 0 if x % 5 == 0]
# 更清晰的版本
result = [
x
for x in range(100)
if x % 2 == 0
if x % 3 == 0
if x % 5 == 0
]
# 或者使用一个条件
result = [x for x in range(100) if x % 30 == 0]
问题1:解释以下代码的输出
squares = [x**2 for x in range(5)]
doubles = [2*x for x in squares if x % 2 == 0]
print(doubles)
问题2:实现一个函数,使用字典推导式将一个字符串中的字符计数
def char_count(text):
"""计算字符串中每个字符的出现次数"""
return {char: text.count(char) for char in set(text)}
问题3:重构以下代码为推导式
result = []
for i in range(10):
if i % 2 == 0:
for j in range(i):
if j % 2 == 1:
result.append((i, j))
解答3
result = [(i, j) for i in range(10) if i % 2 == 0 for j in range(i) if j % 2 == 1]
文本处理:编写一个推导式,从一个句子中提取所有长度大于3的单词,并转换为大写
数据分析:给定一个学生成绩字典,使用推导式找出所有平均分超过90分的学生
矩阵操作:使用嵌套推导式实现一个函数,可以将任意大小的矩阵旋转90度
def extract_and_capitalize(sentence):
"""从句子中提取所有长度大于3的单词,并转换为大写"""
return [word.upper() for word in sentence.split() if len(word) > 3]
# 测试
sentence = "Python is a powerful programming language that is widely used"
result = extract_and_capitalize(sentence)
print(result) # 输出: ['PYTHON', 'POWERFUL', 'PROGRAMMING', 'LANGUAGE', 'THAT', 'WIDELY', 'USED']
这个推导式由三部分组成:
word.upper()
- 转换操作for word in sentence.split()
- 迭代部分,将句子分割为单词if len(word) > 3
- 过滤条件,只保留长度大于3的单词def find_top_students(student_grades):
"""找出所有平均分超过90分的学生"""
return {
name: grades
for name, grades in student_grades.items()
if sum(grades) / len(grades) > 90
}
# 测试
student_grades = {
"Alice": [95, 92, 98, 88],
"Bob": [85, 82, 88, 75],
"Charlie": [92, 95, 96, 98],
"Diana": [88, 91, 87, 89]
}
top_students = find_top_students(student_grades)
print(top_students) # 输出: {'Alice': [95, 92, 98, 88], 'Charlie': [92, 95, 96, 98]}
这个字典推导式:
def rotate_matrix_90(matrix):
"""将矩阵顺时针旋转90度"""
# 使用嵌套推导式,按列从下到上获取元素
return [[matrix[i][j] for i in range(len(matrix)-1, -1, -1)] for j in range(len(matrix[0]))]
# 测试
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
rotated = rotate_matrix_90(matrix)
for row in rotated:
print(row)
# 输出:
# [7, 4, 1]
# [8, 5, 2]
# [9, 6, 3]
这个嵌套推导式的工作原理:
for j in range(len(matrix[0]))
遍历原矩阵的列for i in range(len(matrix)-1, -1, -1)
从下到上遍历原矩阵的行这实现了矩阵的顺时针旋转90度。如果要逆时针旋转,只需修改内层循环的方向或调整列的遍历顺序。
推导式是Python中非常强大的特性,可以让代码更简洁、更具可读性,同时保持高效率。上面的例子展示了列表推导式、字典推导式和嵌套推导式的不同用法。
推导式是Python中独特而强大的特性,掌握它们能够:
但记住,推导式不是万能的,最重要的原则是保持代码可读性。如果一个推导式变得过于复杂,考虑使用传统循环或函数拆分它。
“简单始终优于复杂” —— Python之禅