部分应用很酷。functools有什么功能。部分报价,你不能通过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在某种程度上更有效或更可读吗?


当前回答

除了Alex提到的额外功能,functools的另一个优点是。偏微分是速度。使用partial,你可以避免构造(和破坏)另一个堆栈框架。

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

你可以在这个博客中找到更多细节:Python中的部分函数应用

其他回答

除了Alex提到的额外功能,functools的另一个优点是。偏微分是速度。使用partial,你可以避免构造(和破坏)另一个堆栈框架。

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

你可以在这个博客中找到更多细节:Python中的部分函数应用

好吧,这里有一个例子可以说明区别:

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中的闭包:

Python闭包(第2部分) Python闭包(第3部分)

函数在计算某些变量时很有用。

从一个局外人的角度来看,这里有一系列更友好的例子:

from functools import partial

sum = lambda x, y: x + y            # sum(x, y) == x + y

n = 2
normalSum = lambda x: sum(x, n)     # normalSum(x) == sum(x, y=n)
partialSum = partial(sum, y = n)    # partialSum(sum(y=n)) == sum(x, 2)
print(normalSum(2), partialSum(2))  # 4 4

n = 6
print(normalSum(2), partialSum(2))  # 8 4

注意这个部分是如何保存当时n的值的。

...
n = 2
partialSumOrig = partial(sum, y = n)        # partialSumOrig(sum(y=n)) == sum(x, 2)
n = 6
partialSumNew = partial(sum, y = n)         # partialSumNew(sum(y=n)) == sum(x, 6)

print(partialSumOrig(2), partialSumNew(2))  # 4 8

演示如何将参数传递到嵌套lambdas的额外示例:

...
n = 8
partialSumOrig = partial(sum, y = n)  # partialSumOrig(sum(y=n)) == sum(x, 8)
partialSumNew = partial(sum, n)       # partialSumNew(sum(n)) == sum(8, y)

print(partialSumOrig(2))  # 10        # partialSumOrig(sum(2, 8)) == sum(2, 8)
print(partialSumNew(2))   # 10        # partialSumNew(sum(8, 2)) == sum(8, 2)

最后一个例子展示了参数如何以partial形式传递:

...
n = 2
m = 2
partialSumSilly = partial(sum, n, m)  # partialSumSilly(sum(n, m)) == sum(2, 2)
print(partialSumSilly())              # 4

最重要的是:

normalSum()的行为类似于后期绑定,其中n在运行时计算。 partialSum()的行为类似于早期绑定,其中n在定义时被求值。

注意:实际上,由于cpython的解释性,几乎所有东西都是后期绑定。

在最新版本的Python(>=2.7)中,你可以pickle一个partial,但不能pickle一个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>

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的回答中讨论的早期和晚期绑定的影响。