为什么Python对于函数式编程不是很好?[关闭]


324

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


2
2018年-Coconut (一种可编译为Python的功能编程语言)增强了Python中的功能编程。另见本系列产品由IBM文章第1页 第2页 第3页
cssyphus

Answers:


393

您所参考的问题将询问哪些语言同时促进OO和功能编程。即使Python 运作良好,它也不会促进函数式编程。

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

这并不是说这很糟糕,只是您必须比您改用促进功能编程的语言或改用编写OO Python的语言更加努力。

以下是我在Python中缺少的功能:


  • 没有模式匹配,也没有尾递归,这意味着必须强制性地编写基本算法。递归在Python中很丑陋而且很慢。
  • 一个小的列表库,没有功能词典,这意味着您必须自己编写很多东西。
  • 没有用于currying或composition的语法,这意味着无点样式几乎像显式传递参数一样充满标点符号。
  • 迭代器而不是惰性列表意味着您必须知道是要效率还是持久性,并且list如果需要持久性,则需要分散调用。(迭代器只能使用一次)
  • Python的简单命令式语法及其简单的LL1解析器意味着,基本上不可能为if-expressions和lambda-expressions提供更好的语法。Guido喜欢这种方式,我认为他是对的。

5
+1表示缺少尾部递归-尽管循环结构已取代了它,但在Python和Scheme之间仍然值得缺失。
new123456 2011年

5
关于完整性和组成的极好的答案。遗憾的是,就像具有如此强大功能背景的众多答案一样,它对IMO术语的滥用也是如此。虽然我知道您无法在答案中详细说明每个概念,但我想知道在阅读“模式匹配”,“功能词典”,“ currying”或“懒惰列表”。
ThomasH 2011年

4
好点子; 我认为解决方案是添加链接。您是否有足够的代表来编辑我的答案?如果是这样,请随时添加指向各种概念的链接。待会儿我会开始的。
内森·希夫利·桑德斯

5
我意识到这已经有5年的历史了,但是……这似乎更多的是您想从Haskell错过的事情,而不是功能语言。例如,大多数ML和Lisp的方言和后代都没有自动循环显示,使无点样式过于冗长,没有惰性列表等。因此,如果迭代器而不是惰性列表使Python成为不良的功能语言,不必使CaML成为可怕的功能语言吗?
2014年

4
@abarnert:除了懒惰列表(可作为库使用)以外,Caml的所有要点。在撰写此答案时,我偶尔使用了Caml,目前使用的是F#。它们都是非常好的函数式语言。
内森·希夫利·桑德斯

102

Guido 在这里对此有很好的解释。这是最相关的部分:

我从来没有考虑过Python受功能语言的严重影响,无论人们怎么说。我对诸如C和Algol 68之类的命令式语言更加熟悉,尽管我使函数成为一流的对象,但我并不认为Python是一种函数式编程语言。但是,很早以前,很明显,用户希望对列表和函数做更多的事情。

...

还值得注意的是,即使我没有将Python设想为一种功能语言,闭包的引入对于许多其他高级编程功能的开发也很有用。例如,新型类,装饰器和其他现代功能的某些方面都依赖此功能。

最后,尽管这些年来已经引入了许多功能编程功能,但是Python仍然缺少“真实”功能编程语言中的某些功能。例如,Python不执行某些类型的优化(例如,尾部递归)。通常,由于Python具有极强的动态特性,因此无法进行从Haskell或ML之类的功能语言中已知的那种编译时优化。很好。

我从中得出两点:

  1. 该语言的创建者并没有真正将Python视为一种功能语言。因此,有可能看到“功能式”功能,但是您不太可能看到任何确定的功能。
  2. Python的动态特性抑制了您在其他功能语言中看到的某些优化。诚然,Lisp与Python一样动态(如果不是更动态的话),因此这只是部分解释。

8
您可以在Python中进行尾调用优化。吉多不/不知道这一点。
Jules

