python 每天一个小技巧—Day 5:列表/字典/集合推导式

今天我们学习 Day 5:列表/字典/集合推导式。这是 Python 中最优雅和强劲的特性之一,让你用一行代码创建复杂的数据结构。


深度解析:推导式(Comprehensions)

1. 列表推导式(List Comprehensions)

传统方法:

numbers = [1, 2, 3, 4, 5]
squares = []
for num in numbers:
    squares.append(num ** 2)
print(squares)  # 输出: [1, 4, 9, 16, 25]

列表推导式:

numbers = [1, 2, 3, 4, 5]
squares = [num ** 2 for num in numbers]
print(squares)  # 输出: [1, 4, 9, 16, 25]

2. 带条件的列表推导式

只处理偶数:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_squares = [num ** 2 for num in numbers if num % 2 == 0]
print(even_squares)  # 输出: [4, 16, 36, 64, 100]

使用条件表达式:

numbers = [1, 2, 3, 4, 5]
result = ["偶数" if num % 2 == 0 else "奇数" for num in numbers]
print(result)  # 输出: ['奇数', '偶数', '奇数', '偶数', '奇数']

3. 字典推导式(Dictionary Comprehensions)

传统方法:

numbers = [1, 2, 3, 4, 5]
square_dict = {}
for num in numbers:
    square_dict[num] = num ** 2
print(square_dict)  # 输出: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

字典推导式

numbers = [1, 2, 3, 4, 5]
square_dict = {num: num ** 2 for num in numbers}
print(square_dict)  # 输出: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

4. 集合推导式(Set Comprehensions)

传统方法:

numbers = [1, 2, 2, 3, 3, 4, 5, 5]
unique_squares = set()
for num in numbers:
    unique_squares.add(num ** 2)
print(unique_squares)  # 输出: {1, 4, 9, 16, 25}

集合推导式:

numbers = [1, 2, 2, 3, 3, 4, 5, 5]
unique_squares = {num ** 2 for num in numbers}
print(unique_squares)  # 输出: {1, 4, 9, 16, 25}

5. 嵌套推导式

二维列表展开:

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [num for row in matrix for num in row]
print(flattened)  # 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9]

创建二维矩阵:

# 创建 3x3 矩阵
matrix = [[i * 3 + j + 1 for j in range(3)] for i in range(3)]
print(matrix)  # 输出: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

6. 实际应用场景

场景1:数据清洗和转换

# 从字符串中提取数字并转换为整数
data = ["价格: 100元", "重量: 2.5kg", "数量: 3个"]
numbers = [float(''.join(filter(str.isdigit, s))) for s in data]
print(numbers)  # 输出: [100.0, 2.5, 3.0]

场景2:字典键值转换

# 键值交换(如果值唯一)
original = {'a': 1, 'b': 2, 'c': 3}
reversed_dict = {v: k for k, v in original.items()}
print(reversed_dict)  # 输出: {1: 'a', 2: 'b', 3: 'c'}

# 键值转换(键转为大写,值加倍)
original = {'apple': 2, 'banana': 3, 'cherry': 1}
transformed = {k.upper(): v * 2 for k, v in original.items()}
print(transformed)  # 输出: {'APPLE': 4, 'BANANA': 6, 'CHERRY': 2}

场景3:数据筛选和聚合

# 从学生数据中提取及格学生的姓名和分数
students = [
    {'name': 'Alice', 'score': 85},
    {'name': 'Bob', 'score': 45},
    {'name': 'Charlie', 'score': 92},
    {'name': 'David', 'score': 58}
]

passing_students = {s['name']: s['score'] for s in students if s['score'] >= 60}
print(passing_students)  # 输出: {'Alice': 85, 'Charlie': 92}

7. 高级技巧和最佳实践

技巧1:使用 walrus 操作符(Python 3.8+)

data = ["apple123", "banana", "cherry456", "date"]
# 只有在包含数字时才处理
processed = [s.upper() for s in data if any(c.isdigit() for c in s)]
print(processed)  # 输出: ['APPLE123', 'CHERRY456']

技巧2:多个条件组合

numbers = range(1, 21)
# 能被2或3整除,但不能被5整除的数
result = [n for n in numbers if (n % 2 == 0 or n % 3 == 0) and n % 5 != 0]
print(result)  # 输出: [2, 3, 4, 6, 8, 9, 12, 14, 16, 18]

