Lambda
高阶函数可以接收函数做参数,有些时候,我们不需要显式地定义函数,直接传入匿名函数更方便,也就是lambda。
先来看个简单 lambda 函数
>>> lambda x, y : x+y
<function <lambda> at 0x102bc1c80>
x 和 y 是函数的两个参数,冒号后面的表达式是函数的返回值,你能一眼看出这个函数就是是在求两个变量的和,但作为一个函数,这里我们暂且给这个匿名函数绑定一个名字,这样使得我们调用匿名函数成为可能。
>>> add = lambda x, y : x+y
>>> add
<function <lambda> at 0x102bc2140>
>>> add(1,2)
3
它等同于常规函数
>>> def add2(x, y):
... return x+y
...
>>> add2
<function add2 at 0x102bc1c80>
>>> add2(1,2)
3
匿名函数有个限制,就是只能有一个表达式,不写return,返回值就是该表达式的结果。
例如一个整数列表,要求按照列表中元素的绝对值大小升序排列。
>>> list1 = [3,5,-4,-1,0,-2,-6]
>>> sorted(list1, key=lambda x: abs(x) , reverse = False)
排序函数 sorted 支持接收一个函数作为参数,该参数作为 sorted 的排序依据,这里按照列表元素的绝对值进行升序排序。当然,我们也可以用普通函数来实现:
>>> def foo(x):
... return abs(x)
...
>>> sorted(list1, key=foo)
闭包
下面的一段代码定义了两个函数f() 和 g(),在函数f()中把g()作为返回值返回。
这里的g你可以参考C语言中函数的指针来进行理解,它们两个几乎在语义和用法上几乎完全相同。
def g():
print 'g()...'
def f():
print 'f()...'
return g
我们把函数g()的定义移到f()内部,这样除了f()之外的区域就无法调用g():
def f():
print 'f()...'
def g():
print 'g()...'
return g
但是对于以下 calc_sum 函数,发现没法把 lazy_sum 移到 calc_sum 的外部,因为它引用了 calc_sum 的参数 lst:
def calc_sum(lst):
def lazy_sum():
return sum(lst)
return lazy_sum
lst是一个列表,作为参数传进函数calc_sum()中。
此时我们发现无法把函数lazy_sum()移到函数calc_sum()的外面,因为函数lazy_sum()的功能是计算list中所有元素值的和,必须依赖于函数calc_sum()的参数。
如果函数lazy_sum()不在函数calc_sum()里面,则无法取得数据进行计算。
像这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。
闭包的特点是返回的函数还引用了外层函数的局部变量。
所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变,例如:
# 希望一次返回3个函数,分别计算1x1,2x2,3x3:
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
print(f1(),f2(),f3())
你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是 9,9,9。
因为当count()函数返回三个函数时,这三个变量所引用的变量i的值已经变成了3,因为在返回的时候三个函数并没有被调用。
所以此时它们并没有及时计算它们对应的i乘以i的值,等到三个函数都返回时,然后调用三个函数,此时i的值已经为3,计算i*i的值自然就都是9了。
因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。
将上述程序如下改写后,即可得到结果1,4,9。
def count():
fs = []
for i in range(1, 4):
def f(m=i):
return m*m
fs.append(f)
return fs
f1, f2, f3 = count()
print (f1(), f2(), f3())