26
归结为Guido van Rossum不喜欢功能样式。
Svante,2009年

59
我认为说Guido van Rossum不了解功能样式,也不了解Python为什么需要它们是更准确的说法。您必须了解两件事:1)编程语言位于技术堆栈的底部,并且会影响基于它们的所有内容; 2)与其他任何软件一样,添加功能比删除它们更容易。因此,我认为对于语言设计人员来说,批评此类要求是一种很好的素质。
杰森·贝克

8
“理所当然,Lisp同样具有活力”->也是必须的!
pyon

6
@Jules,您介意分享有关在Python中使用尾部调用优化的指南吗?指向某些源的指针将很有用。
David Shaked

52

Scheme没有代数数据类型或模式匹配,但肯定是一种功能语言。从功能编程的角度来看,有关Python的烦人的事情:

  1. 残缺的Lambdas。由于Lambda仅包含一个表达式,并且您无法在表达式上下文中轻松地进行所有操作,因此这意味着您可以“即时”定义的功能受到限制。

  2. 如果是语句,而不是表达式。这意味着,除其他事项外,您不能在其中带有If的lambda。(这在Python 2.5中由三元数修复,但看起来很丑。)

  3. 圭多威胁要删除地图,过滤器,并减少在每过一段时间

另一方面,python具有词汇闭包,Lambda和列表理解(无论Guido是否接受,这实际上都是“功能性”概念)。我在Python中进行了大量的“函数式”编程,但是我很难说它是理想的。


3
python确实不需要映射,过滤和缩减。我还没有看到一段通过使用它们而被简化的代码。另外,在Python中调用函数可能会很昂贵,因此通常最好还是使用列表/生成器理解或for循环。
杰森·贝克

2
内森·桑德斯(Nathan Sanders)正是这样说的:“ Python不能很好地促进函数式编程,即使它运行得很好。” 如果Guido希望Python成为一种功能语言,那么他将使实现工作得足够好以使用一次性函数,并且将Lambda分解为可以实际使用有用的方式使用map / filter / reduce的程度。另一方面,功能强大的人们开始意识到列表理解的威力。希望我们不必选择一个。
雅各布·B

7
map和filter被列表推导简单地替换了。降低-几乎总是-降低效率,以至于应将其替换为生成器函数。
S.Lott

13
@ S.Lott如何用发电机代替reduce?
锑2013年

17
@JacobB列表推导在Python发明15年之前和Python获得该功能的实现25年之前就已经以函数式语言提供。认为Python影响了它们的传播,或者fp从Python那里学到了这一点,或者仅仅因为它在fp世界中的流行早于Python实现,这一想法是错误的。Python的实现直接来自Haskell。也许我误会了你,这不是你的意思,但是我对“职能人员开始意识到列表理解的威力”感到困惑。
itsbruce 2014年

23

我永远都不会将Python称为“功能性的”,但每当我使用Python进行编程时,代码总是以几乎纯粹的功能性结尾。

诚然,这主要是由于列表理解非常好。因此,我不一定会建议将Python作为一种功能编程语言,但我建议对使用Python的任何人进行功能编程。


17

让我用从SO上的“功能性” 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(!!)


7
这里有一个单行替代grandKids()体生成器表达式:return reduce(lambda a, v: concat((x for x in kidsFunc(v)) for v in a), xrange(generation), [val])
Lloeki 2011年

6
这是不需要的concatreturn reduce(lambda a, v: (x for v in a for x in kidsFunc(v)), xrange(generation), [val])
Lloeki 2011年

9
@Lloeki:迭代可以显着简化该代码,并且(xx for v in kidsFunc(v)中的for x)与concatMap(kidsFunc)相比更加清晰。与Haskell相比,Python缺少不错的高阶内置函数使等效代码变得晦涩难懂。
Phob 2012年

2
concat可以替换为itertools.chain.from_iterable
锑2013年