技巧3:处理嵌套数据

# 从嵌套结构中提取数据
data = [
    {'items': ['apple', 'banana', 'cherry']},
    {'items': ['date', 'elderberry']},
    {'items': ['fig', 'grape']}
]

all_items = [item for entry in data for item in entry['items']]
print(all_items)  # 输出: ['apple', 'banana', 'cherry', 'date', 'elderberry', 'fig', 'grape']

8. 性能思考

推导式一般比等效的循环更快,由于:

  • 在C层优化执行
  • 避免多次方法调用(如 append())
  • 更少的内存分配操作

但要注意:过于复杂的推导式会影响可读性!

9. 实际项目案例

案例1:配置文件处理

def process_config(config_lines):
    """处理配置文件行,提取键值对"""
    return {
        line.split('=')[0].strip(): line.split('=')[1].strip()
        for line in config_lines
        if '=' in line and not line.strip().startswith('#')
    }

# 模拟配置文件
config_lines = [
    "# 数据库配置",
    "host = localhost",
    "port = 5432",
    "user = admin",
    "# 应用配置",
    "debug = True"
]

config = process_config(config_lines)
print(config)

案例2:数据分析预处理

def clean_data(data_points):
    """清洗数据:去除异常值,标准化格式"""
    return [
        {
            'timestamp': point['ts'],
            'value': float(point['value']),
            'status': '正常' if 0 <= float(point['value']) <= 100 else '异常'
        }
        for point in data_points
        if point['value'].replace('.', '', 1).isdigit()
    ]

# 测试数据
raw_data = [
    {'ts': '2023-01-01', 'value': '45.2'},
    {'ts': '2023-01-02', 'value': '102.5'},  # 异常值
    {'ts': '2023-01-03', 'value': 'invalid'},  # 无效数据
    {'ts': '2023-01-04', 'value': '78.9'}
]

cleaned = clean_data(raw_data)
for point in cleaned:
    print(point)

今日练习

练习1:基础应用

words = ['hello', 'world', 'python', 'programming']
# 使用列表推导式:创建每个单词的大写版本列表
# 你的代码 here

# 使用字典推导式:创建 {单词: 长度} 的字典
# 你的代码 here

练习2:条件筛选

numbers = [23, 45, 12, 67, 89, 34, 56, 78, 90]
# 找出所有大于50的偶数,并计算它们的平方
# 你的代码 here

练习3:实战应用

def process_student_grades(students):
    """
    处理学生成绩数据,返回及格学生的新字典
    格式: {学号: {'name': 姓名, 'grade': 等级}}
    等级规则: >=90 A, >=80 B, >=70 C, >=60 D, <60 F
    只保留及格(D及以上)的学生
    """
    # 你的代码 here
    pass

# 测试数据
student_data = {
    '001': {'name': 'Alice', 'score': 85},
    '002': {'name': 'Bob', 'score': 45},
    '003': {'name': 'Charlie', 'score': 92},
    '004': {'name': 'David', 'score': 78}
}

print(process_student_grades(student_data))

练习答案:

# 练习1答案:
upper_words = [word.upper() for word in words]
word_lengths = {word: len(word) for word in words}

# 练习2答案:
result = [n**2 for n in numbers if n > 50 and n % 2 == 0]

# 练习3答案:
def process_student_grades(students):
    def get_grade(score):
        if score >= 90: return 'A'
        elif score >= 80: return 'B'
        elif score >= 70: return 'C'
        elif score >= 60: return 'D'
        else: return 'F'
    
    return {
        sid: {'name': info['name'], 'grade': get_grade(info['score'])}
        for sid, info in students.items()
        if info['score'] >= 60
    }

今日总结

  • 列表推导式: [表达式 for 项目 in 可迭代对象 if 条件]
  • 字典推导式: {键: 值 for 项目 in 可迭代对象 if 条件}
  • 集合推导式: {表达式 for 项目 in 可迭代对象 if 条件}
  • 优点: 简洁、高效、Pythonic
  • 注意事项: 保持可读性,复杂逻辑还是用传统循环

记住:推导式让代码更优雅,但不要为了简洁而牺牲可读性!

明天我们将学习两个实用的内置函数:any() 和 all() 的使用技巧。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
美味小熊脆香米的头像 - 宋马
评论 共3条

请登录后发表评论