函数式编程语言的特点:允许把函数本身作为参数传入另一个函数,还允许返回一个函数。
1 高阶函数
1.1 变量可以指向函数
对于绝对值函数abs()来说,abs(x)为函数调用,abs是函数本身。
通过将函数本身赋值给变量达到变量指向函数得的目的
f = abs
f(-10)
10
1.2 函数名也是变量
函数名其实就是指向函数的变量,即函数名abs自身就是一个变量,指向了生成绝对值的函数。
如果更改原函数的指向后重新调用函数会报错
abs = 10
abs(-10)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-2-dae1c1c42fe8> in <module>
1 abs = 10
----> 2 abs(-10)
TypeError: 'int' object is not callable
1.3 传入函数
一个函数接收另一个函数作为参数,这个函数就被称之为高阶函数
def add(x,y,f):
return f(x)+f(y)
add(-5,6,abs)
11
1.4 map/reduce
map() 函数能够接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到Iterable的每个元素上,结果用Iterator返回。
def f(x):
return x*x
r = map(f,list(range(1,10)))
list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
reduce的作用效果:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
传入的函数必须接收两个参数,第二个参数需要时是列表。
from functools import reduce
def add(x,y):
return x+y
reduce(add,[1,3,5,7,9])
25
1.5 filter
filter()的主要作用在于过滤序列,filter()同样是接收一个函数和一个序列,不过在将函数作用与每个元素后会根据返回值的布尔类型判断是否丢弃元素。
#在列表中删除偶数,只保留奇数
def is_odd(n):
return n%2 ==1
list(filter(is_odd,[1,2,3,4,5,6,9,10,15]))
[1, 3, 5, 9, 15]
#删除序列中的空字符串
def not_empty(s):
return s and s.strip()
list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))
['A', 'B', 'C']
#用python实现埃式筛法
#构造从3开始的奇数序列(构造出的为一个生成器,生成无限序列)
def _odd_iter():
n = 1
while True:
n = n+2
yield n
#定义筛选器
def _not_divisible(n):
return lambda x:x%n >0
#定义生成器,不断返回下一个素数
def primes():
yield 2
it = _odd_iter()
while True:
n = next(it)
yield n
it = filter(_not_divisible(n),it)
for n in primes():
if n < 1000:
print(n)
else:
break
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
59
61
67
71
73
79
83
89
97
101
103
107
109
113
127
131
137
139
149
151
157
163
167
173
179
181
191
193
197
199
211
223
227
229
233
239
241
251
257
263
269
271
277
281
283
293
307
311
313
317
331
337
347
349
353
359
367
373
379
383
389
397
401
409
419
421
431
433
439
443
449
457
461
463
467
479
487
491
499
503
509
521
523
541
547
557
563
569
571
577
587
593
599
601
607
613
617
619
631
641
643
647
653
659
661
673
677
683
691
701
709
719
727
733
739
743
751
757
761
769
773
787
797
809
811
821
823
827
829
839
853
857
859
863
877
881
883
887
907
911
919
929
937
941
947
953
967
971
977
983
991
997
1.6 sorted
利用sorted函数对列表进行排序
sorted([36,5,-12,9,-21])
[-21, -12, 5, 9, 36]
接收key函数实现自定义排序,比如按绝对值大小排序
sorted([36,5,-12,9,-21],key = abs)
[5, 9, -12, -21, 36]
利用sorted对字符串进行排序
sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']
忽略字符串的大小写
sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
反向排序
sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']
2 返回函数
2.1 函数作为返回值
高阶函数不仅可以接收函数作为参数,还可以把参数作为结果返回
# 实现可变参数求和
def calc_sum(*args):
ax = 0
for n in args:
ax = ax+n
return ax
#不返回求和结果,返回求和的函数:
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax+n
return ax
return sum
f = lazy_sum(1,3,5,7,9)
f
<function __main__.lazy_sum.<locals>.sum()>
f()
25
在函数lazy_sum中定义函数sum,内部函数sum引用外部函数lazy_sum的参数和局部变量,lazy_sum返回函数sum的时候将相关参数和变量都保存在返回的函数中,叫做"闭包"(程序结构)
2.2 闭包
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())
print(f2())
print(f3())
9
9
9
之所以全部为9的原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。
返回闭包时牢记:返回函数不要使用任何循环变量,或者后续产生变化的变量
3 匿名函数
通过匿名函数及对应正常写法快速掌握
#正常写法
def f(x):
return x*x
#匿名函数写法
lambda x:x*x
匿名函数得限制:只能有一个表达式,不用写return,返回值就是表达式结果
匿名函数可以赋值给变量,让变量来调用
也可以把匿名函数作为返回值返回
def build(x,y):
return lambda x,y:x*x+y*y
print(build(1,2))
<function build.<locals>.<lambda> at 0x000001F21FF113A0>
4 装饰器
#通过变量调用函数
def now():
print('2015-3-25')
f = now
f()
2015-3-25
利用函数对象的__ name __ 属性拿到函数名
now.__name__
'now'
装饰器(decorator)定义:在代码运行期间动态增加功能的方式(增加函数功能又不改变定义)
def log(func):
def wrapper(*args,**kw):
print('call %s():' % func.__name__)
return func(*args,**kw)
return wrapper
装饰器接收一个函数作为参数,返回一个参数,我们需要采用@语法将装饰器置于函数的定义处:
@log
def now():
print('2015-3-25')
now()
call now():
2015-3-25
调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:
@log与语句now = log(now) 相同
wrapper()函数的参数定义是(* args, ** kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。
5 偏函数
引入
#字符串转整数
print(int('12345'))
#利用int()中的base参数进行N进制转换
print(int('12345',16))
12345
74565
定义一个二进制转换函数
def int2(x,base = 2):
return int(x,base)
int2('1000000')
64
functools.partial帮助创建偏函数,不需要我们自己定义二进制转换函数,使用下面代码即可创建
import functools
int2 = functools.partial(int, base=2)
int2('1000000')
64
当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。