@锑:很高兴知道。thx
yairchu

14

对于这个问题(和答案)而言,真正重要的一件事是:函数式编程到底是什么,它的最重要特性是什么。我将尽我的看法:

函数式编程很像在白板上写数学。在白板上写方程式时,您无需考虑执行顺序。(通常)没有突变。您不会在第二天回来查看它,并且当您再次进行计算时,您会得到不同的结果(或者,如果您喝了一些新鲜的咖啡,则可以:)。基本上,板子上有什么,当您开始写下内容时,答案已经在那里,您只是还没有意识到它到底是什么。

函数式编程非常像这样。您不会改变任何事情,只需评估方程式(在本例中为“程序”),然后找出答案是什么。该程序仍然存在,未经修改。与数据相同。

我将以下内容列为函数式编程的最重要特征:a)引用透明性-如果您在其他时间和地点评估相同的语句,但具有相同的变量值,则其含义仍相同。b)没有副作用-不管您凝视白板多长时间,另一个人看着另一个白板的方程式都不会意外改变。c)函数也是值。可以传递给其他变量或与其他变量一起应用。d)函数组合,您可以执行h = g·f,从而定义一个新函数h(..),该函数等效于调用g(f(..))。

此列表按我的优先顺序排列,因此参照透明性是最重要的,其次没有副作用。

现在,如果您通过python检查语言和库在这些方面的支持和保证程度,那么您就可以很好地回答自己的问题了。


2
函数在Python中是一流的。
卡尔·史密斯

@CarlSmith那一个,但留下了3/4 Python所没有的。:-\
arya '16

1
我认为Python不是函数式编程的好语言。老实说,我现在甚至不确定那句话的意思。它似乎与答案无关。我将其删除,但随后您的评论将脱离上下文。
卡尔·史密斯

1
引用透明性和不变性并不是真正的语言功能。是的,某些语言(Haskell)会强调它们,并使其难以拥有,但是您可以使用任何语言编写参照透明函数或不可变对象。您只需要解决标准库,这经常会违反它们。
凯文

此外,Python同时支持currying和composition,但在语言级别上却不支持,而是在标准库中。
凯文

10

Python几乎是一种功能语言。这是“功能精简版”。

它具有额外的功能,因此对于某些人来说还不够纯粹。

它还缺少一些功能,因此对于某些功能来说还不够完善。

缺少的功能相对容易编写。在Python中的FP上查看此类帖子。


2
在大多数情况下,我同意这篇文章。但是我不同意说Python是一种功能语言。它极大地鼓励了命令式编程,并且很难不提到您提到的“额外功能”。我认为最好像在另一篇文章中一样将Python称为“功能精简版”。:-)
杰森·贝克

8
-1对不起,不。我的意思是,只是,没有。功能语言提供了有助于形式推理的构造:归纳(ML),等式(Haskell)。闭包和匿名函数本身仅仅是策略模式的语法糖。
pyon

8

上面未提及的另一个原因是,许多内置类型的内置函数和方法会修改对象,但不会返回修改后的对象。如果返回那些修改后的对象,那将使功能代码更简洁。例如,如果some_list.append(some_object)返回添加了some_object的some_list。


4

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

无论如何,结果map( foo() , x ) * map( foo(), y )与相同map( foo(), x * y )。后者的情况实际上比前者快,因为前者执行两份副本,后者执行一份。

更好的功能语言会识别这些基于数学的关系并自动执行优化。非专用于功能范例的语言可能不会进行优化。


map( foo() , x ) * map( foo(), y ) == map( foo(), x * y )并非所有功能都正确。例如,考虑foo计算导数的情况。
Eli Korvigo

1
我想他的意思+不是*
user1747134 '16

您假设foo()是线性的?
胡安

对于foo(x)= x + 1而言,该属性为true。如(x + 1)*(y + 1)!= x * y + 1
Juan Isaza
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.