【问题标题】:Is there a built-in "apply" function (like "lambda f: f()") as there used to be in Python 2?是否有像 Python 2 中那样的内置“应用”函数(如“lambda f: f()”)?
【发布时间】:2018-09-08 15:19:34
【问题描述】:

看着this question,我意识到如果要并行运行函数列表,使用multiprocessingPool.map 有点尴尬:

from multiprocessing import Pool

def my_fun1(): return 1
def my_fun2(): return 2
def my_fun3(): return 3

with Pool(3) as p:
   one, two, three = p.map(lambda f: f(), [my_fun1, my_fun2, my_fun3])

我并不是说它完全是神秘的,但我想我希望它有一些传统的名称,即使只是在 functools 或其他东西中,类似于 JavaScript 中的 apply/call(是的,我知道JavaScript 在定义这些函数时没有 lambda,不,我并不是说 JavaScript 是一种示例性编程语言,只是一个示例)。事实上,我绝对认为operator 中应该有类似的东西,但是(除非我的眼睛欺骗了我)它似乎不存在。我读到in the case of the identity function 的解决方案是让人们定义自己的琐碎功能,在这种情况下我更理解它,因为您可能想要一些不同的变体,但这对我来说感觉像是缺失的一点。

编辑:正如 cmets 中所指出的,Python 2 曾经为此目的有一个 apply 函数。

【问题讨论】:

  • Python 曾经为此内置了一个 apply,但它已被删除。原因与不添加身份的原因大致相同:自己编写 apply 及其所有合理的变体很简单,而且很少使用,所以如果你想要它,就自己写吧。
  • Pool.async_apply 在这里似乎很有用。
  • @abarnert 我明白了,我从不知道(或者忘记了)Python 2 中的 apply。我仍然认为在 operator 中是有意义的(比如 reduce 被移动了functools),我的意思是,同样的琐碎论点适用于那里的任何东西,但我是谁来质疑 BDFL。无论如何,如果您不介意发布我会接受的答案,我认为这是正确的解释。
  • @jdehesa 如果你想让我挖掘完整的历史和理由来寻找答案,我会的。但突然想到:在 2.1 左右,您无法进行完美的转发(因为 lambdas 没有关键字 splat),所以建议使用 apply。然后 2.2 修复了它,从那时起 Python 一直确保始终允许完美转发。所以没有推荐的 apply 用途,这引发了一个讨论,导致它在 2.3 中被弃用,并附有说明如何自己编写它(或它的变体)的说明。 3.0,删除它而不是移动到 functools 因为没有人使用它。
  • @chepner 是的,但是apply_async 将单个函数作为参数。你可以调用每个函数,但是如果你想将结果提取到局部函数变量中,回调可能会很奇怪......

标签: python python-3.x apply higher-order-functions built-in


【解决方案1】:

如果你做一行额外的工作,它有点在operator

>>> def foo():
...     print 'hi'
... 
>>> from operator import methodcaller
>>> call = methodcaller('__call__')
>>> call(foo)
hi

当然,call = lambda f: f() 也只有一行...

【讨论】:

  • 对,不确定一个选项是否比另一个更好,但它是lambda 的一个不错的替代方案。
【解决方案2】:

首先,我们来看看实际问题。

对于从 2.3 开始的任何 Python,您不仅可以简单地编写无参数 apply,还可以编写完美转发 apply,作为单行符,如 the 2.x docs for apply 中所述:

apply()的使用等价于function(*args, **keywords)

换句话说:

def apply(function, *args, **keywords):
    return function(*args, **keywords)

… 或者,作为内联 lambda:

lambda f, *a, **k: f(*a, **kw)

当然,C 实现要快一些,但这几乎是无关紧要的。1

如果您要多次使用它,我认为定义函数外线并通过名称重用它可能更清晰,但是 lamdba 版本足够简单和明显(对您来说更是如此no-args 用例),我无法想象有人会抱怨它。

