0%

Python高阶函数

函数式编程简介

函数式:functional,一种编程范式,与函数是完全不同的东西

高阶函数简介

高阶函数:能接收函数做参数的函数
1.变量可以指向函数,函数名就是指向函数的变量。

1
2
f = abs
print (f(-12)) # 12

2.函数的参数可以接收变量,所以一个函数可以接收另一个函数作为参数。

1
2
3
4
5
import math
def add(x,y,f=abs):
return f(x) + f(y)
print (add(1,-1)) # 2
print (add(4,9,math.sqrt)) # 5.0

Python中返回函数

Python的函数不但可以返回int、str、list、dict等数据类型,还可以返回函数。注意区分返回函数和返回值,

1
2
3
4
def myabs():
return abs # 返回函数
def myabs2(x):
return abs(x) # 返回函数调用的结果,返回值是一个数值

返回函数可以把一些计算延迟执行。例如,如果定义一个普通的求和函数,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def calc_sum(lst):
return sum(lst)
# 调用calc_sum()函数时,将立刻计算并得到结果
calc_sum([1, 2, 3, 4]) # 10
#如果返回一个函数,就可以“延迟计算”
def calc_sum(lst):
def lazy_sum():
return sum(lst)
return lazy_sum
# 调用calc_sum()并没有计算出结果,而是返回函数
f = calc_sum([1, 2, 3, 4])
print(f) # <function lazy_sum at 0x1037bfaa0>
# 对返回的函数进行调用时,才计算出结果:
print(f()) # 10

由于可以返回函数,在后续代码里就可以决定到底要不要调用该函数。

闭包

在函数内部定义的函数和外部定义的函数是一样的,只是无法被外部访问
例如,将g的定义移入函数f内部,防止其他代码调用g,

1
2
3
4
5
def f():
print 'f()...'
def g():
print 'g()...'
return g

但是,考察如下calc_sum函数,

1
2
3
4
def calc_sum(lst):
def lazy_sum():
return sum(lst)
return lazy_sum

注意:发现没法把lazy_sum移到calc_sum的外部,因为它引用了calc_sum的参数lst。
像这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)
闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。举例如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
# 希望一次返回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()
``
f1(),f2()和f3()实际结果全部都是9,而不是149。原因就是当count()函数返回了3个函数时,这3个函数所引用的变量i的值已经变成了3。由于f1、f2、f3并没有被调用,所以,此时他们并未计算i*i,当f1被调用时,
​```Python
print(f1()) # 9 因为f1现在才计算i*i,但现在i的值已经变为3

第一步:执行代码“f1, f2, f3 = count()”赋值号的右边,调用count()函数,开始执行count()函数的内部命令
第二步:创建一个局部变量fs
第三步:开始执行for循环遍历列表[1,2,3],i赋值1,然后执行fs.append(f)代码,得到[f],注意这时候并不需要执行函数f(),因为没有调用
第四步:继续执行for循环遍历列表[1,2,3],i赋值2,然后执行fs.append(f)代码,得到[f,f],注意这时候并不需要执行函数f(),因为没有调用
第五步:继续执行for循环遍历列表[1,2,3],i赋值3,然后执行fs.append(f)代码,得到[f,f,f],注意这时候并不需要执行函数f(),因为没有调用
第六步:执行代码“f1, f2, f3 = count()”赋值号的左边,将fs(即[f,f,f])赋值给f1,f2,f3,使用到了高级序列赋值语句模式
第七步:执行print (f1(),f2(),f3())代码
第八步:调用函数f(),执行return ii,此时的i的地址值指向的是外层函数中i的地址,i的值应为for之前的for循环重新赋值为3,所以return 33
因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。

匿名函数

在Python中,对匿名函数提供了有限支持。以map()函数为例,计算 f(x)=x2时,除了定义一个f(x)的函数外,还可以直接传入匿名函数

1
map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])  # [1, 4, 9, 16, 25, 36, 49, 64, 81]

通过对比可以看出,匿名函数lambda x: x * x实际上就是,

1
2
def f(x):
return x * x

关键字lambda表示匿名函数,冒号前面的x表示函数参数
匿名函数只能有一个表达式,不写return,返回值就是该表达式的结果
使用匿名函数,可以不必定义函数名,直接创建一个函数对象,可以简化代码,

1
sorted([1, 3, 9, 5, 0], lambda x,y: -cmp(x,y))  # [9, 5, 3, 1, 0]

返回函数的时候,也可以返回匿名函数

1
2
3
myabs = lambda x: -x if x < 0 else x 
myabs(-1) # 1
myabs(1) # 1

偏函数

当一个函数有很多参数时,调用者就需要提供多个参数。如果减少参数个数,就可以简化调用者的负担。
比如,int()函数可以把字符串转换为整数,当仅传入字符串时,int()函数默认按十进制转换,

1
print(int('12345'))  # 12345

但int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做N进制的转换,

1
2
print(int('12345', base=8))  # 5349
print(int('12345', 16)) # 74565

假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,可以定义一个int2()的函数,默认把base=2传进去,

1
2
3
4
5
def int2(x, base=2):
return int(x, base)

print(int2('1000000')) # 64
print(int2('1010101')) # 85

functools.partial用来创建偏函数,不需要自己定义int2(),可以直接使用下面的代码创建一个新的函数int2,

1
2
3
4
import functools
int2 = functools.partial(int, base=2)
int2('1000000') # 64
int2('1010101') # 85

所以,functools.partial可以把一个参数多的函数变成一个参数少的新函数,少的参数需要在创建时指定默认值。

天生我材必有用,千金散尽还复来~
  • 本文作者: XTLei
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
-------------本文结束感谢您的阅读-------------