Python: Why is functools.partial necessary?
部分应用程序很酷。
1 2 3 4 5 6 7 8 9 10 11 12 | >>> 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 |
What functionality does
functools.partial offer that you can't
get through lambdas?
在额外的功能方面不多(但是,见下文),并且,可读性在旁观者眼中。大多数熟悉函数式编程语言的人(尤其是Lisp/Scheme家族的人)似乎都很喜欢
然而,对于一群爱以东的人来说,情况并非如此,他们策划了一场与Python历史上最接近的叛乱,直到圭多回去决定离开以东。虽然
记住,
1 2 3 4 5 6 7 8 9 | >>> import functools >>> f = functools.partial(int, base=2) >>> f.args () >>> f.func <type 'int'> >>> f.keywords {'base': 2} >>> |
1 2 | >>> f('23', base=10) 23 |
所以,正如你所看到的,它显然没有
是的,你可以扭曲你的lambda来给你一些这个——例如,对于关键字重写,
1 | >>> f = lambda s, **k: int(s, **dict({'base': 2}, **k)) |
但我非常希望,即使是最热心的
1 2 | >>> f = [f for f in (lambda f: int(s, base=2),) if setattr(f, 'keywords', {'base': 2}) is None][0] |
现在,将命名参数的可重写性,加上三个属性的设置,组合成一个表达式,告诉我这将是多么的可读…!-)
好吧,这里有一个例子说明了不同之处:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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部分)
在最新版本的python(>=2.7)中,可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | >>> pickle.dumps(partial(int)) 'cfunctools partial p0 (c__builtin__ int p1 tp2 Rp3 (g1 (tNNtp4 b.' >>> 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> |
Is functools somehow more efficient..?
作为对此的部分回答,我决定测试一下性能。下面是我的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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上,它给出了:
1 2 3 4 | lambda creation time 3.1743163756961392 lambda execution time 3.040552701787919 partial creation time 3.514482823352731 partial execution time 1.7113973411608114 |
这意味着分部需要更多的时间来创建,但执行时间要少得多。这很可能是ARS答案中讨论的早期和后期绑定的效果。
除了上面提到的额外功能,functools.partial的另一个优点是速度。使用分部可以避免构造(和销毁)另一个堆栈帧。
默认情况下,分部和lambda生成的函数都没有doc string(尽管您可以通过
您可以在这个博客中找到更多详细信息:Python中的部分函数应用程序
在第三个例子中,我最快地理解了意图。
当我解析lambda时,我期望比标准库直接提供的更复杂/更古怪。
另外,您会注意到,第三个示例是唯一一个不依赖于