繁体   English   中英

functools.partial 和类似的 lambda 之间的区别?

[英]Differences between functools.partial and a similar lambda?

在 Python 中,假设我有一个函数f ,我想传递一些次要参数(为简单起见,假设它只是保持可变的第一个参数)。

这两种方式(如果有的话)之间有什么区别?

# Assume secondary_args and secondary_kwargs have been defined

import functools

g1 = functools.partial(f, *secondary_args, **secondary_kwargs)
g2 = lambda x: f(x, *secondary_args, **secondary_kwargs)

例如,在partial的文档页面中,有这样一句话:

类中定义的partial对象的行为类似于静态方法,并且在实例属性查找期间不会转换为绑定方法。

如果用于从提供给类的参数(在构造函数中或稍后通过函数)创建类方法,lambda 方法是否会受到影响?

  1. lambda 函数与标准函数具有相同的类型,因此它的行为类似于实例方法。

  2. 您示例中的partial对象可以这样调用:

     g1(x, y, z)

    导致这个调用(不是有效的 Python 语法,但你明白了):

     f(*secondary_args, x, y, z, **secondary_kwargs)

    lambda 只接受一个参数并使用不同的参数顺序。 (当然这两种差异都可以克服——我只是回答你给出的两个版本之间的差异是什么。)

  3. partial对象的执行比等效的lambda的执行稍快。

概括

lambdafunctools.partial在常见用例中的实际区别似乎是

  • functools.partial需要导入, lambda不需要。
  • 使用functools.partial创建的函数的函数定义仅通过打印创建的函数可见。 使用lambda创建的函数应该使用inspect.getsource()检查。

发现这些对于lambdafunctools.partial实际上是相同

  • 速度
  • 回溯

速度(lambda 与 functools.partial)

我认为测试和真实数据比仅仅猜测哪个比另一个更快更能说明问题。

看起来lambdafunctools.partial之间的速度差异没有统计证据 我以不同的重复次数运行了不同的测试,每次得到的结果都略有不同; 这三种方法中的任何一种都可能是最快的。 速度相同,置信度为 95% (2 sigma)。 这是一些数值结果*

# When functions are defined beforehand
In [1]: timeit -n 1000 -r 1000 f_partial(data)
23.6 µs ± 2.92 µs per loop (mean ± std. dev. of 1000 runs, 1000 loops each)

In [2]: timeit -n 1000 -r 1000 f_lambda(data)
22.6 µs ± 2.6 µs per loop (mean ± std. dev. of 1000 runs, 1000 loops each)

# When function is defined each time again
In [3]: timeit -n 1000 -r 1000 (lambda x: trim_mean(x, 0.1))(data)
22.6 µs ± 1.98 µs per loop (mean ± std. dev. of 1000 runs, 1000 loops each)

In [4]: timeit -n 1000 -r 1000 f_lambda = lambda x: trim_mean(x, 0.1); f_lambda(data)
23.7 µs ± 3.89 µs per loop (mean ± std. dev. of 1000 runs, 1000 loops each)

In [5]: timeit -n 1000 -r 1000 f_partial = partial(trim_mean, proportiontocut=0.1); f_partial(data)
24 µs ± 3.38 µs per loop (mean ± std. dev. of 1000 runs, 1000 loops each)

回溯

我还尝试使用插入了字符串元素的列表运行f_lambdaf_partial ,并且回溯是相等的(当然除了第一个条目)。 所以那里没有区别。

检查源代码

  • 使用functools.partial创建的函数的函数定义仅通过打印创建的函数可见。 使用lambda创建的函数应该使用inspect.getsource()检查。
# Can be inspected with just printing the function
In [1]: f_partial
Out[1]: functools.partial(<function trim_mean at 0x000001463262D0D0>, proportiontocut=0.1)

In [2]: print(f_partial)
functools.partial(<function trim_mean at 0x000001463262D0D0>, proportiontocut=0.1)

# Lambda functions do not show the source directly
In [3]: f_lambda
Out[3]: <function __main__.<lambda>(x)>

# But you can use inspect.getsource()
In [4]: inspect.getsource(f_lambda)
Out[4]: 'f_lambda = lambda x: trim_mean(x, 0.1)\n'

# This throws a ValueError, though.
In [5]: inspect.getsource(f_partial)

附录

* 测试中使用的设置

from functools import partial
from scipy.stats import trim_mean
import numpy as np
data = np.hstack((np.random.random(1000), np.random.random(50)*25000))

f_lambda = lambda x: trim_mean(x, 0.1)
f_partial = partial(trim_mean, proportiontocut=0.1)

测试是在 Python 3.7.3 64 位 (Windows 10) 上执行的。

如前所述,partials 不仅比等效的 lambda 快 20%,而且它们保留了一个直接引用,以指示它们函数的相关性。 而在 lambda 中,该函数被“埋藏”在函数体内。

=> 如果您只需要解决将一个函数的评估推迟到所有参数都已知的问题,那么请使用部分函数。 与将调用埋入匿名函数(即 lambda)相比,您将拥有更好的自省方法。

我相信类方法只适用于在类定义期间分配的函数。 以后分配的功能不作特殊处理。

除此之外,我个人更喜欢 lambda,因为它们更常见,因此使代码更容易理解。

class Foo(object):
    def __init__(self, base):
        self.int = lambda x:int(x, base)

print Foo(4).int('11')

是的, lambda将因此而“受苦”。 partial没有这个问题,因为它是一个重载了调用运算符的对象,而不是一个真正的函数。

但是在类定义中使用这样的 lambda 只是误用。

这里最重要的一点被遗漏了lambda有指向输入变量的链接,但是partition在创建过程中复制了 args:

>>> for k,v in {"1": "2", "3": "4"}.items():
...     funcs.append(lambda: print(f'{k}: {v}'))
...
>>> print(funcs)
[<function <lambda> at 0x106db71c0>, <function <lambda> at 0x10747a3b0>]
>>> for f in funcs:
...     f()
...
3: 4  # result are indentical
3: 4
>>> import functools
>>> funcs = []
>>> for k,v in {"1": "2", "3": "4"}.items():
...     funcs.append(functools.partial(print, f'{k}: {v}'))
...
>>> print(funcs)
[functools.partial(<built-in function print>, '1: 2'), functools.partial(<built-in function print>, '3: 4')]
>>>
>>> for f in funcs:
...     f()
...
1: 2  # result differs
3: 4

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM