第二十一篇:sorted 与匿名函数 lambda——灵活排序的利器
前言
**lambda(匿名函数)**和 sorted() 是 Python 中处理数据排序与转换的两大神器。本文全面讲解 lambda 语法、sorted() 的高级用法,以及两者配合实现复杂排序的技巧。
lambda 基础
基本语法
# 普通函数 vs lambda
def add(a, b):
return a + b
add_lambda = lambda a, b: a + b
print(add_lambda(3, 5)) # 8
# ⚠️ lambda 只能包含表达式(不能含语句)
square = lambda x: x ** 2
print(square(4)) # 16
lambda 作为一等公民
ops = {
"+": lambda a, b: a + b,
"-": lambda a, b: a - b,
"*": lambda a, b: a * b,
"/": lambda a, b: a / b if b != 0 else float('inf'),
}
result = ops["+"](10, 5)
print(result) # 15
operation = "*"
result = ops[operation](10, 5)
print(result) # 50
lambda 在 map/filter/reduce 中的应用
nums = [1, 2, 3, 4, 5]
# map — 平方
squares = list(map(lambda x: x ** 2, nums))
print(squares) # [1, 4, 9, 16, 25]
# filter — 偶数
evens = list(filter(lambda x: x % 2 == 0, nums))
print(evens) # [2, 4]
from functools import reduce
product = reduce(lambda a, b: a * b, nums)
print(product) # 120
lambda 的局限性
# ❌ Lambda 只支持单行表达式
bad_example = lambda: (x := 5; print(x); return x * 2) # SyntaxError!
# ✅ 多行逻辑应提取为具名函数
def complex_transform(x):
result = x * 2
result += 10
return max(result, 100)
# Lambda 适合简单的一行转换
double_plus_ten = lambda x: x * 2 + 10
print(double_plus_ten(5)) # 20
sorted() 函数详解
基本用法
nums = [5, 2, 8, 1, 9, 3]
# 正序排序
print(sorted(nums)) # [1, 2, 3, 5, 8, 9]
# 逆序排序
print(sorted(nums, reverse=True)) # [9, 8, 5, 3, 2, 1]
# 不修改原列表
print(nums) # [5, 2, 8, 1, 9, 3] — 不变
字符串排序
words = ["banana", "Apple", "cherry", "date"]
# 默认按字典序
print(sorted(words)) # ['Apple', 'banana', 'cherry', 'date']
# 忽略大小写
print(sorted(words, key=str.lower)) # ['apple', 'banana', 'cherry', 'date']
# 按长度排序
print(sorted(words, key=len)) # ['date', 'Apple', 'banana', 'cherry']
字典排序
students = [
{"name": "Alice", "score": 85},
{"name": "Bob", "score": 95},
{"name": "Carol", "score": 78},
]
# 按分数升序
print(sorted(students, key=lambda s: s["score"]))
# Carol(78), Alice(85), Bob(95)
# 按名字字母序
print(sorted(students, key=lambda s: s["name"].upper()))
# Alice, Bob, Carol
# 多级排序:先分数降序,再姓名升序
print(sorted(students, key=lambda s: (-s["score"], s["name"])))
自定义比较键
class Person:
def __init__(self, name, age, height):
self.name = name
self.age = age
self.height = height
def __repr__(self):
return f"Person({self.name}, {self.age}cm)"
persons = [
Person("张三", 25, 170),
Person("李四", 30, 180),
Person("王五", 22, 165),
]
# 按身高排序
print(sorted(persons, key=lambda p: p.height))
# [王五 (22, 165cm), 张三 (25, 170cm), 李四 (30, 180cm)]
# 按年龄/身高的复合键
print(sorted(persons, key=lambda p: (p.age, -p.height)))
sorted 与 list.sort() 对比
| 特性 | list.sort() |
sorted(iterable) |
|---|---|---|
| 返回值 | None(原地排序) |
新列表(原 iterable 不变) |
| 适用对象 | 仅列表 | 任何可迭代对象 |
| 内存效率 | 更高(就地操作) | 需要额外内存复制 |
| 链式调用 | ❌ 不能(返回 None) | ✅ 可以 |
nums = [3, 1, 2]
# sort() 原地排序,返回 None
result_sort = nums.sort()
print(result_sort) # None
print(nums) # [1, 2, 3]
# sorted() 返回新列表
result_sorted = sorted([3, 1, 2])
print(result_sorted) # [1, 2, 3]
print([3, 1, 2]) # [3, 1, 2] — 不变
高级排序技巧
稳定排序与多重键
records = [
("Alice", 95, 170),
("Bob", 95, 180),
("Carol", 85, 165),
("Dave", 85, 175),
]
# 按成绩排序(稳定排序:相同成绩保持原始顺序)
stable_sorted = sorted(records, key=lambda r: r[1], reverse=True)
for record in stable_sorted:
print(f"{record[0]}: 成绩={record[1]}, 身高={record[2]}")
# Alice(95), Bob(95), Dave(85), Carol(85) — 同分顺序不变
# 复合键:第一关键字成绩,第二关键字身高
compound_sorted = sorted(records, key=lambda r: (-r[1], -r[2]))
for record in compound_sorted:
print(f"{record[0]}: 成绩={record[1]}, 身高={record[2]}")
# Bob(95, 180), Alice(95, 170), Dave(85, 175), Carol(85, 165)
利用 operator.itemgetter 优化
from operator import itemgetter, attrgetter
students = [
{"name": "Alice", "score": 85},
{"name": "Bob", "score": 95},
{"name": "Carol", "score": 78},
]
# 用 itemgetter 替代 lambda(速度略快)
sorted_by_score = sorted(students, key=itemgetter("score"))
# 多级排序
multi_key = sorted(students, key=itemgetter("score", "name"))
# 对象属性排序
persons = [Person("A", 25, 170), Person("B", 30, 180)]
sorted_by_attr = sorted(persons, key=attrgetter("height"))
自定义排序规则
# 按数字的绝对值排序
numbers = [-5, -3, 0, 3, 5, -1, 1]
print(sorted(numbers, key=abs))
# [0, -1, 1, -3, 3, -5, 5]
# 按最后一个字母排序
words = ["hello", "world", "python", "java"]
print(sorted(words, key=lambda w: w[-1]))
# java, hello, world, python
# 按出现频率排序
text = "aabbbccccddddd"
from collections import Counter
freq = Counter(text)
# {'d': 5, 'c': 4, 'b': 3, 'a': 2}
by_freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
print(by_freq)
# [('d', 5), ('c', 4), ('b', 3), ('a', 2)]
自定义类排序
class Score:
def __init__(self, name, math, english, chinese):
self.name = name
self.math = math
self.english = english
self.chinese = chinese
@property
def total(self):
return self.math + self.english + self.chinese
def __repr__(self):
return f"{self.name} 总分={self.total}"
scores = [
Score("张三", 90, 85, 92),
Score("李四", 88, 92, 78),
Score("王五", 95, 80, 85),
]
# 按总分排序
total_sorted = sorted(scores, key=lambda s: s.total, reverse=True)
for score in total_sorted:
print(score)
lambda 常见陷阱
陷阱一:闭包延迟绑定
# ❌ 错误:所有函数都捕获相同的 i(最终值为 4)
funcs = []
for i in range(5):
funcs.append(lambda: i)
for f in funcs:
print(f())
# 全部输出 4!(不是预期的 0,1,2,3,4)
# ✅ 修复方案一:使用默认参数捕获当时的值
funcs = []
for i in range(5):
funcs.append(lambda x=i: x) # 默认参数在定义时求值
for f in funcs:
print(f())
# 0 1 2 3 4 — 正确!
# ✅ 修复方案二:生成器推导式自动捕获
funcs_gen = (lambda i=i: i for i in range(5))
for f in funcs_gen:
print(f())
陷阱二:Lambda 不支持赋值
# ❌ Lambda 内不能有语句
wrong = lambda: (x := 5; print(x)) # SyntaxError!
# ✅ 改用多行函数
def my_func():
x = 5
print(x)
return x
实战示例
学生成绩排名系统
class StudentDatabase:
def __init__(self, students_data):
self.students = [Student(*data) for data in students_data]
def get_top_students(self, n=3, subject="math"):
"""获取某科目最高分的 n 名学生"""
key_map = {
"math": lambda s: s.math,
"english": lambda s: s.english,
"chinese": lambda s: s.chinese,
"total": lambda s: s.total,
}
key_fn = key_map.get(subject, lambda s: s.total)
return sorted(self.students, key=key_fn, reverse=True)[:n]
def rank_all(self):
"""按总分排名并打印"""
ranked = sorted(self.students, key=lambda s: s.total, reverse=True)
for i, student in enumerate(ranked, 1):
print(f"Rank{i}. {student}")
class Student:
def __init__(self, name, math, english, chinese):
self.name = name
self.math = math
self.english = english
self.chinese = chinese
@property
def total(self):
return self.math + self.english + self.chinese
def __repr__(self):
return f"{self.name} 总分={self.total}"
db = StudentDatabase([
("张三", 90, 85, 92),
("李四", 88, 92, 78),
("王五", 95, 80, 85),
])
db.rank_all()
# Rank1. 王五 总分=260
# Rank2. 张三 总分=267
# Rank3. 李四 总分=258
日志解析与分组统计
import re
from collections import defaultdict
log_lines = [
"2024-01-15 INFO user_login alice",
"2024-01-15 ERROR db_connection failed",
"2024-01-16 INFO file_upload bob",
"2024-01-16 ERROR api_timeout server1",
]
# 解析日志并按类型分组
parsed = [re.match(r"(.*?) (\w+) (.*)", line).groups() for line in log_lines]
grouped = defaultdict(list)
for date, level, msg in parsed:
grouped[(date, level)].append(msg)
# 按日期和时间先后排序
sorted_entries = sorted(grouped.keys(), key=lambda x: (x[0], x[1]))
for key in sorted_entries:
print(f"{key}: {', '.join(grouped[key])}")
总结
本文涵盖了 sorted 与 lambda 的核心知识:
- lambda 语法 — 匿名单行函数,适合简单转换
- lambda 作为函数传递 — 配合 map/filter/reduce 使用
- sorted() 基础 — 正序/逆序、不修改原对象
- key 参数 — 自定义排序规则的核心机制
- 多级排序 — tuple/元组作为复合键
- operator 工具 — itemgetter/attrgetter 替代 lambda 加速
- 自定义类排序 — @property + key 组合
- 常见陷阱 — 闭包延迟绑定、不支持语句
- 性能对比 — C 层 key 函数比 Python 循环快
- 实战应用 — 学生排名系统、日志解析分组
下一篇文章将深入讲解返回函数与闭包。
本文是 Python 教程系列的第二十一篇。
