218

部分应用很酷。提供了哪些functools.partial您无法通过 lambdas 获得的功能?

>>> sum = lambda x, y : x + y
>>> sum(1, 2)
3
>>> incr = lambda y : sum(1, y)
>>> incr(2)
3
>>> def sum2(x, y):
    return x + y

>>> incr2 = functools.partial(sum2, 1)
>>> incr2(4)
5

functools以某种方式更有效,还是更易读?

4

6 回答 6

293

提供了哪些functools.partial您无法通过 lambdas 获得的功能?

在额外的功能方面并不多(但是,见后文)——而且,可读性是旁观者的眼中。
大多数熟悉函数式编程语言的人(尤其是 Lisp/Scheme 家族的人)似乎都喜欢lambda——我说“大多数”,绝对不是全部,因为 Guido 和我肯定属于那些“熟悉”的人(等等) 却被认为是lambdaPython 中令人眼花缭乱的异常......
他后悔曾经将它接受到 Python 中,同时计划将其从 Python 3 中删除,作为“Python 的故障”之一。
我完全支持他。(我喜欢lambda Scheme ......虽然它在 Python中的局限性,以及它只是没有的奇怪方式'用其余的语言,让我的皮肤爬行)。

然而,对于成群的lambda爱好者来说并非如此——他们上演了 Python 历史上最接近叛乱的事情之一,直到 Guido 退缩并决定 离开lambda。等)没有发生(以避免明确复制更多的功能),尽管当然仍然存在(这不是完全重复,也不是令人眼花缭乱的)。
functoolslambdapartial

请记住,lambda的 body 仅限于一个表达式,所以它有限制。例如...:

>>> import functools
>>> f = functools.partial(int, base=2)
>>> f.args
()
>>> f.func
<type 'int'>
>>> f.keywords
{'base': 2}
>>> 

functools.partial的返回函数装饰有对内省有用的属性——它包装的函数,以及它在其中修复的位置和命名参数。此外,命名参数可以立即被覆盖(在某种意义上,“修复”是默认值的设置):

>>> f('23', base=10)
23

因此,如您所见,它绝对不像lambda s: int(s, base=2)!-)

是的,你可以扭曲你的 lambda 来给你一些这个——例如,对于关键字覆盖,

>>> f = lambda s, **k: int(s, **dict({'base': 2}, **k))

但我非常希望即使是最热心的lambda恋人也不会认为这种恐怖比partial电话更具可读性!-)。“属性设置”部分更加困难,因为 Python 的“主体是单个表达式”的限制lambda(加上赋值永远不能成为 Python 表达式的一部分的事实)......你最终会“在表达式中伪造赋值”通过将列表理解远远超出其设计限制......:

>>> f = [f for f in (lambda f: int(s, base=2),)
           if setattr(f, 'keywords', {'base': 2}) is None][0]

现在将命名参数的可覆盖性以及三个属性的设置组合到一个表达式中,然后告诉我它的可读性如何......

于 2010-07-15T04:16:21.527 回答
94

好吧,这是一个显示差异的示例:

In [132]: sum = lambda x, y: x + y

In [133]: n = 5

In [134]: incr = lambda y: sum(n, y)

In [135]: incr2 = partial(sum, n)

In [136]: print incr(3), incr2(3)
8 8

In [137]: n = 9

In [138]: print incr(3), incr2(3)
12 8

Ivan Moore 的这些帖子扩展了“lambda 的限制”和 python 中的闭包:

于 2010-07-15T04:03:37.813 回答
27

在最新版本的 Python (>=2.7) 中,您可以picklea partial,但不能 a lambda

>>> pickle.dumps(partial(int))
'cfunctools\npartial\np0\n(c__builtin__\nint\np1\ntp2\nRp3\n(g1\n(tNNtp4\nb.'
>>> pickle.dumps(lambda x: int(x))
Traceback (most recent call last):
  File "<ipython-input-11-e32d5a050739>", line 1, in <module>
    pickle.dumps(lambda x: int(x))
  File "/usr/lib/python2.7/pickle.py", line 1374, in dumps
    Pickler(file, protocol).dump(obj)
  File "/usr/lib/python2.7/pickle.py", line 224, in dump
    self.save(obj)
  File "/usr/lib/python2.7/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.7/pickle.py", line 748, in save_global
    (obj, module, name))
PicklingError: Can't pickle <function <lambda> at 0x1729aa0>: it's not found as __main__.<lambda>
于 2013-10-09T17:41:20.113 回答
24

functools 是否更有效..?

作为对此的部分回答,我决定测试性能。这是我的例子:

from functools import partial
import time, math

def make_lambda():
    x = 1.3
    return lambda: math.sin(x)

def make_partial():
    x = 1.3
    return partial(math.sin, x)

Iter = 10**7

start = time.clock()
for i in range(0, Iter):
    l = make_lambda()
stop = time.clock()
print('lambda creation time {}'.format(stop - start))

start = time.clock()
for i in range(0, Iter):
    l()
stop = time.clock()
print('lambda execution time {}'.format(stop - start))

start = time.clock()
for i in range(0, Iter):
    p = make_partial()
stop = time.clock()
print('partial creation time {}'.format(stop - start))

start = time.clock()
for i in range(0, Iter):
    p()
stop = time.clock()
print('partial execution time {}'.format(stop - start))

在 Python 3.3 上,它给出:

lambda creation time 3.1743163756961392
lambda execution time 3.040552701787919
partial creation time 3.514482823352731
partial execution time 1.7113973411608114

这意味着部分创建需要更多时间,但执行时间要少得多。这很可能是早期和晚期绑定的影响,这在ars的答案中进行了讨论。

于 2014-07-09T12:31:08.670 回答
13

除了 Alex 提到的额外功能之外,functools.partial 的另一个优势是速度。使用 partial 可以避免构造(和破坏)另一个堆栈帧。

默认情况下,partial 和 lambdas 生成的函数都没有文档字符串(尽管您可以通过 为任何对象设置文档字符串__doc__)。

您可以在此博客中找到更多详细信息:Python 中的部分函数应用

于 2013-09-05T03:08:02.717 回答
1

在第三个示例中,我最快地理解了意图。

当我解析 lambdas 时,我期望比标准库直接提供的更复杂/奇怪。

此外,您会注意到第三个示例是唯一不依赖于 ; 完整签名的示例sum2。从而使它稍微松散耦合。

于 2010-07-15T03:37:10.417 回答