Python的:为什么functools.partial有必要吗?Python的:为什么functo

2019-05-10 15:47发布

部分应用程序是很酷。 哪些功能呢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某种程度上更有效,或可读?

Answer 1:

哪些功能呢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]

现在结合命名参数覆盖性,再加上三个属性的设置,到一个单一的表达,并告诉我是多么可读将是... - !)



Answer 2:

那么,这里的显示中不同,例如:

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部分)


Answer 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>


Answer 4:

是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 。



Answer 5:

除了额外的功能亚历克斯提到,functools.partial的另一个优点是速度快。 具有部分就可以避免构造(和破坏)另一个堆栈帧。

无论是通过部分也不产生lambda表达式的功能有文档字符串默认情况下(尽管你可以通过设置任何对象doc字符串__doc__ )。

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



Answer 6:

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

当我解析lambda表达式,我期待比标准库中直接提供了更多的复杂性/怪胎。

此外,你会发现,第三个例子是不依赖于完整的签名是唯一一个sum2 ; 因此使得稍微松散耦合。



文章来源: Python: Why is functools.partial necessary?