有些函数式语言(比如Haskell和Scala)是静态编译的,通过类型声明确定函数及其参数的类型。为了具备Python式的灵活性,这些语言发展出了各自的类型匹配规则,从而能编写针对相近的一组类的抽象函数。
在面向对象的Python中,常用类继承代替复杂的函数类型匹配,利用Python的命名匹配规则将运算符与正确的方法关联。
由于Python语言非常灵活,并不需要编译式函数语言的类型匹配规则,甚至可以说,复杂的类型匹配规则不过是静态编译语言为了能编写抽象函数而采取的变通方法。Python是动态语言,不需要采用这种变通方法。
Python 3引入了类型提示功能,可以使用mypy等工具通过分析类型匹配发现潜在的问题。使用类型提示比通过类型测试(即assert isinstance(a, int)
)检查参数a
的类型是否为整型值更好,因为assert
语句增加了运行时的资源开销,而运行mypy验证提示是常规质量保证环节的一部分,通常与单元测试工具pylint等配合使用,以保证软件的正确性。
函数式编程
文行至此,不难发现Python具备了函数式编程的绝大多数特征。实际上,这些函数式编程技术甚至已经广泛应用于面向对象编程中了。
作为特例,流畅的应用编程接口(API)很好体现了函数式编程的特质。在一个类中,只要花点时间在它的每个方法后面都加上return self()
,就可以编写出如下所示的代码:
some_object.foo().bar().yet_more()
或者把紧密相关的几个函数写成下面的形式:
yet_more(bar(foo(some_object)))
也就是把传统面向对象风格的后缀式语法改为偏函数式的前缀式语法。两种写法均可用于Python,不过对于有特殊意义的方法,主要用前缀形式,例如len()
函数实际上是通过class.__len__()
方法实现的。
当然,前面这些类的实现仍然可能包含有状态的对象,但即便如此,视角的微小改变也有助于我们在编程实践中灵活运用函数式方法,编写出简洁明了的程序代码。
再次强调,使用函数式编程并不意味着命令式编程存在某些严重缺陷,或者函数式编程能提供高新技术。函数式编程的精髓在于改变视角,这种改变往往有助于更好地编码。
高级概念
之后会讨论函数式编程的一些相关高级概念,这些概念在纯粹的函数式语言中是不可或缺的。由于Python并非纯函数式语言,而是一种混合式函数编程方法,所以不需要深入研究这些概念。
这部分讨论对那些熟悉函数式语言(例如Haskell)的Python初学者特别有用,因为Python处理这些问题的方式与其他语言不同。很多时候,我们会采用命令式编程的方法解决问题,而不局限于函数式方法。
这些概念如下所示。
-
引用透明性:在编译语言中,为了保证惰性求值和多种优化方法的正确性,需要确保通向同一对象有多条路径。在Python中,这一点并不是特别重要,因为Python没有编译阶段优化。
-
柯里化:类型系统通过柯里化技术把多参数函数转化为单参数函数。
-
Monad:将一系列操作灵活串联起来,构成一个纯函数。在某些场景中,可使用命令式Python代码达到相同的效果。也可以借助Python库
PyMonad
构造Monad。