匿名函数与闭包
匿名函数与闭包
匿名函数
Python 提供了一个关键字 lambda,让我们可以创建一个匿名函数,也就是没有名称的函数。它的形式如下:
lambda 参数: 表达式
关键字 lambda 说明它是一个匿名函数,冒号 : 前面的变量是该匿名函数的参数,冒号后面是函数的返回值,注意这里不需使用 return 关键字。我们将上面的 double 函数改写成一个匿名函数,如下:
lambda x: 2 * x
那怎么调用匿名函数呢?可以直接这样使用:
>>> (lambda x: 2 * x)(8)
16
由于匿名函数本质上是一个函数对象,也可以将其赋值给另一个变量,再由该变量来调用函数,如下:
>>> f = lambda x: 2 * x # 将匿名函数赋给变量 f
>>> f
<function <lambda> at 0x7f835a696578>
>>> f(8)
16
lambda
函数一般适用于创建一些临时性的,小巧的函数。比如上面的 double
函数,我们当然可以使用 def
来定义,但使用 lambda
来创建会显得很简洁,尤其是在高阶函数的使用中。
def func(g, arr):
return [g(x) for x in arr]
现在给一个列表 [1, 2, 3, 4],利用上面的函数,对列表中的元素加 1,返回一个新的列表,你可能这样用:
def add_one(x):
return x + 1
arr = func(add_one, [1, 2, 3, 4])
这样做没什么错,可是 add_one
这个函数太简单了,使用 def
定义未免有点小题大作,我们改用 lambda
:
arr = func(lambda x: x + 1, [1, 2, 3, 4])
闭包
在 Python 中,函数也是一个对象。因此,我们在定义函数时,可以再嵌套定义一个函数,并将该嵌套函数返回,比如:
from math import pow
def make_pow(n):
def inner_func(x): # 嵌套定义了 inner_func
return pow(x, n) # 注意这里引用了外部函数的 n
return inner_func # 返回 inner_func
上面的代码中,函数 make_pow
里面又定义了一个内部函数 inner_func
,然后将该函数返回。因此,我们可以使用 make_pow
来生成另一个函数:
>>> pow2 = make_pow(2) # pow2 是一个函数,参数 2 是一个自由变量
>>> pow2
<function inner_func at 0x10271faa0>
>>> pow2(6)
36.0
我们还注意到,内部函数 inner_func
引用了外部函数 make_pow
的自由变量 n
,这也就意味着,当函数 make_pow
的生命周期结束之后,n
这个变量依然会保存在 inner_func
中,它被 inner_func
所引用。
>>> del make_pow # 删除 make_pow
>>> pow3 = make_pow(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'make_pow' is not defined
>>> pow2(9) # pow2 仍可正常调用,自由变量 2 仍保存在 pow2 中
81.0
像上面这种情况,一个函数返回了一个内部函数,该内部函数引用了外部函数的相关参数和变量,我们把该返回的内部函数称为闭包(Closure)。在上面的例子中,inner_func
就是一个闭包,它引用了自由变量 n
。
闭包的作用
- 闭包的最大特点就是引用了自由变量,即使生成闭包的环境已经释放,闭包仍然存在。
- 闭包在运行时可以有多个实例,即使传入的参数相同。
>>> pow_a = make_pow(2)
>>> pow_b = make_pow(2)
>>> pow_a == pow_b
False
- 利用闭包,我们还可以模拟类的实例。
这里构造一个类,用于求一个点到另一个点的距离:
from math import sqrt
class Point(object):
def __init__(self, x, y):
self.x, self.y = x, y
def get_distance(self, u, v):
distance = sqrt((self.x - u) ** 2 + (self.y - v) ** 2)
return distance
>>> pt = Point(7, 2) # 创建一个点
>>> pt.get_distance(10, 6) # 求到另一个点的距离
5.0
用闭包来实现:
def point(x, y):
def get_distance(u, v):
return sqrt((x - u) ** 2 + (y - v) ** 2)
return get_distance
>>> pt = point(7, 2)
>>> pt(10, 6)
5.0
可以看到,结果是一样的,但使用闭包实现比使用类更加简洁。