函数是Python内建支持的一种封装,把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
一高阶函数
二返回函数
三匿名函数
四装饰器
五偏函数
一高阶函数
既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
例1
>>> abs(-1)
1
>>> f = abs
>>> f(-1)
1
>>> f
<built-in function abs>
>>> abs = 2
>>> abs(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
例2
>>> def add(x, y, f):
... return f(x) + f(y)
>>> print(add(-5, 6, abs))
11
1 map/reduce函数
map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
例3
>>> def f(x):
... return x*x
>>> r = map(f, [1,2,3,4,5])
>>> r
<map object at 0x1072590f0>
>>> list(r)
[1, 4, 9, 16, 25]
reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算
效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
例4
>>> from functools import reduce
>>> def fn(x, y):
... return x*10+y
>>> reduce(fn, [1,3,5,7,3])
13573
>>> type(reduce(fn, [1,3,5,7,3]))
<class 'int'>
例5
>>> def char2num(s):
... digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
... return digits[s]
>>> reduce(fn, map(char2num, '13579'))
13579
>>> r = map(char2num, '13579')
>>> list(r)
[1, 3, 5, 7, 9]
>>> type(reduce(fn, map(char2num, '13579')))
<class 'int'>
例6 利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456
from functools import reduce
def str2float(s):
str2part1 = s.split('.')[0]
str2part2 = s.split('.')[1]
def char2num(s):
digits = {'0':0, '1':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9}
return digits[s]
def fn1(x,y):
return x*10 + y
def fn2(x,y):
return x*0.1 + y
return reduce(fn1, map(char2num, str2part1)) + reduce(fn2, map(char2num, str2part2[::-1]))*0.1
2 filter函数
Python内建的filter()函数用于过滤,筛选序列。
filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
例7:
>>> def not_empty(s):
... return s and s.strip()
>>> filter(not_empty, ['A', '', 'B', None, 'C', ' '])
<filter object at 0x10ba9ce80>
>>> list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))
['A', 'B', 'C']
//注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。
例8:回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()筛选出回数:
def is_palindrome(n):
return str(n) == str(n)[::-1]
output = filter(is_palindrome, range(1, 1000))
3 sorted函数
sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序。
例8:
>>> sorted([36, 5, -12, 9, -21])
[-21, -12, 5, 9, 36]
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
例9:
>>> 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']
例10: 假设我们用一组tuple表示学生名字和成绩:
>>> L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
>>> def by_name(t):
... return t[0]
>>> L2 = sorted(L, key=by_name)
>>> print(L2)
[('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
>>> def by_score(t):
... return -t[1]
>>> L2 = sorted(L, key=by_score)
>>> print(L2)
[('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]
二返回函数
高阶函数出了可以接受函数作为参数外,还可以把函数作为结果值返回。
注意:返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。
闭包的简介:https://foofish.net/python-closure.html
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
例11
>>> 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 lazy_sum.<locals>.sum at 0x1028efe18>
>>> f()
25
//内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中
三 匿名函数
传入函数时,有时,不需要显式地定义函数,直接传入匿名函数更方便,同时不必担心函数名冲突。
匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数,也可以把匿名函数作为返回值返回。
例12
def is_odd(n):
return n % 2 == 1
L = list(filter(is_odd, range(1, 20)))
//用匿名函数改造
L = list(filter(lambda x:x%2==1, range(1, 20)))
四 装饰器
这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。
例13 我们要定义一个能打印日志的decorator
//import functools
>>> def log(func):
// @functools.wraps(func)
... def wrapper(*args, **kw): //wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。
... print('call %s():' % func.__name__)
... return func(*args, **kw) //在wrapper()函数内,首先打印日志,再紧接着调用原始函数。
... return wrapper
>>> @log //借助Python的@语法,把decorator置于函数的定义处,相当于执行了now = log(now)
... def now():
... print('2018-04-08')
>>> now() //由于log()是一个decortor,返回一个函数, 原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即log()函数返回的wrapper()函数。
call now():
2018-04-08
例14 要自定义log的文本, 3层嵌套的decorator
// import functools
>>> def log(text):
// @functools.wraps(func)
... def decorator(func):
... def wrapper(*args, **kw):
... print('%s %s():' % (text, func.__name__))
... return func(*args, **kw)
... return wrapper
... return decorator
>>> @log('execute') //now = log('execute')(now)
... def now():
... print('2018-04-08')
>>> now()
execute now():
2018-04-08
>>> now.__name__
'wrapper' //经过decorator装饰之后的函数,它们的__name__已经从原来的'now'变成了'wrapper'
例15 请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间
>>> import time, functools
>>> def metric(fn):
... @functools.wraps(fn)
... def wrapper(*agrs, **kw):
... t0 = time.time()
result = fn(*args, **kw) //不过可以在前面执行函数的时候把返回值保存下来最后返回。这样不用重复执行那个函数了
... //fn(*agrs, **kw)
... t1 = time.time()
... print('%s executed in %f ms' % (fn.__name__, (t1 - t0)*1000))
return result
... // return fn(*agrs, **kw) return 带函数返回,则主程序执行了2次
... return wrapper //必须要带返回啊,要不wrapper返回的东西就和加装饰器之前不一样了。
>>> @metric
... def fast(x, y):
... time.sleep(0.0012)
... return x + y;
...
>>> @metric
... def slow(x, y, z):
... time.sleep(0.1234)
... return x * y * z;
...
>>> f = fast(11, 22)
fast executed in 1.627 ms
>>> s = slow(11, 22, 33)
slow executed in 125.969 ms
五 偏函数
简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
例16
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('10010')
18