(五)把函数当作数据交给另一段处理
1. 函数对象与回调
第二章的 sorted(titles, key=len) 已经把一个函数交给另一个函数。现在换成自己的规则:
# 不带括号时得到函数对象;带括号时才执行并取得返回值。
def sort_key(record):
title, minutes = record
return -minutes, title
records = [("B", 35), ("A", 35), ("C", 20)]
rule = sort_key
print(sorted(records, key=rule))
print(rule(("B", 35)))得到 [('A', 35), ('B', 35), ('C', 20)],以及 (-35, 'B')。排序为每项求出键,先比较负分钟数,再比较书名。key=sort_key 把规则交过去;若写成 key=sort_key(...),交过去的就会是这次调用得到的元组。
把函数传进去,等处理过程中再调用,常叫回调。下面的 predicate 接收一条记录,返回是否保留:
# 筛选流程固定,把“满足什么条件”交给传进来的函数决定。
def select_records(records, predicate):
selected = []
for record in records:
if predicate(record):
selected.append(record)
return selected
def is_met(record):
return record[1] >= 30
print(select_records([("Python", 20), ("NumPy", 35)], is_met))输出 [('NumPy', 35)]。这里传入的是函数对象,不是名称的字符串。后面事件处理、网页路由与模型训练中的回调,都会出现“现在交入动作,稍后由另一段代码调用”的安排。
2. 用 lambda 写一个返回表达式
# lambda 参数: 表达式,调用时把表达式的值作为结果返回。
records = [("B", 35), ("A", 35), ("C", 20)]
print(sorted(records, key=lambda record: (-record[1], record[0])))
# filter 按谓词保留元素,返回的迭代器由 list 消耗后收集。
selected = filter(lambda record: record[1] >= 30, records)
print(list(selected))两次分别得到排序结果与 [('B', 35), ('A', 35)]。这段 lambda 与前面的 sort_key 返回同样的键;它适合在当前调用附近写清一个短规则。需要多步判断、循环或解释规则时,用有名称的 def 更容易继续阅读。
lambda 也是函数对象,同样遵循作用域规则。它不会因为写得短,就自动把外层变量的当前值复制下来。