Python 纯函数,函数式编程简洁明了,因为函数可以用作其他函数的参数或者返回值,后续会给出很多这样的例子。
要做到这一点,函数必须是运行时环境中的头等对象。在C等语言中,函数不是运行时中的对象,然而在Python中,函数通常是通过def
语句创建的对象,且其他函数可以使用。我们还可以通过创建可调用对象,或者将lambda
表达式赋给变量来创建函数。
创建函数即创建一个带有属性的对象,如下所示。
>>> def example(a, b, **kw):
... return a*b
...
>>> type(example)
<class 'function'>
>>> example.__code__.co_varnames
('a', 'b', 'kw')
>>> example.__code__.co_argcount
2
这里我们创建了一个对象:example
,其为function
类。此对象包含很多属性,与该函数对象关联的__code__
对象也含有自己的属性。其具体实现细节不重要,重要的是Python中的函数是头等对象,我们完全可以像处理其他对象一样处理函数,比如上面的代码示例展示了函数对象的其中两个属性。
纯函数
为了提高程序可读性,使用的函数要尽量没有副作用,即所谓的“纯函数”。使用纯函数的好处包括可以通过改变求值顺序实现优化,而其最重要的优势在于概念简单、测试方便。
在Python中,编写纯函数式代码要求代码的作用域为本地,具体而言,就是避免使用global
语句。nonlocal
语句的使用也可能对作用域产生副作用,也应留意,虽然副作用限制在一个嵌套函数里。实际上,达到这些要求并不难。可以把纯函数看作普通的Python编程实践。
并没有简单的方法能保证Python函数没有副作用,编码时不小心违反了纯函数规则也是常有之事。如果实在担心可能违反规则,可以写一个函数,使用dis
模块扫描给定函数的__code__.co_code
属性,即编译后的代码,检查是否包含全局引用。它能对内部闭包和__code__.co_freevars
元组方法的使用给出提示。然而为了避免极少出现的情形而运用这类复杂的技术有些得不偿失,因此后续不会展开讨论。
Python的lambda
表达式是纯函数。虽然不太推崇,但确实可以通过lambda
表达式创建纯函数。
将lambda
表达式赋给变量以创建函数的示例如下。
>>> mersenne = lambda x: 2 ** x - 1
>>> mersenne(17)
131071
将lambda
表达式赋给变量mersenne
,即可得到一个纯函数,实际上是一个包含单一参数x
,并返回单个值的可调用对象。因为lambda
表达式中不能包含赋值语句,所以它总为纯函数,适用于函数式编程。
高阶函数
使用高阶函数可以使程序简洁明了。高阶函数以其他函数为参数,或者用函数作为返回值。我们可以使用高阶函数将简单的函数组合成复合函数。
以Python的max()
函数为例,我们可以提供一个函数作为其参数,来改变max()
函数的行为。
待处理的数据如下。
>>> year_cheese = [(2000, 29.87), (2001, 30.12), (2002, 30.6), (2003,
30.66),(2004, 31.33), (2005, 32.62), (2006, 32.73), (2007, 33.5),
(2008, 32.84), (2009, 33.02), (2010, 32.92)]
可以如下所示使用max()
函数。
>>> max(year_cheese)
(2010, 32.92)
其默认行为会比较列表中的每个元组,按元组下标为0的元素比较大小,返回最大的元素所在的元组。
由于max()
函数是高阶函数,因此可以添加一个函数作为其参数。这里用一个lambda
表达式作为它的函数参数,如下所示。
>>> max(year_cheese, key=lambda yc: yc[1])
(2007, 33.5)
在这个例子中,max()
函数用lambda
表达式定义的函数作为比较依据,返回了下标为1的最大元素所在的元组。
Python提供了许多高阶函数,后面会介绍Python提供的许多高阶函数以及编写高阶函数的方法。