下面学习一下Python的函数式编程。
1. 高阶函数
变量可以指向函数。
函数名也是变量。
一个函数可以接收另一个函数作为参数,这种函数称为高阶函数。
2. map/reduce
Python内建了map()和reduce()函数。
map()函数接收两个参数,第一个参数是函数,第二个参数是Iterable。map将传入的函数依次作用在Iterable的每个元素,并把结果作为新的Iterator返回。
Iterator是惰性序列。
例如我们将list的所有数字转为字符串,可以这么写:
list(map(str, [1, 2, 3, 4, 5]))
reduce()函数同样接收两个参数,不过第一个参数的函数要求接收两个参数,reduce把结果继续和下一个元素做累积运算。
也就是说:
reduce(f, [x1, x2, x3]) = f(f(x1, x2), x3)
要使用reduce,需要引入:
from functools import reduce
3. filter
filter()也是接收两个参数,一个函数和一个序列。filter()将函数作用于序列的每个元素,根据返回值是True还是False来决定保留还是丢弃该元素。
filter()返回的也是一个惰性序列,如果要强迫完成计算结果,需要用list()获得所有结果并返回list。
比如输出回文数字的方法:
def is_palindrome(n):
return n == int(str(n)[::-1])
output = filter(is_palindrome, range(1, 1000))
print(list(output))
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191, 202, 212, 222, 232, 242, 252, 262, 272, 282, 292, 303, 313, 323, 333, 343, 353, 363, 373, 383, 393, 404, 414, 424, 434, 444, 454, 464, 474, 484, 494, 505, 515, 525, 535, 545, 555, 565, 575, 585, 595, 606, 616, 626, 636, 646, 656, 666, 676, 686, 696, 707, 717, 727, 737, 747, 757, 767, 777, 787, 797, 808, 818, 828, 838, 848, 858, 868, 878, 888, 898, 909, 919, 929, 939, 949, 959, 969, 979, 989, 999]
4. sorted
Python内置的sorted()可以对list进行排序。
可以接收一个key函数来支持自定义的排序规则。
若需要进行反向排序,可传入参数reverse=True。
比如将字符串倒序排序,且忽略大小写,可以这么写:
sorted(['a', 'B', 'c'], key=str.lower, reverse=True)
5. 返回函数
一个函数可以返回一个函数最为结果,当返回函数时,该函数并未执行。
返回函数中不要引用任何可能变化的变量,否则可能会拿到意想不到的结果。
比如这段程序:
def calc():
rs = []
for i in range(1, 4):
def f():
return i * i
rs.append(f)
return rs
f1, f2, f3 = calc()
f1()
# 9
f2()
# 9
f3()
# 9
本来预想应该是1,4,9,可实际上却是3个9。原因就在于返回的函数并不是直接执行,而是在调用时才执行。
但是在调用时,由于已遍历生成了数组,i已经是3了,所以这三个函数执行的结果就都是9了。
6. 匿名函数
有些时候,我们不需要显示的定义函数,这时候就可以使用匿名函数。
关键字lambda表示匿名函数,冒号前面的是参数。
匿名函数有一个限制:只能有一个表达式,返回值就是该表达式的结果。
匿名函数也是一个函数对象,可以赋值给一个变量,或者作为函数的返回值。
7. 装饰器
假设我们想在一个函数调用前后打印一些日志,又不希望修改函数本身的定义,就可以使用“装饰器”模式来增强函数的功能。
本质上,装饰器就是一个返回函数的高阶函数,可以这么定义:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print("call %s():" % func.__name__)
return func(*args, **kw)
return wrapper
函数对象有一个__name__属性,可以拿到函数的名字。另外为了使拿到的名字是实际的名字,而不是wrapper的名字,可以使用functools的wraps方法。
如果是带参数的修饰器,可以这么定义:
import functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print("%s %s():" % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
然后我们可以这么用:
@log
def now():
print('2016')
@log('execute')
def now():
print('2016')
8. 偏函数
functools.partial可以帮助创建偏函数,即把一个函数的某些参数给固定住,返回一个新的函数。
创建偏函数时,可以接收函数对象、*args、**kw这3个参数。
当传入
int2 = functools.partial(int, base=2)
实际上固定了int()函数的关键字参数base,也就是:
int2('1010')
相当于:
kw = {'base': 2}
int2('1010', **kw)
当传入:
max2 = functools.partial(max, 10)
实际上会把10当做*args的一部分自动加到左边,也就是:
max2(5, 6, 7)
相当于:
args = (10, 5, 6, 7)
max(*args)