Python第十一课:彻底搞懂Python中的高阶函数和匿名函数
文章目录
引言
在 Python 中,函数是一等公民——这意味着函数可以像普通变量一样被传递、作为参数、甚至作为返回值。这种特性催生了“高阶函数”的概念,也让我们能够用匿名函数(lambda)写出简洁的临时函数。无论是数据清洗、排序,还是简化代码逻辑,高阶函数和匿名函数都无处不在。我将从基础概念开始,带你逐步掌握 map、filter、reduce 等内置高阶函数,以及 lambda 表达式的正确使用姿势,让你的 Python 编程更上一层楼。
在开始之前先检查一下你的装备吧!!!
python环境不会装的看这里:从安装到Hello World:Python环境搭建完整指南
python编辑器不会装的看这里:零基础Python入门:手把手教你安装Python、新版PyCharm和VS Code
什么是高阶函数?
1. 高阶函数的定义
在 Python 中,高阶函数是指满足以下条件之一的函数:
接受一个或多个函数作为参数;
返回一个函数作为结果。
这个定义听起来可能有点抽象,但其实你每天都在不知不觉中使用它们。Python 把函数当作“一等公民”来处理,这意味着函数和整数、字符串、列表一样,可以被赋值给变量、存储在容器中,当然也可以作为参数传递或作为返回值返回。
简单来说,高阶函数就是对函数进行操作的函数——它把函数当作“数据”(参数)来使用。
2. 为什么需要高阶函数?
你可能会问:“我直接写普通的函数不就行了吗?为什么非要搞得这么复杂?”
想象一下这些场景:
- 你需要对列表中的每个元素进行相同的操作(比如平方、转大写),难道每次都要写一个
for循环吗? - 你想根据不同的规则对数据进行排序,难道要为每种规则都重写排序逻辑吗?
- 你想实现一个通用的“重试”机制,当函数执行失败时自动重试,难道要把重试逻辑复制到每个函数里吗?
高阶函数就是为了解决这类问题而生的——它能让我们抽象出通用的模式,把具体操作作为参数传递进去,从而写出更灵活、更简洁、更少重复的代码。这也是函数式编程思想的核心之一。
3. Python 中常见的高阶函数
Python 内置了许多实用的高阶函数,下面我们来认识几个最常用的。
1. map():对可迭代对象的每个元素应用函数
map(function, iterable) 接收一个函数和一个可迭代对象,将函数依次作用于可迭代对象的每个元素,并返回一个迭代器(Python 3 中)。
示例:将列表中的每个数字平方
numbers = [1, 2, 3, 4, 5]
def square(x):
return x * x
# 使用自定义函数
squared = map(square, numbers)
print(list(squared)) # [1, 4, 9, 16, 25]
# 当然,更常见的做法是配合 lambda 使用(下一部分会详细讲)
squared_lambda = map(lambda x: x**2, numbers)
print(list(squared_lambda)) # [1, 4, 9, 16, 25]
2. filter():筛选出满足条件的元素
filter(function, iterable) 接收一个返回布尔值的函数和一个可迭代对象,保留那些使函数返回 True 的元素,返回一个迭代器。
示例:筛选出列表中的偶数
numbers = [1, 2, 3, 4, 5, 6]
def is_even(x):
return x % 2 == 0
evens = filter(is_even, numbers)
print(list(evens)) # [2, 4, 6]
3. reduce():对序列进行累积计算
reduce(function, iterable[, initial]) 位于 functools 模块中。它接收一个接收两个参数的函数,并依次将序列中的元素累积起来。
示例:计算列表中所有数字的乘积
from functools import reduce
numbers = [1, 2, 3, 4]
def multiply(x, y):
return x * y
product = reduce(multiply, numbers)
print(product) # 24
# 带初始值:reduce(multiply, numbers, 10) 相当于 10*1*2*3*4 = 240
# reduce(multiply, numbers, 10) # 2400
4. sorted():自定义排序规则
sorted(iterable, key=None, reverse=False) 接受一个可选的 key 参数,该参数是一个函数,用于从每个元素中提取比较键。
示例:按字符串长度排序
words = ['apple', 'banana', 'cherry', 'date']
sorted_by_len = sorted(words, key=len)
print(sorted_by_len) # ['date', 'apple', 'banana', 'cherry'] (按长度升序)
示例:按元组的第二个元素排序
students = [('Alice', 88), ('Bob', 75), ('Charlie', 92)]
sorted_by_score = sorted(students, key=lambda s: s[1])
print(sorted_by_score) # [('Bob', 75), ('Alice', 88), ('Charlie', 92)]
5. partial():冻结部分参数
functools.partial 是一个高阶函数,它接受一个函数和一些参数,返回一个新的函数,新函数被调用时只需传递剩余的参数。
示例:创建一个专门用于计算 2 的幂的函数
from functools import partial
def power(base, exponent):
return base ** exponent
square = partial(power, exponent=2) # 固定 exponent=2
cube = partial(power, exponent=3) # 固定 exponent=3
print(square(5)) # 25
print(cube(5)) # 125
4. 自定义高阶函数
除了使用内置的高阶函数,你完全可以自己编写高阶函数。比如实现一个函数计时器:
import time
def timer(func):
"""一个简单的计时器装饰器(本质上是一个高阶函数)"""
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 运行耗时: {end - start:.4f} 秒")
return result
return wrapper
# 使用
@timer
def slow_function():
time.sleep(1)
return "完成"
slow_function() # 输出: slow_function 运行耗时: 1.0005 秒
这个 timer 函数接收一个函数 func,并返回一个新的函数 wrapper,它包裹了原函数并添加了计时功能。这就是高阶函数的典型应用——增强或改变原函数的行为。
什么是匿名函数(Lambda)?
上一部分我们学习了高阶函数——那些能够接收函数作为参数或返回函数的函数。但你可能会注意到,在示例代码中,我们常常需要为 map、filter 等写一个完整的函数定义(比如 def square(x): return x*x),即使这个函数只用一次。这显得有些繁琐。
这时,匿名函数就派上用场了。它允许我们快速定义“用完即弃”的简单函数,让代码更加紧凑。
1. 匿名函数的定义与语法
匿名函数,顾名思义,就是没有名字的函数。在 Python 中,使用 lambda 关键字创建,因此也常被称为 lambda 函数。
基本语法:
lambda 参数列表: 表达式
lambda:关键字,声明这是一个匿名函数。参数列表:与普通函数一样,可以有一个或多个参数,用逗号分隔,也可以没有参数(但括号不写)。表达式:一个单一的表达式,不能包含语句(如if、for、return等)。表达式的结果会自动作为返回值。
例如,一个简单的加法函数:
add = lambda x, y: x + y
print(add(3, 5)) # 8
实际上,上面的代码将匿名函数赋值给了变量 add,此时它有了名字,不再是“匿名”了。但这只是为了演示语法。lambda 的真正威力在于直接作为参数传递,不需要给它命名。
2. 与普通函数的对比
为了更好地理解 lambda,我们把它和用 def 定义的普通函数放在一起比较:
| 特性 | 普通函数 (def) |
匿名函数 (lambda) |
|---|---|---|
| 名称 | 必须有函数名 | 没有函数名(除非赋值给变量) |
| 语法 | def 函数名(参数): 函数体 |
lambda 参数: 表达式 |
| 函数体 | 可以包含多条语句、复杂逻辑 | 只能包含一个表达式 |
| 返回值 | 使用 return 语句返回 |
表达式的结果就是返回值,无需 return |
| 适用场景 | 需要重复使用的、逻辑复杂的函数 | 一次性使用的、逻辑简单的函数 |
示例对比:
# 普通函数
def square(x):
return x * x
# lambda 等价(通常不会这样赋值,仅作对比)
square_lambda = lambda x: x * x
3. 为什么需要匿名函数?
你可能会问:“直接用普通函数不好吗?lambda 看起来只是省略了函数名而已。”
主要有两个原因:
- 简洁性:对于简单的逻辑,用 lambda 可以少写几行代码,尤其适合作为高阶函数的参数,直接内联,让代码更紧凑。
- 临时性:有些函数只在某个特定地方使用一次,专门给它起个名字反而会增加命名负担,lambda 正好解决了这个问题。
4. 常见使用场景
场景一:作为高阶函数的参数(最常见)
这是 lambda 最典型的用法,配合 map、filter、sorted 等函数,让代码极其简洁。
numbers = [1, 2, 3, 4, 5]
# 用 lambda 替代 square 函数
squared = list(map(lambda x: x**2, numbers))
print(squared) # [1, 4, 9, 16, 25]
# 筛选偶数
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4]
# 按元组的第二个元素排序
students = [('Alice', 88), ('Bob', 75), ('Charlie', 92)]
sorted_by_score = sorted(students, key=lambda s: s[1])
print(sorted_by_score) # [('Bob', 75), ('Alice', 88), ('Charlie', 92)]
场景二:与 reduce 配合进行累积操作
from functools import reduce
numbers = [1, 2, 3, 4]
product = reduce(lambda x, y: x * y, numbers)
print(product) # 24
场景三:在需要简单回调函数的地方
例如,在 GUI 编程或事件处理中,有时需要一个简单的回调:
button.clicked.connect(lambda: print("按钮被点击了"))
(当然,Python 的 GUI 库并不常用 lambda 作为回调,但思想是通用的。)
场景四:作为 key 函数进行排序或求极值
words = ['apple', 'banana', 'cherry', 'date']
# 按最后一个字母排序
sorted_by_last_char = sorted(words, key=lambda w: w[-1])
print(sorted_by_last_char) # 输出:['banana', 'apple', 'date', 'cherry']
# 找出长度最长的单词
longest = max(words, key=lambda w: len(w))
print(longest) # 'banana'
5. 注意事项与限制
虽然 lambda 很方便,但使用不当也会影响代码可读性。以下几点需要留意:
-
只能包含表达式,不能包含语句:这意味着你不能在 lambda 中使用
print(Python 3 中print是函数,所以其实可以,但不建议)、if-elif-else(但可以用三元表达式x if cond else y)、循环、try-except等。# 错误示例:不能在 lambda 中使用 if 语句 # func = lambda x: if x > 0: return x # 语法错误 # 正确:使用三元表达式 func = lambda x: x if x > 0 else -x -
逻辑复杂时影响可读性:如果 lambda 的表达式过于复杂,嵌套太多,代码会变得难以理解。这时应该改用普通的
def函数。# 不推荐:过于复杂的 lambda func = lambda x: (x**2 + 3*x + 2) if x > 0 else (x**3 - 2*x) if x < 0 else 0 # 推荐:用普通函数清晰表达 def func(x): if x > 0: return x**2 + 3*x + 2 elif x < 0: return x**3 - 2*x else: return 0 -
调试困难:lambda 函数没有
__name__属性,在堆栈跟踪中显示为<lambda>,如果程序出错,难以定位是哪个 lambda 的问题。 -
性能差异微乎其微:lambda 和普通函数在性能上没有明显差异,不必为此担心。
6. 进阶技巧:lambda 中的赋值表达式(Python 3.8+)
Python 3.8 引入了赋值表达式(海象运算符 :=),可以在表达式中进行赋值。这个特性也可以用在 lambda 中,让某些场景更简洁,但需谨慎使用以免降低可读性。
# 示例:使用海象运算符在 lambda 中缓存计算结果
data = [1, 2, 3, 4, 5]
result = list(map(lambda x: (squared := x**2, squared + 1)[1], data))
print(result) # [2, 5, 10, 17, 26] (每个元素:平方+1)
但这种写法通常不推荐,因为它牺牲了可读性。除非你非常确定这样写能让代码更清晰。
更清晰的写法是直接使用普通函数或列表推导式。
更多推荐



所有评论(0)