在 python 中,如何用好 functools 来实现函数式编程

757 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情

在之前给大家分享过函数式编程,最早感觉到函数式编程很酷,和朋友同事聊天时,是不是会把函数式编程挂在嘴边,当时函数式编程的确很火,主要因为 js 的借着 nodejs 和 npm 已经走上 top3 ,并且稳居 top3。因为 js 中 function 是一等公民,天生地对函数式编程 friendly,哦,我们还是回到 python,因为自己对于 js 喜爱,通常说着说着就跑题了。

偏函数

在开始介绍偏函数,以及如何在 python 中实现偏函数之前,我们来看一个简单函数,这个 add 函数用于将两个数相加,python 是一个弱类型的语言,不过也有自己的类型系统,如果类型明确,可以在参数后面给出类型,估计这样会解释器省掉一些类型推断的工作。

import functools

def add(a:float, b:float)->float:
    return a + b

好定义好了这么一个函数,我们进一步思考,在函数式编程中,我们希望输入和输出都是单一的。函数式编程主要是将程序中不变的,也就是确定性那部分内容抽里,这样便于降低程序的复杂度。偏函数又是什么呢? 偏函数对上面说的有什么作用呢。

简单的理解偏函数,是对原函数的二次封装,是将现有函数的部分参数预先绑定为指定值,从而得到一个新的函数,该函数就称为偏函数。

柯里化实现偏函数

接下来可以通过 currying 化来实现偏函数,不仅被用于JavaScript,还被用于其他编程语言。 柯里化是一种函数的转换,是指将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c) 。 也就是让我们 add 函数从同时接受 2 个参数变为分别有一定先后次序接受两个参数add(2)(3)。

def make_add(a:float)->Callable[[float],float]:
    def add_inner(b:float)->float:
        return a + b

    return add_inner

add_2 = make_add(2)

不过我们并不满足,还可以进一步优化,这里我们定义函数,可以通过定 lambda 函数来代替显式地定义一个函数,并将其返回。

def make_add(a:float)->Callable[[float],float]:
    return lambda b: add(a,b)

add_2 = make_add(2)

基于 functools 实现偏函数

如果利用 functools 库提供 partial 方法来实现一个偏函数,这样做可以让偏函数的实现更简单一些,这函数 partial 接收第一个参数为要包装函数名称,接下来参数就是为这个原函数的参数事先指定值了,我们通过代码来进行解释说明

add_2 = functools.partial(add,2)

我们也可以同时对 2 个参数都预先进行进行赋值,下面隐式为 add 函数两个参数都预先指定数值,当然也可以显式指定值。

add_2_3 = functools.partial(add,2,3)

也可以通过对 b 进行预先赋值来得到一个偏函数

add_3 = functools.partial(add,b=3)

还有可能可能让你感觉有点不自然地方,就是我们也可以在调用时候重新对已经事先赋值的 b 进行更改,其实有时候更灵活,少了一些约束,可能容易出现问题。

>>> add_3(2,b=5)
7