另外,请注意,如果您了解自己在做什么,这实际上比 identity 更简单,而不是更少。使用identity,您应该使用多个参数(或关键字参数)返回什么是模棱两可的,因此您必须决定您想要哪种行为;对于apple,只有一个明显的答案,而且几乎不可能出错。


至于历史:

Python 和 JavaScript 一样,最初没有 lambda。在 2.6 之前的版本中很难挖掘可链接的文档,甚至在 2.3 之前也很难找到它们,但我认为 lambda 是在 1.5 中添加的,最终达到了可以在 2.2 左右完美转发的地步。在此之前,文档建议使用apply 进行转发,但在那之后,文档建议使用lambda 代替apply。事实上,apply 已不再被推荐使用。

所以在 2.3 中,该功能已被弃用。2

在导致 3.0 的 Python-3000 讨论期间,Guido 建议除了 可能mapfilter 之外的所有“函数式编程”函数都是不必要的。3 sup> 其他人为 reducepartial 提供了很好的案例。4 但案例的很大一部分是它们实际上并不简单(以完全一般的形式),而且很容易弄错了。 apply 并非如此。此外,人们能够在实际代码库中找到reducepartial 的相关用途,但任何人都可以找到的apply 的唯一用途是2.3 之前的旧代码。事实上,这种情况非常罕见,甚至不值得将2to3 工具转换调用为apply

删除它的最终理由总结在PEP 3100

apply():改用f(*args, **kw) [2]

该脚注链接到 Guido 的一篇名为“Python Regrets”的文章,现在是 404 链接。但是,随附的 PowerPoint 演示文稿是 still available,或者您可以查看他为其编写的演示文稿的 an HTML flipbook。但它真正说的只是同一条线,而 IIRC,唯一进一步的讨论是“我们已经在 2.3 中有效地摆脱了它。”


1。在大多数必须应用函数的惯用 Python 代码中,该函数内部的工作非常繁重。当然,在您的情况下,调用函数(腌制参数并将它们通过管道传递)的开销甚至更大。重要的一种情况是当你在做“Haskell 风格的函数式编程”而不是“Lisp 风格的”时——也就是说,只有很少的函数定义,而有很多函数是通过转换函数和组合结果来实现的。但这在 Python 中已经很慢(而且堆栈繁重),所以这不是一件合理的事情。 (平面使用装饰器来应用一个或三个包装器效果很好,但是可能无限的包装器链会影响您的性能。)

2。正式的弃用机制尚不存在,因此它只是移至文档中的“非必要内置函数”部分。但从 2.3 开始,它被追溯认为已弃用,正如您在 2.7 文档中看到的那样。

3。圭多本来连他们都想干掉的;论点是列表推导可以更好地完成相同的工作,正如您在“遗憾”翻书中看到的那样。但是推广itertools.imap 代替map 意味着它可以变得懒惰,就像新的zip 一样,因此比理解更好。我不确定为什么 Guido 不只是对生成器表达式提出相同的论点。

4.我不确定 Guido 本人是否曾被 reduce 说服,但核心开发人员作为一个整体是。

【讨论】:

  • 太棒了,这是百科全书。我不知道对高阶函数有这样的“敌意”!我很高兴他们留下来了,在某些情况下,我仍然发现使用 map 更惯用(不太像 filter),尽管我看到它可能与“一种明显的方法”相悖。
  • @jdehesa Guido 的立场常常被夸大了。我认为最好的表达方式不是他讨厌 HOF,而是他认为理想的语言应该具有使常用 HOF 变得不必要的语法。一旦你发现列表推导式(在 Haskell 中,HOF 就是一切),就不难相信理想是可以实现的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-13
  • 2011-09-21
  • 2019-08-29
  • 1970-01-01
  • 1970-01-01
  • 2012-12-11
  • 2013-04-02
相关资源
最近更新 更多