Lambda 和推导式
这三个特性有一个共同点:它们让你用一个可读的表达式表达原本需要几行代码才能表达的想法。用得好,可以使代码更简短、更清晰;用得不好,则会让代码难以阅读。本章涵盖了何时使用每一个,以及何时不要使用。
Lambda 函数
lambda 是一个无名的、只有一个表达式的函数。你用 lambda 关键字创建它。它真正的用处在于,你可以在需要它的地方就地写出来,而无需先定义一个具名函数。这正是它与 sorted() 一起使用时的实用之处。
double = lambda x: x * 2
double(5) # 10这等价于:
def double(x):
return x * 2在大多数情况下,使用 def。Lambda 有一个真正的优势:你可以在需要它们的地方内联编写它们,而无需命名。这正是它们在 sorted()、map() 和 filter() 中有用的原因:
players = [("小明", 87), ("小红", 74), ("小华", 92)]
sorted(players, key=lambda p: p[1]) # 按分数排序(升序)
sorted(players, key=lambda p: p[1], reverse=True) # 按分数排序(降序)如果没有 lambda,你将不得不为 key= 参数定义一个具名函数。Lambda 让意图保持在局部并清晰可见。
Lambda 可以接受多个参数:
add = lambda a, b: a + b
add(3, 4) # 7何时使用 lambda: 仅当它是一个用于单一场景的简单表达式时。如果它变得复杂,或者你需要重用它,请编写一个正规的 def。一个跨越多个运算符或需要条件判断的 lambda 通常是切换到 def 的信号。
列表推导式
Python 中最常见的转换:取一个序列,对每个元素做一些操作,得到一个新列表。列表推导式可以在一行可读的代码中完成此操作:[expression for item in iterable]。你也可以使用 if 添加过滤条件。
冗长的写法:
numbers = [1, 2, 3, 4, 5]
squares = []
for n in numbers:
squares.append(n ** 2)列表推导式写法:
squares = [n ** 2 for n in numbers]结构始终相同:[expression for item in iterable]。
scores = [87, 42, 96, 55, 71]
scaled = [s * 1.1 for s in scores] # 应用 10% 的加成
as_grades = [f"{s}/100" for s in scores] # 格式化每一项使用条件进行过滤
添加一个 if 子句以仅包括通过测试的项目。结果是一个新列表,其中只包含条件为 True 的项。
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
evens = [n for n in numbers if n % 2 == 0] # [2, 4, 6, 8]
odds = [n for n in numbers if n % 2 != 0] # [1, 3, 5, 7]scores = [87, 42, 96, 55, 71, 38]
passing = [s for s in scores if s >= 60] # [87, 96, 71]
failing = [s for s in scores if s < 60] # [42, 55, 38]嵌套推导式
你可以嵌套推导式,将列表的列表展平为单个列表。从左到右阅读:对于每一行,对于该行中的每一项,包含该项。
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [item for row in matrix for item in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]从左到右阅读:对于 matrix 中的每一行,对于行中的每一项,包含 item。
嵌套推导式很容易令人混淆。如果需要片刻才能解析,请显式地写出循环。
字典推导式
字典推导式使用一个表达式构建字典,理念与列表推导式相同:{key: value for item in iterable}。像列表推导式一样,使用 if 添加过滤条件。
names = ["小明", "小红", "小华"]
scores = [87, 74, 92]
score_map = {name: score for name, score in zip(names, scores)}
# {"小明": 87, "小红": 74, "小华": 92}带过滤器:
passing = {name: score for name, score in score_map.items() if score >= 80}
# {"小明": 87, "小华": 92}words = ["apple", "banana", "cherry"]
word_lens = {word: len(word) for word in words}
# {"apple": 5, "banana": 6, "cherry": 6}集合推导式
集合推导式使用一个表达式构建集合,使用花括号且没有冒号。由于结果是一个集合,重复项会被自动删除。
words = ["apple", "banana", "cherry", "apple"]
unique = {w.lower() for w in words} # {"apple", "banana", "cherry"}当你需要唯一值且不关心顺序时,使用集合推导式。
生成器表达式
生成器看起来像列表推导式,但用圆括号而不是方括号。关键区别在于:列表推导式一次性在内存中构建整个列表,而生成器一次产生一个值,只在需要时产生。对于大型序列,这会使用少得多的内存。
squares_gen = (n ** 2 for n in range(1000000))total = sum(n ** 2 for n in range(1000000)) # sum() 消费生成器当直接将生成器传递给 sum()、max()、min() 或 any() 等函数时,你可以省去额外的括号:
total = sum(n ** 2 for n in range(1000)) # 一对括号,而不是两对对于大多数日常代码,列表推导式就足够了。当你处理大型数据集或流式数据,把所有数据保存在内存中会造成浪费时,请使用生成器。
zip()
zip() 将两个或多个序列中的项配对在一起,以便你可以并行地遍历它们。它在最短的序列处停止。当两个列表相互对应时,它是避免手动管理索引的简洁方式。
names = ["小明", "小红", "小华"]
scores = [87, 74, 92]
for name, score in zip(names, scores):
print(f"{name}: {score}")
# 小明: 87
# 小红: 74
# 小华: 92zip() 在最短的序列处停止。如果你的序列可能长度不同,请使用 itertools.zip_longest() 并指定填充值。
要从配对的列表转换回两个单独的列表,使用 zip(*pairs):
pairs = [("小明", 87), ("小红", 74), ("小华", 92)]
names, scores = zip(*pairs)
# names = ("小明", "小红", "小华")
# scores = (87, 74, 92)* 在这里做什么?
*pairs 将列表解包为单独的参数:zip(*pairs) 变成 zip(("小明", 87), ("小红", 74), ("小华", 92))。* 运算符在函数章节中有介绍。
zip() 也是并行迭代多个序列而无需手动管理索引的简洁方法:
before = [10, 20, 30]
after = [15, 18, 35]
for b, a in zip(before, after):
change = a - b
print(f"{b} -> {a} ({'+' if change >= 0 else ''}{change})")map() 和 filter()
map() 和 filter() 是较老的函数式风格工具,它们做的事情和推导式一样。你会在旧代码中看到它们,所以了解它们的含义是值得的。新代码中优先使用推导式;对于大多数 Python 开发者来说,推导式更具可读性。
numbers = [1, 2, 3, 4, 5]
list(map(lambda x: x ** 2, numbers)) # [1, 4, 9, 16, 25]
list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4]优先使用推导式;它们对大多数 Python 开发者来说更具可读性。当你已经有一个具名函数时,使用 map():
strings = ["1", "2", "3"]
numbers = list(map(int, strings)) # [1, 2, 3] (在这里比推导式更简洁)实战应用
将玩家列表过滤为及格分数,使用 sorted 和 lambda 按分数排序,然后使用枚举位置打印:
players = [
{"name": "小明", "score": 87},
{"name": "小红", "score": 42},
{"name": "小华", "score": 96},
{"name": "小刚", "score": 55},
]
passing = [p for p in players if p["score"] >= 60]
ranked = sorted(passing, key=lambda p: p["score"], reverse=True)
score_map = {p["name"]: p["score"] for p in ranked}
for i, (name, score) in enumerate(score_map.items(), start=1):
print(f"{i}. {name}: {score}")
