部分应用程序是很酷。 哪些功能呢functools.partial
优惠,您无法通过lambda表达式得到什么?
>>> 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优惠,您无法通过lambda表达式得到什么?
没有多少额外的功能方面(但,见下文) -和可读性是在旁观者的眼睛。
谁是熟悉函数式编程语言的大多数人(那些特别是Lisp语言/计划家庭)似乎喜欢lambda
就好了-我说“最”,肯定不是全部,因为圭多和我确实是在那些“熟悉” (ETC),但认为lambda
为在Python碍眼异常...
他是忏悔曾经接受该项成Python,而计划从Python 3中删除它,“Python的毛刺”之一。
我完全支持他在这。 (我爱lambda
方案 ......而它在Python,和奇怪的方式限制它只是不适合与语言的其余部分,我的鸡皮疙瘩)。
事实并非如此,但是,成群的lambda
爱好者-谁上演最亲密的事情见过Python的历史叛乱之一,直到圭多回溯和决定离开lambda
英寸
几种可能的补充functools
(使函数返回的常量,身份等)并没有发生(避免明确地复制更多的lambda
的功能),但partial
做当然保持(它是没有总重复,也不是碍眼)。
请记住, lambda
的身体被限制是一个表达式 ,所以它有局限性。 例如...:
>>> 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
所以,你看,这是definely不是简单的lambda s: int(s, base=2)
)!
是的,你可能会扭曲你的拉姆达给你一些这一点-例如,对于关键字替换,
>>> f = lambda s, **k: int(s, **dict({'base': 2}, **k))
但我真希望 ,即使是最狂热的lambda
-lover不考虑这个恐怖不是更可读的partial
通话- !)。 在“属性设置”部分更是难上加难,因为“身体是一个表达式” Python的限制lambda
(加上一个事实,即分配永远是一个Python表达式的一部分)...你落得“在表达式中伪造分配“通过拉伸列表理解远远超出其设计限制...:
>>> f = [f for f in (lambda f: int(s, base=2),)
if setattr(f, 'keywords', {'base': 2}) is None][0]
现在结合命名参数覆盖性,再加上三个属性的设置,到一个单一的表达,并告诉我,是多么可读将是... - !)
那么,这里的显示中不同,例如:
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
这些职位由伊万·摩尔在蟒蛇的“拉姆达的限制”和封锁扩大:
- 在Python闭包(第2部分)
- 在Python闭包(第3部分)
在Python的(> = 2.7)的最新版本,可以pickle
一个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某种程度上更高效..?
作为一个部分回答这个,我决定来测试性能。 这是我的例子:
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 。
除了额外的功能亚历克斯提到,functools.partial的另一个优点是速度快。 具有部分就可以避免构造(和破坏)另一个堆栈帧。
无论是通过部分也不产生lambda表达式的功能有文档字符串默认情况下(尽管你可以通过设置任何对象doc字符串__doc__
)。
你可以在这个博客找到更多的细节: 在Python部分函数应用
我明白了第三个例子的意图最快。
当我解析lambda表达式,我期待比标准库中直接提供了更多的复杂性/怪胎。
此外,你会发现,第三个例子是不依赖于完整的签名是唯一一个sum2
; 因此使得稍微松散耦合。