为什么Python不适合函数式编程?

我一直认为函数式编程可以用Python完成。因此,我很惊讶Python在问题中没有得到太多的提及,而且当它被提及时,通常不是非常积极的。然而,没有给出太多原因(缺乏模式匹配和代数数据类型被提到)。所以我的问题是:为什么Python不适合函数式编程?除了缺乏模式匹配和代数数据类型之外,还有其他原因吗?或者这些概念对函数式编程如此重要,以至于不支持它们的语言只能被归类为二流函数式编程语言?(请记住,我在函数式编程方面的经验相当有限。)

129600 次浏览

Guido对这个在这里有一个很好的解释。以下是最相关的部分:

我从来不认为Python是 很大程度上受功能性的影响 语言,不管人们说什么 或者思考。我要熟悉得多 使用命令式语言,如C 和Algol 68,虽然我做了 函数类对象,I 不认为Python是函数式的 编程语言。然而,早些时候 很明显,用户想这样做

...

值得注意的是,即使 虽然我并没有把Python想象成 函数式语言,介绍 的闭包在 其他许多先进的开发 编程功能。例如, 新式课程的某些方面, 装饰,和其他现代功能

.

最后,尽管一些 函数式编程特性具有 已经被介绍过了,Python 仍然缺乏某些特征发现 “真正的”函数式编程 语言。例如,Python就是这样 不执行某些种类的 优化(例如,尾部递归)。 一般来说,因为Python非常 动态自然,是不可能做到的 编译时优化 众所周知的函数式语言 Haskell或ML。这很好。

我从中得出两点结论:

  1. 该语言的创造者并不真正认为Python是一种函数式语言。因此,可能会看到“函数式”的特性,但不太可能看到任何明确的功能。
  2. Python的动态特性抑制了您在其他函数式语言中看到的一些优化。当然,Lisp和Python一样是动态的(如果不是更动态的话),所以这只是部分解释。

Python几乎是一种函数式语言。它是“功能精简版”。

它有额外的特性,所以对某些人来说不够纯。

它还缺乏一些功能,因此对某些人来说还不够完整。

缺少的特性相对容易编写。查看类似的关于Python中的FP的帖子。

Scheme没有代数数据类型或模式匹配,但它肯定是一种函数式语言。从函数式编程的角度来看,Python令人讨厌的地方:

  1. λ。由于Lambdas只能包含一个表达式,并且不能在表达式上下文中轻松地做所有事情,这意味着可以“动态”定义的函数是有限的。

  2. if是语句,不是表达式。这意味着,除其他外,你不能有一个包含If的lambda。(这在Python 2.5中由三元组修复,但看起来很难看。)

  3. Guido威胁删除映射,过滤器和减少每隔一段时间

另一方面,python有词法闭包、Lambdas和列表推导式(无论Guido是否承认,这实际上是一个“函数式”概念)。我用Python做了很多“函数式”编程,但我不敢说它是理想的。

您引用的问题是哪些语言同时促进面向对象和函数式编程。Python没有促进函数式编程,尽管它作品相当好。

Python中反对函数式编程的最佳论据是Guido仔细考虑了命令式/OO用例,而函数式编程用例则不是。当我编写命令式Python时,它是我所知道的最漂亮的语言之一。当我编写函数式Python时,它变得像没有BDFL的普通语言一样丑陋和令人不快。

这并不是说它不好,只是说您必须比转换到促进函数式编程的语言或转换到编写OO Python时更加努力。

以下是我在Python中遗漏的函数性内容:


  • 没有模式匹配和尾递归意味着你的基本算法必须被强制编写。Python中的递归又丑又慢。
  • 一个小的列表库和没有函数字典意味着你必须自己写很多东西。
  • 没有用于填充或组合的语法意味着无点风格与显式传递参数一样充满标点符号。
  • 迭代器而不是惰性列表意味着你必须知道你是想要效率还是持久性,如果你想要持久性,就分散对list的调用。(迭代器只使用一次)
  • Python简单的命令式语法,以及它简单的LL1解析器,意味着基本上不可能有更好的if-expression和lambda-expression语法。圭多喜欢这样,我觉得他是对的。

我从来不会称Python为“函数式”,但无论何时我用Python编程,代码总是几乎完全是函数式的。

不可否认,这主要是由于非常好的列表理解。所以我不一定建议Python作为函数式编程语言,但我建议使用Python的人进行函数式编程。

让我用一段代码来演示,这段代码取自SO上的“功能性”Python的问题的答案

Python:

def grandKids(generation, kidsFunc, val):
layer = [val]
for i in xrange(generation):
layer = itertools.chain.from_iterable(itertools.imap(kidsFunc, layer))
return layer

Haskell:

grandKids generation kidsFunc val =
iterate (concatMap kidsFunc) [val] !! generation

这里的主要区别是Haskell的标准库有用于函数式编程的有用函数:在本例中是iterateconcat(!!)

对于这个问题(以及答案),有一件事是非常重要的: 函数式编程到底是什么,它最重要的特性是什么? 我将试着给出我的观点:

函数式编程很像在白板上写数学。当你写方程时 在白板上,您不会考虑执行命令。(通常)没有突变。 你不会在第二天回来看它,当你再次进行计算时, 你会得到不同的结果(如果你喝了一些新鲜的咖啡,你可能会得到不同的结果:))。基本上, 黑板上写的东西在那里,当你开始写的时候答案已经在那里了 事情发生了,你只是还没有意识到它是什么 函数式编程很像这样;你不需要改变,只需要评估 方程(或者在这种情况下,“程序”),并找出答案是什么。这个项目 还在那里,没有改变。

我认为函数式编程最重要的特性如下: A)参考透明度-如果你在其他时间评估相同的语句 和位置,但是相同的变量值,它的含义仍然相同。 B)没有副作用——不管你盯着白板看多久,方程都是另一个 那家伙正在看另一块白板不会不小心变了。 C)函数也是值。哪些可以传递和应用于其他 变量。 D)函数复合,你可以令h=g·f,从而定义一个新的函数h(..) 等价于调用g(f(..)).

这个列表是按我的优先顺序排列的,所以参考透明度是最重要的,

现在,如果你浏览python并检查该语言和库的支持情况, 并且保证,这些方面——那么你就在回答你自己的问题的路上走得很好

除了其他答案之外,Python和大多数其他多范式语言不适合真正的函数式编程的一个原因是,它们的编译器/虚拟机/运行时不支持函数式优化。这种类型的优化是通过编译器理解数学规则来实现的。例如,许多编程语言都支持map函数或方法。这是一个相当标准的函数,它将一个函数作为一个参数,一个可迭代对象作为第二个参数,然后将该函数应用于可迭代对象中的每个元素。

不管怎样,map( foo() , x ) * map( foo(), y )map( foo(), x * y )是一样的。后者实际上比前者快,因为前者执行两个副本,而后者执行一个副本。

更好的函数式语言能够识别这些基于数学的关系,并自动执行优化。不致力于函数式范式的语言可能无法优化。

上面没有提到的另一个原因是,许多内置类型的内置函数和方法修改了对象,但不返回修改后的对象。如果返回这些修改后的对象,将使函数代码更干净、更简洁。例如,如果some_list.append(some_object)返回附加some_object的some_list。