簡體   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