[英]Interaction between functools.partial and functools.reduce
[英]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 方法是否會受到影響?
lambda 函數與標准函數具有相同的類型,因此它的行為類似於實例方法。
您示例中的partial
對象可以這樣調用:
g1(x, y, z)
導致這個調用(不是有效的 Python 語法,但你明白了):
f(*secondary_args, x, y, z, **secondary_kwargs)
lambda 只接受一個參數並使用不同的參數順序。 (當然這兩種差異都可以克服——我只是回答你給出的兩個版本之間的差異是什么。)
partial
對象的執行比等效的lambda
的執行稍快。
lambda
和functools.partial
在常見用例中的實際區別似乎是
functools.partial
需要導入, lambda
不需要。functools.partial
創建的函數的函數定義僅通過打印創建的函數可見。 使用lambda
創建的函數應該使用inspect.getsource()
檢查。 發現這些對於lambda
和functools.partial
實際上是相同的
我認為測試和真實數據比僅僅猜測哪個比另一個更快更能說明問題。
看起來lambda
和functools.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_lambda
和f_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.