【问题标题】:Differences between functools.partial and a similar lambda?functools.partial 和类似的 lambda 之间的区别?
【发布时间】:2012-08-06 12:36:08
【问题描述】:

在 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)

doc page for partial为例,有这样的引用:

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

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

【问题讨论】:

  • 我认为我的问题与链接的问题完全不同,但是该问题的最佳答案是如此彻底,以至于它也(巧合地)回答了我正在寻找的大部分内容。如果它作为副本关闭,我不会抱怨。
  • 我同意这不是重复的,我已重新打开您的问题。供其他人参考,相关问题是here

标签: python lambda partial functools


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

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

    g1(x, y, z)
    

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

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

    lambda 只接受一个参数并使用不同的参数顺序。 (当然,这两个差异都可以克服——我只是在回答您提供的两个版本之间的差异。)

  3. partial 对象的执行速度略快于等效lambda 的执行速度。

【讨论】:

  • 您是否对partial 对象与lambda 进行了比较?
  • @nightcracker:是的,不是现在,而是过去几次。 partial 对象不创建 Python 代码对象,因此它保存了完整的 Python 堆栈帧。
  • @Sven Marnach:究竟什么更快,创建代码、调用代码或两者兼而有之?
  • @nightcracker:大多数时候,只有执行时间很重要,所以这是我测量的。
【解决方案2】:

总结

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

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

lambdafunctools.partial 发现这些几乎相同

  • 速度
  • 追溯

速度(lambda vs 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) 上执行的。

【讨论】:

  • "lambda 函数只接受一个位置参数。"只有这样定义。他们可以被赋予任何签名(但没有注释)。
【解决方案3】:

partials 不仅比已经说过的等效 lambda 快 20%,而且它们保持直接引用它们的功能相关。而在 lambdas 中,该函数被“隐藏”在函数体内。

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

【讨论】:

    【解决方案4】:

    我相信类方法的东西只适用于类定义期间分配的函数。后面分配的函数不做特殊处理。

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

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

    【讨论】:

    • I'd personally favor lambdas since they're more common and hence make the code easier to understand.我以前从未听过这些说法。
    • @phant0m 我听说过的关于 Python lambda 的大多数陈述都包含“破碎”这个词。 ;-)
    • @Chris functools.partial 更简洁,但我认为只要您没有范围界定问题,lambdas 就更容易理解。一方面,lambda 意味着您不必记住 functools.partial 采用的精确参数顺序。
    【解决方案5】:

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

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

    【讨论】:

      猜你喜欢
      • 2012-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-26
      • 2021-12-31
      • 2011-05-21
      • 2011-03-30
      • 2014-02-26
      相关资源
      最近更新 更多