gpt4 book ai didi

python - 为什么用 Python 进行函数式编程?

转载 作者:行者123 更新时间:2023-12-02 17:15:32 24 4
gpt4 key购买 nike

在工作中,我们过去常常以非常标准的面向对象的方式编写 Python。最近,有几个人加入了功能性的潮流。他们的代码现在包含更多的 lambdas、maps 和 reduce。我知道函数式语言有利于并发,但是在功能上编程 Python 真的有助于并发吗?我只是想了解如果我开始使用更多 Python 的功能特性,我会得到什么。

最佳答案

编辑 :我已经在评论中承担了任务(部分,似乎是 Python 中 FP 的狂热分子,但不是唯一的),因为没有提供更多的解释/示例,因此,扩展答案以提供一些。
lambda ,更是如此map (和 filter)、尤其是reduce , 几乎都不是 Python 工作的正确工具,Python 是一种强大的多范式语言。
lambda与普通 def 相比的主要优势 (?)声明是它产生了 匿名 函数,而 def给函数一个名字——为了这个非常可疑的优势,你付出了巨大的代价(函数的主体仅限于一个表达式,产生的函数对象是不可pickle的,名称的缺失有时会使理解变得更加困难)堆栈跟踪或以其他方式调试问题——我需要继续吗?!-)。

考虑一下您有时会在“Python”(带有“恐吓引号”的 Python,因为它显然是 而不是 惯用的 Python 中使用的最愚蠢的成语——它是来自惯用的 Scheme 等的糟糕音译,只是就像在 Python 中更频繁地过度使用 OOP 是来自 Java 等的糟糕音译):

inc = lambda x: x + 1

通过将 lambda 分配给一个名称,这种方法立即抛弃了上述“优势”——并且不会失去任何 DISadvantages!例如, inc没有 知道 它的名字-- inc.__name__是无用的字符串 '<lambda>' -- 祝你好运,理解其中的一些堆栈跟踪;-)。在这种简单的情况下,实现所需语义的正确 Python 方法当然是:
def inc(x): return x + 1

现在 inc.__name__是字符串 'inc' ,因为它显然应该是,并且对象是可腌制的——语义在其他方面是相同的(在这种简单的情况下,所需的功能很适合一个简单的表达式—— def如果你需要的话,也可以很容易地重构当然,暂时或永久插入诸如 printraise 之类的语句)。
lambda是(的)表达式 while def是(部分)声明——这是让人们使用 lambda 的一点语法糖有时。许多 FP 爱好者(就像许多 OOP 和程序爱好者一样)不喜欢 Python 在表达式和语句之间的相当强的区别(对 Command-Query Separation 的一般立场的一部分)。我,我认为当你使用一种语言时,你最好“有条不紊”地使用它——就像 一样。设计 被使用——而不是与之抗争;所以我以 Pythonic 的方式编写 Python,以 Schematic (;-) 的方式编写 Scheme,以 Fortesque (?) 的方式编写 Fortran,等等:-)。

转到 reduce -- 一条评论声称 reduce是计算列表乘积的最佳方法。哦真的吗?让我们来看看...:
$ python -mtimeit -s'L=range(12,52)' 'reduce(lambda x,y: x*y, L, 1)'
100000 loops, best of 3: 18.3 usec per loop
$ python -mtimeit -s'L=range(12,52)' 'p=1' 'for x in L: p*=x'
100000 loops, best of 3: 10.5 usec per loop

所以简单的、基本的、琐碎的循环比执行任务的“最佳方式”快两倍(而且更简洁)?-) 我猜因此速度和简洁性的优势必须使琐碎循环成为“最好的” “方式,对吧?-)

通过进一步牺牲紧凑性和可读性...:
$ python -mtimeit -s'import operator; L=range(12,52)' 'reduce(operator.mul, L, 1)'
100000 loops, best of 3: 10.7 usec per loop

...我们可以得到 差不多 回到最简单、最明显、紧凑和可读的方法(简单、基本、琐碎的循环)的容易获得的性能。这指出了 lambda 的另一个问题,其实:性能!对于足够简单的运算,例如乘法,与实际执行的运算相比,函数调用的开销相当大—— reduce (以及 mapfilter )经常迫使您插入这样一个函数调用,其中简单的循环、列表推导式和生成器表达式允许内联操作的可读性、紧凑性和速度。

也许比上述“将 lambda 分配给名称”更糟糕的反习语实际上是以下反习语,例如按长度对字符串列表进行排序:
thelist.sort(key=lambda s: len(s))

而不是显而易见的、可读的、紧凑的、更快的
thelist.sort(key=len)

在这里,使用 lambda除了插入一定程度的间接性之外什么都不做——没有任何好的效果,还有很多不好的效果。

使用动机 lambda通常允许使用 mapfilter而不是一个非常可取的循环或列表理解,它可以让你在线进行简单、正常的计算;当然,您仍然需要支付“间接级别”。不得不怀疑“我应该在这里使用 listcomp 还是 map”并不是 Pythonic:只要始终使用 listcomp,当两者都适用并且您不知道选择哪个时,基于“应该有一个,并且最好只有一种明显的做某事的方式”。您经常会编写无法合理转换为映射的 listcomps(嵌套循环、 if 子句等),而没有调用 map不能明智地将其改写为 listcomp。

Python 中完美的函数式方法通常包括列表推导式、生成器表达式、 itertools 、高阶函数、各种形式的一阶函数、闭包、生成器(偶尔还有其他类型的迭代器)。
itertools ,正如评论者指出的那样,确实包括 imapifilter :区别在于,与所有 itertools 一样,它们是基于流的(如 Python 3 中的 mapfilter 内置函数,但与 Python 2 中的内置函数不同)。 itertools提供了一组相互组合良好的构建块,以及出色的性能:特别是如果您发现自己可能处理很长(甚至无限!-)的序列,那么您应该熟悉 itertools——它们的整体 chapter在文档中可以很好地阅读,而 recipes特别是很有教育意义。

编写自己的高阶函数通常很有用,尤其是当它们适合用作 decorators 时。 (函数装饰器,如文档的那部分所述,和类装饰器,在 Python 2.6 中引入)。记得使用 functools.wraps在您的函数装饰器上(以保持函数的元数据被包装)!

所以,总结一下...:任何你可以用 lambda 编码的东西, map , 和 filter ,您可以使用 def 进行编码(通常更有利) (命名函数)和 listcomps —— 通常向上移动一级到生成器、生成器表达式或 itertools ,甚至更好。 reduce符合“有吸引力的滋扰”的法律定义...:它是 几乎没有 适合这项工作的工具(这就是为什么它终于不再是 Python 3 中的内置工具了!-)。

关于python - 为什么用 Python 进行函数式编程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1892324/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com