Python 编写纯函数

Python 编写纯函数,没有副作用的函数符合在数学中函数的纯粹定义:变量不会在全局范围内发生变化,避免使用global语句基本可以达到要求。为了达到纯粹,则要求避免函数改变可变对象的状态。

下面是一个纯函数的例子:

def m(n: int) -> int:
    return 2 ** n - 1

返回结果仅与参数n的值有关,既没有改变全局变量,也没有更新任何可变数据结构。

任何(通过自由变量)对Python全局命名空间中值的引用都可以用参数实现,通常这类操作都很简单。下面是一个使用自由变量的例子:

def some_function(a: float, b: float, t: float) -> float:
    return a + b * t + global_adjustment

可以将上面的global_adjustment变量变为函数的参数,然后修改所有使用这个函数的地方。在一个复杂应用中,这么做会引发一系列变动。对全局变量的引用在函数体中表现为自由变量。

Python的许多内置对象都带有状态,例如文件类以及与文件相关的对象,都是常用的有状态对象。可以把Python中的大多数有状态对象看作上下文管理器,虽然很多开发者不使用上下文管理器,但实际上很多对象都实现了它要求的接口。少数有状态对象没有完全实现上下文管理器的接口,但往往实现了close()方法。可以通过contextlib.closing()函数为这些对象实现对应的上下文管理接口。
除非是小程序,否则我们难以去除所有的Python有状态对象。因此,必须在发挥函数式优势与管理状态中间寻找平衡。为了达到目标,应尽量用with语句将有状态对象严格限制在一定的作用域内。

尽可能把文件对象放在with语句中。

尽量避免使用全局文件对象和全局数据库连接,以避免相关状态问题。全局文件对象是处理文件的常规方式,例如下面的命令片断所示的函数:

def open(iname: str, oname: str):
    global ifile, ofile
    ifile = open(iname, "r")
    ofile = open(oname, "w")

在这个场景中,其他函数可以随意使用ifile变量和ofile变量,而这两个变量不仅是全局的,还始终保持打开的状态。

这样的设计不符合函数式编程的要求,应尽量避免。文件应作为函数的参数,打开的文件应嵌在with语句中,以确保能正确处理其状态。将全局变量改为普通参数非常重要,会使文件操作更易辨识。

该原则也适用于数据库,应把数据库连接对象作为应用程序中函数的普通参数。有些流行的Web框架把数据库连接设计成全局的,使得整个应用程序都可以使用数据库的某些功能。这种透明性隐藏了Web操作对数据库的依赖,使得单元测试变得非常复杂。但单一的全局数据库连接并不能很好地支持多线程Web服务,使用连接池往往更好。这也说明了总体使用函数式设计,辅以一些严格限制的状态对象,是处理这种情况的理想解决方案。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程