5.10
5.10
5.10 支持函数式编程的包
函数式编程的本质是什么?
这个“函数”的概念并不是指计算机中的函数,而是指数学中的函数,即自变量的映射。
也就是说,一个函数的值仅仅取决于函数参数的值,不依赖其他状态。例如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候被调用,被调用了几次,都是一个结果。
函数式编程与命令式编程最大的不同其实在于:
函数式编程关心数据的映射(类型或代数结构之间的关系),命令式编程关心解决问题的步骤。
所谓映射,说白了就是一种东西和另一种东西之间的对应关系。
命令式编程中,你要做什么事情,要把完成目的的步骤详细描述出来,然后交给机器去运行。
那么函数式的风格,则是更注重在“对映射的描述”上。这样写出来的代码易推理,不易出错。
operator和functools等包的支持下,python可以轻松实现函数式编程。
示例如下:
>>> from functools import reduce
>>> def fact(n):
... return reduce(lambda a,b: a*b, range(1, n+1))
...
>>> fact(3)
6
另一种方式:
>>> from operator import mul
>>> def fact(n):
... return reduce(mul, range(1, n+1))
...
>>> fact(3)
6
operator模块为许多算术运算符提供了对应的函数,也避免了使用匿名函数这种复杂的写法。
operator模块中还有一类的函数(itemgetter,attrgetter),可以替代从序列中取出元素或者读取对象属性的lambda表达式。
>>> students = [('liu',18,(170,50)),('qi',20,(165,43)),('ss',23,(163,44))]
>>> from operator import itemgetter
>>> for s in sorted(students, key=itemgetter(1)):
... print(s)
...
('liu', 18, (170, 50))
('qi', 20, (165, 43))
('ss', 23, (163, 44))
>>> for s in sorted(students, key=itemgetter(2)):
... print(s)
...
('ss', 23, (163, 44))
('qi', 20, (165, 43))
('liu', 18, (170, 50))
>>> s_name = itemgetter(1,0)
>>> for s in students:
... print(s_name(s))
...
(18, 'liu')
(20, 'qi')
(23, 'ss')
可以得知itemgetter的用途在于根据元组中的某个字段给元组列表进行排序。
attrgetter于itemgetter类似,它创建的函数会根据名称提取对象的属性。如果将多个属性名传给attrgetter,它会返回提取的值构成的元组。
使用functools.partial冻结参数
这个高阶函数用于部分应用一个函数。部分应用是指,基于一个函数创建一个新的可调用的对象,将原函数的某些参数固定。使用这个函数可以将一个接受1或多个参数的函数改编成需要回调的API,这样参数更少。
>>> from functools import partial
>>> triple = partial(mul, 3)
>>> triple(7)
21
>>> list(map(triple, range(1,10)))
[3, 6, 9, 12, 15, 18, 21, 24, 27]
>>> triple
functools.partial(<built-in function mul>, 3)
在这个例子中,函数本身需要两个参数,而partial将它变成了单参数,因为它将mul这个参数固定了。
partial返回的是一个functools.partial对象。该对象提供了访问原函数和固定参数的属性。
有了这些函数,函数式编程就不太需要功能有限的lambda表达式了。