Python:为什么需要functools.partial?

部分应用是很酷的。 functools.partial提供哪些function,您无法通过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不知何故更有效率,或可读?

functools.partial提供哪些function,您无法通过lambdas获得?

在额外的function方面没有太多的function (但是,见后面的内容),并且可读性在旁观者的眼中。
大多数熟悉函数式编程语言的人(尤其是Lisp / Scheme系列中的人)似乎很喜欢lambda – 我说的是“最”,绝对不是全部,因为Guido和我确实是那些“熟悉的” (等),但认为lambda作为Python的眼睛exception…
他已经悔过自己已经接受了Python,而计划将其从Python 3中删除,作为“Python的毛病”之一。
我完全支持他。 (我喜欢Scheme中的 lambda 虽然它在Python中的局限性,以及它不适合于其他语言的奇怪的方式,使我的皮肤爬行)。

然而,对于那些对lambda爱好者来说并不是这样的人,他们在Python历史上曾经发生过一次叛乱事件,但是直到Guido退后,才决定离开lambda
functools (使函数返回常量,标识等)的几个可能的补充没有发生(为了避免显式地重复更多的lambda的function),虽然partial当然保留(这不是完全重复,也不是一个眼睛)。

请记住, lambda的身体被限制为一个expression式 ,所以它是有限制的。 例如…:

 >>> 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 s: int(s, base=2) ! – )

是的,你可以扭曲你的lambda给你一些这样的 – 例如,关键字覆盖,

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

但我非常希望即使是最热心的lambda -lover也不会认为这个恐怖比partial调用更可读!)。 “属性设置”部分更加困难,因为Python的lambda的“主体的单一expression”限制(再加上赋值永远不能成为Pythonexpression式的一部分的事实)……最后,“伪装expression式中的赋值“通过扩展列表理解超出其devise限制…:

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

现在,将命名参数的可重写性和三个属性的设置组合成一个expression式,并告诉我这是多么可读……! – )

那么,这是一个显示不同的例子:

 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的这些post扩展了“lambda的限制”和python中的闭包:

  • Python中的闭包(第2部分)
  • Python中的闭包(第3部分)

在最新版本的Python(> = 2.7)中,可以使用partial而不是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不知何故更有效率..?

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

 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 

这意味着部分创build需要更多的时间,但执行的时间要less得多。 这很可能是早期和晚期结合的效果,这些结果是在答复中讨论的。

除了Alex提到的额外function之外,functools.partial的另一个优点是速度。 部分你可以避免构build(和破坏)另一个堆栈框架。

由部分生成的函数inheritance原始函数的文档string,而默认情况下,lambda没有文档string(尽pipe您可以通过__doc__设置任何对象的文档string)

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

我明白第三个例子中最快的意图。

当我parsinglambdaexpression式时,我期望比标准库直接提供的更复杂/更古怪。

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