当前位置: 首页 > 面试题库 >

为什么要用Python功能编程?

楚宏胜
2023-03-14
问题内容

在工作中,我们曾经以非常标准的OO方式对Python进行编程。最近,有几个人加入了实用的潮流行列。现在,他们的代码包含更多的lambda,映射和简化。我了解功能语言对并发性有好处,但对Python进行功能编程真的对并发有所帮助吗?我只是想了解如果我开始使用Python的更多功能,将会得到什么。


问题答案:

编辑 :由于没有提供更多的解释/示例,我被注释中的任务(部分地,似乎是由FP的狂热者(但不是排他性的))提供的,因此扩大了答案以提供一些解释/示例。

lambda,更是这样map(和filter),
最特别的是reduce,在几乎没有在Python中,这是一个强烈的多范式语言工作的工具。

lambda与普通def语句相比,主要优点(?)是它可以创建一个 匿名 函数,同时def为该函数提供一个名称-
而对于这一非常可疑的优点,您将付出巨大的代价(该函数的主体仅限于一个表达式,即结果函数对象不是可腌制的,缺少名称有时会使理解堆栈跟踪或以其他方式调试问题变得更加困难-
我需要继续吗?!-)。

考虑一下您有时会在“ Python”中使用的最愚蠢的惯用法(带有“吓人的引号”的Python,因为它显然 不是 惯用的Python-
从惯用Scheme之类的音译中很糟糕,就像更频繁地过度使用) Python中的OOP是Java或类似语言的错误音译):

inc = lambda x: x + 1

通过为lambda分配名称,此方法将立即丢弃上述“优点”,并且不会丢失任何缺点!例如,inc知道
它的名字-inc.__name__是无用的字符串'<lambda>'-祝您好运,了解其中的一些堆栈跟踪;-)。在这种简单情况下,实现所需语义的正确Python方法当然是:

def inc(x): return x + 1

现在 inc.__name__是字符串'inc',这显然是应该的,并且该对象是可腌制的-
语义是相同的(在这种简单的情况下,所需的功能可以轻松地用一个简单的表达式表示)-def如果需要,它也可以很容易地进行重构暂时或永久插入语句,例如printraise)。

lambda是一个表达式的def一部分,而是一个语句的一部分-
这是使人们lambda有时使用的语法糖的一点点。许多FP爱好者(与许多OOP和程序迷一样)不喜欢Python在表达式和语句之间的合理区分(这是对Command-
Query Separation的一般立场的一部分)。我,我认为,当您使用一种语言时,最好不要“杂乱无章”地使用它- 设计 要使用的方式-
而不是与之抗争。因此我以Python方式编程Python,以示意图(;-)方式编程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(andmapfilter)通常会迫使您在简单循环,列表推导和生成器表达式中插入此类函数调用允许在线操作的可读性,紧凑性和速度。

也许比上述“为名称分配lambda”反习惯用语更糟,实际上它是以下反习惯用语,例如,按字符串的长度对列表进行排序:

thelist.sort(key=lambda s: len(s))

而不是明显的,可读的,紧凑的,更快的

thelist.sort(key=len)

在这里,使用lambda只是插入了一定程度的间接操作-什么都没有产生任何效果,也有很多不好的效果。

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

在Python中,完全正确的函数方法通常包括列表理解,生成器表达式,itertools高阶函数,各种伪装的一阶函数,闭包,生成器(有时还包括其他种类的迭代器)。

itertools作为评论者指出的那样,不包括imapifilter:所不同的是,像所有itertools,这些是基于流的(像mapfilter在Python
3建宏,但不同于那些内建在Python 2)。
itertools提供了一组相互融合且性能出色的构建块:尤其是如果您发现自己可能处理很长(甚至是无穷大!)的序列,则应归功于自己熟悉itertools-
它们的整体文档中的章节有助于您阅读,尤其是食谱很有启发性。

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

因此,总结一下……:可以使用lambdamap和进行编码的任何事情filter,都可以使用def(命名函数)和listcomps进行编码(通常不是很方便),并且通常将一个缺口移到生成器,生成器表达式或上itertools。更好。
reduce符合“有吸引力的滋扰”的法律定义…: 几乎从来都不是 适合该工作的工具(这就是为什么它最终不再是Python
3中的内置工具!)。



 类似资料:
  • 问题内容: 为什么要编译Python脚本?您可以直接从.py文件运行它们,并且效果很好,那么在性能上有什么优势吗? 我还注意到,我的应用程序中的某些文件被编译为.pyc,而另一些则没有,为什么? 问题答案: 它被编译为字节码,可以更快,更快速地使用。 无法编译某些文件的原因是,每次运行脚本时都会重新编译与之一起调用的主脚本。所有导入的脚本将被编译并存储在磁盘上。 Ben Blank的 重要补充:

  • 问题内容: 我认为这个问题已经存在,但是我找不到。 我不明白,为什么必须要有一个功能接口才能使用lambda。考虑以下示例: 这可以正常工作,但是如果您取消注释行,则不会。为什么?以我的理解,编译器应该能够区分这两种方法,因为它们具有不同的输入参数。为什么我需要一个功能接口并炸毁我的代码? 编辑:链接的重复项没有回答我的问题,因为我在询问不同的方法参数。但是在这里,我得到了一些非常有用的答案,这要

  • 问题内容: 我不明白为什么Python没有功能。它有一个内置的(我认为是姐姐),但没有。 在python 2.6中甚至有一个函数(在math中),但是没有符号。当您可以只写一个然后直接从中获取时,为什么还要写一个?后者会更清楚:x带有y的符号,而带copysign的则必须记住它是x带有y的符号还是y带有x的符号! 显然,除了,它不提供任何其他功能,但是它也将更具可读性(对于像python这样的高度

  • 我正在阅读关于功能更新的React Hook留档,请参阅以下引用: “”和“-”按钮使用函数形式,因为更新的值基于以前的值 但是我看不出需要功能更新的目的是什么,它们和直接使用旧状态计算新状态有什么区别。 为什么React-useState钩子的更新程序函数需要函数更新表单?在哪些示例中,我们可以清楚地看到差异(因此使用直接更新会导致错误)? 例如,如果我从文档中更改此示例 要直接更新: 我看不到

  • 本文向大家介绍python 为什么说eval要慎用,包括了python 为什么说eval要慎用的使用技巧和注意事项,需要的朋友参考一下 eval前言 当内存中的内置模块含有os的话,eval同样可以做到命令执行: 当然,eval只能执行Python的表达式类型的代码,不能直接用它进行import操作,但exec可以。如果非要使用eval进行import,则使用__import__: 在实际的代码中

  • 本文向大家介绍为什么要用 redis ?为什么要用缓存?相关面试题,主要包含被问及为什么要用 redis ?为什么要用缓存?时的应答技巧和注意事项,需要的朋友参考一下 主要从“高性能”和“高并发”这两点来看待这个问题。 高性能: 假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