是否有内置的 "apply" 函数(如 "lambda f: f()"),就像 Python 2 中曾经有的那样?

Is there a built-in "apply" function (like "lambda f: f()") as there used to be in Python 2?

查看 this question, I realised that it is kind of awkward to use multiprocessing's Pool.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 or something, similarly to apply/call in JavaScript (yes, I know JavaScript didn't have lambdas at the time those functions were defined, and no, I'm not saying JavaScript is an exemplary programming language, just an example). In fact, I definitely think something like this should be present in operator, but (unless my eyes deceive me) it seems to be absent. I read that in the case of the identity function 内解决方案是让人们定义他们自己的琐碎函数,我理解在那种情况下更好,因为您可能需要几个不同的变体,但我觉得这个变体有点缺失。

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

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

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

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

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

对于从 2.3 开始的任何 Python,您不仅可以简单地编写 no-argument apply,还可以编写 perfect-forwarding apply,作为 one-liner,如 the 2.x docs for apply:

中所述

The use of apply() is equivalent to function(*args, **keywords)

换句话说:

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

... 或者,作为内联 lambda:

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

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

如果你要多次使用它,我认为定义函数 out-of-line 并按名称重用它可能更清楚,但 lamdba 版本足够简单明了(更是如此对于您的 no-args 用例),我无法想象有人会抱怨它。

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


至于历史:

Python,和JavaScript一样,本来就没有lambda。 2.6 之前的版本很难找到 linkable 文档,2.3 之前的版本也很难找到,但我认为 lambda 是在 1.5 中添加的,最终达到了可以用于的地步2.2左右的完美转发。在此之前,文档建议使用apply进行转发,但在此之后,文档建议使用lambda代替apply。事实上,不再推荐使用 apply.

因此在 2.3 中,该函数已被弃用。2

在导致 3.0 的 Python-3000 次讨论中,Guido 建议所有 "functional programming" 函数除了 maybe mapfilter 是不必要的。3 其他人为 reducepartial 做了很好的案例。4 但是一个大部分原因是它们实际上写起来并不简单(以 fully-general 形式),而且很容易出错。 apply 并非如此。此外,人们能够在 real-world 代码库中找到 reducepartial 的相关用途,但任何人都能找到的 apply 的唯一用途是旧的 pre-2.3 代码。事实上,这种情况非常罕见,甚至不值得对 apply.

进行 2to3 工具转换调用

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

apply(): use f(*args, **kw) instead [2]

Guido 的一篇名为 "Python Regrets" 的文章的脚注 link,现在是 404 link。随附的 PowerPoint 演示文稿是他为其编写的演示文稿的 still available, however, or you can view an HTML flipbook。但它真正说的都是一样的 one-liner 和 IIRC,唯一进一步的讨论是 "We already effectively got rid of it in 2.3."


1.在大多数必须应用函数的惯用 Python 代码中,该函数内部的工作非常繁重。当然,在您的情况下,调用函数的开销(腌制参数并通过管道传递它们)甚至更重。重要的一种情况是当您执行 "Haskell-style functional programming" 而不是 "Lisp-style" 时——也就是说,函数定义非常少,而许多函数是通过转换函数和组合结果生成的。但这在 Python 中已经很慢了(并且 stack-heavy),这不是一件合理的事情。 (平面使用装饰器来应用一个或三个包装器效果很好,但潜在的无限包装器链会降低您的性能。)

2。正式的弃用机制还不存在,所以它只是被移动到文档中的 "Non-essential Built-in Functions" 部分。但它被追溯认为自 2.3 以来已弃用,如您在 2.7 文档中所见。

3。圭多原本连他们也想除掉;正如您在 "Regrets" 活页簿中看到的那样,争论的焦点是列表理解可以更好地完成同样的工作。但是提升 itertools.imap 代替 map 意味着它可以像新的 zip 一样变得懒惰,因此比理解更好。我不确定为什么 Guido 不只是对生成器表达式提出相同的论点。

4.我不确定 Guido 本人是否曾相信 reduce,但整个核心开发人员都相信。