【问题标题】:Why isn't Python very good for functional programming? [closed]为什么 Python 不适合函数式编程? [关闭]
【发布时间】:2010-11-04 07:04:35
【问题描述】:

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

【问题讨论】:

  • 2018 - Coconut (一种可编译为 Python 的函数式编程语言) 增强了 Python 中的函数式编程。另请参阅 IBM page1 page2 page3 的本系列文章

标签: python functional-programming


【解决方案1】:

您引用的问题询问哪些语言促进了 OO 和函数式编程。 Python 并不促进函数式编程,尽管它工作相当好。

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

这并不是说它不好,只是你必须比你改用一种促进函数式编程的语言或改用编写 OO Python 的语言更加努力。

以下是我在 Python 中错过的功能性东西:


  • 没有模式匹配和尾递归意味着您的基本算法必须以命令方式编写。 Python 中的递归既丑陋又缓慢。
  • 一个小的列表库和没有功能字典意味着你必须自己写很多东西。
  • 没有用于柯里化或组合的语法意味着无点样式与显式传递参数一样充满标点符号。
  • 迭代器而不是惰性列表意味着你必须知道你想要效率还是持久性,如果你想要持久性,就分散对list的调用。 (迭代器只能使用一次)
  • Python 简单的命令式语法,连同其简单的 LL1 解析器,意味着 if 表达式和 lambda 表达式的更好语法基本上是不可能的。 Guido 喜欢这种方式,我认为他是对的。

【讨论】:

  • +1 表示缺少尾递归 - 尽管循环结构已经取代了它,但它仍然值得在 Python 和 Scheme 之间使用。
  • 关于完整性和组成的优秀答案。唉,就像有这么多具有​​强大功能背景的答案一样,它滥用了 IMO 术语。虽然我知道您无法在答案中详细说明每个概念,但我想知道 OP(承认有限的 FP 背景)在阅读“模式匹配”、“功能字典”、“currying”或“惰性列表”。
  • 好点;我认为解决方案是添加链接。你有足够的代表来编辑我的答案吗?如果是这样,请随意添加各种概念的链接。以后有时间我会开始的。
  • 我知道这已经 5 年了,但是……这似乎更多是关于你错过的Haskell 的东西,而不是函数式语言 的东西。例如,大多数 ML 和 Lisp 方言和后代没有自动柯里化,使无点样式过于冗长,没有惰性列表等。因此,如果迭代器而不是惰性列表使 Python 成为一种糟糕的函数式语言,那么两者都必须让 CaML 成为一种糟糕的函数式语言?
  • @abarnert:Caml 拥有除惰性列表之外的所有要点,惰性列表可作为库使用。我在写这个答案时偶尔使用 Caml,目前使用 F#。它们都是非常好的函数式语言。
【解决方案2】:

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

我从不认为 Python 是 受功能影响很大 语言,不管人们怎么说 或认为。我更熟悉 使用命令式语言,例如 C 和 Algol 68 虽然我做了 函数一流的对象,我 没有将 Python 视为函数式 编程语言。然而,早些时候 on,很明显,用户想要 使用列表和函数做更多事情。

...

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

最后,尽管有一些 函数式编程特性有 多年来被引入,Python 仍然缺乏在 “真正的”函数式编程 语言。例如,Python 确实 不执行某些类型的 优化(例如,尾递归)。 一般来说,因为 Python 的极 动态性质,这是不可能的 编译时优化的类型 从函数式语言中知道 Haskell 或 ML。这很好。

我从中得出两点:

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

【讨论】:

  • 你可以在 Python 中进行尾调用优化。 Guido 不明白/不明白这一点。
  • 这似乎归结为 Guido van Rossum 不喜欢 功能风格。
  • 我认为更准确的说法是 Guido van Rossum 不懂函数式风格,也不懂 Python 为什么需要它们。您必须了解两件事:1) 编程语言位于技术堆栈的底部并影响基于它们构建的所有内容;2) 就像任何其他软件一样,添加功能比删除它们更容易。所以我认为语言设计师对这类请求持批评态度是一种很好的品质。
  • “当然,Lisp 也是动态的”--> 也同样重要!
  • @Jules,你介意分享一个在 Python 中使用尾调用优化的指南吗?指向某个来源的指针会很有用。
【解决方案3】:

Scheme 没有代数数据类型或模式匹配,但它肯定是一种函数式语言。从函数式编程的角度来看,关于 Python 的恼人之处:

  1. 残废的 Lambda。由于 Lambda 只能包含一个表达式,并且您不能在表达式上下文中轻松完成所有操作,这意味着您可以“动态”定义的函数是有限的。

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

  3. Guido 每隔一段时间就威胁remove map, filter, and reduce

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

【讨论】:

  • 在 python 中确实不需要映射、过滤和归约。我还没有看到一段代码通过使用它们得到了极大的简化。另外,在 Python 中调用函数可能很昂贵,因此通常最好还是使用列表/生成器推导式或 for 循环。
  • 这正是 Nathan Sanders 在下面所说的:“Python 并不提倡函数式编程,尽管它工作得很好。”如果 Guido 希望 Python 成为一种函数式语言,他会让实现工作得足够好,可以使用一次性函数,并且会在一定程度上削弱 Lambda,让您可以以有用的方式实际使用 map/filter/reduce。另一方面,功能型人员开始意识到列表推导的威力。希望我们不必选择其中之一。
  • map 和 filter 被列表理解替换。 reduce——几乎总是——效率太低,应该用生成器函数代替。
  • @S.Lott 如何用生成器替换 reduce?
  • @JacobB 列表推导式在 Python 发明之前大约 15 年和 Python 实现该功能的 25 年之前在函数式语言中可用。 Python 影响了它们的传播,或者 fp 从 Python 中学到了这一点,或者甚至只是认为它在 fp 世界中流行于 Python 实现之后的想法,都是完全错误的。 Python 的实现直接取自 Haskell。也许我误解了你,这不是你的意思,但我对“功能人员开始意识到列表推导的真棒”感到困惑。
【解决方案4】:

我永远不会称 Python 为“函数式”,但每当我用 Python 编程时,代码最终总是几乎是纯函数式的。

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

【讨论】:

  • 列表推导在两个层面上已经很棘手:三个是不可读的。使用支持functionalsbuilders、链接和lambda 的语言并没有被削弱(如上面另一个答案中提到的),我们可以按顺序放置任意数量的操作。这在python 没有晦涩的第三方库是不可能的:即使使用它们仍然是有限的(由于残废的lambdas)。见stackoverflow.com/questions/49001986/…
【解决方案5】:

让我用一段代码来演示一下 SO 上“功能性”Python question 的答案

Python:

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

哈斯克尔:

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

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

【讨论】:

  • 这里用生成器表达式替换grandKids() 正文:return reduce(lambda a, v: concat((x for x in kidsFunc(v)) for v in a), xrange(generation), [val])
  • 这里也不需要concatreturn reduce(lambda a, v: (x for v in a for x in kidsFunc(v)), xrange(generation), [val])
  • @Lloeki:迭代会显着简化代码,并且 (x for v in a for x in kidsFunc(v)) 比 concatMap(kidsFunc) 更清晰。与 Haskell 相比,Python 缺乏良好的高阶内置函数,这使得等效代码变得晦涩冗长。
  • concat 可以替换为itertools.chain.from_iterable
  • @Antimony:很高兴知道。谢谢
【解决方案6】:

对于这个问题(以及答案)来说,真正重要的一点是: 函数式编程到底是什么,它最重要的属性是什么。 我会尝试给出我的看法:

函数式编程很像在白板上写数学。当你写方程 在白板上,您不会考虑执行顺序。 (通常)没有突变。 你不会在后天回来查看它,当你再次进行计算时, 你会得到不同的结果(或者你可能会,如果你有一些新鲜的咖啡:))。基本上, 黑板上的东西就在那里,当你开始写作的时候答案就已经在那里了 事情发生了,你只是还没有意识到它是什么。

函数式编程很像这样;你不改变事情,你只是评估 方程(或者在这种情况下,“程序”)并找出答案是什么。该程序 仍然存在,未修改。数据也是一样。

我将以下列为函数式编程最重要的特性: a) 参考透明度——如果您在其他时间评估相同的陈述 和地点,但具有相同的变量值,它仍然意味着相同。 b) 没有副作用——不管你盯着白板多久,等式是另一个 家伙正在看另一个白板不会意外改变。 c) 函数也是值。可以传递并与其他人一起应用或应用于其他人 变量。 d) 函数组合,你可以做 h=g·f 从而定义一个新函数 h(..) 它是 相当于调用 g(f(..))。

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

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

【讨论】:

  • 函数在Python中是一流的。
  • @CarlSmith 那个,但留下了 Python 没有的 3/4。 :-\
  • 我不认为 Python 是函数式编程的好语言。老实说,我现在什至不确定我所说的评论是什么意思。它似乎与答案无关。我会删除它,但你的评论会断章取意。
  • 引用透明和不变性并不是真正的语言特性。是的,一些语言(Haskell)强调它们并且很难没有它们,但是您可以在基本上任何语言中创建一个引用透明的函数或一个不可变的对象。您只需要绕过标准库,这通常会违反它们。
  • 另外,Python 支持柯里化和组合,但不在语言级别,而是在标准库中。
【解决方案7】:

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

它有额外的功能,所以它对某些人来说不够纯净。

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

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

【讨论】:

  • 在大多数情况下,我同意这篇文章。但我不能同意说 Python 是一种函数式语言。它非常鼓励命令式编程,并且很难不使用您提到的“额外功能”。我认为最好将 Python 称为“功能精简版”,就像您在另一篇文章中所做的那样。 :-)
  • -1 对不起,不。我的意思是,只是,不。函数式语言提供了便于形式推理的构造:归纳式 (ML)、等式 (Haskell)。闭包和匿名函数只是策略模式的语法糖。
【解决方案8】:

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

【讨论】:

  • append()extend() 不返回原始列表是一个令人头疼的问题
【解决方案9】:

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

无论如何,map( foo() , x ) * map( foo(), y )map( foo(), x * y ) 相同。后一种情况实际上比前一种更快,因为前者执行两个副本,而后者执行一个。

更好的函数式语言可以识别这些基于数学的关系并自动执行优化。不专注于函数范式的语言可能不会优化。

【讨论】:

  • map( foo() , x ) * map( foo(), y ) == map( foo(), x * y ) 并非适用于所有函数。例如,考虑foo 计算导数的情况。
  • 我认为他的意思是 + 而不是 *
  • 你假设 foo() 是线性的?
  • 对于 foo(x) = x+1,该属性不成立。作为 (x+1)*(y+1) != x*y + 1。
猜你喜欢
  • 2023-03-02
  • 2010-09-17
  • 1970-01-01
  • 2010-09-12
  • 2019-11-03
  • 2010-09-06
  • 2010-12-05
  • 2014-12-05
相关资源
最近更新 更多