[英]Pythonic way to parameterize a function (without lambda)
這是關於編程風格的問題:參數化函數的最“Pythonic”方式是什么(即使是正確的單詞呢?)
假設我有一個函數(例如ODE求解器)接受兩個參數的另一個函數(例如ODE本身)作為參數。
def solver(fun):
# Implementation goes here
# fun is a function handle that accepts two args e.g. fun(t,y)
但是,我希望傳遞給solver
的函數由第三個值參數化
def myFun(t,y,A):
# Implementation goes here
我一直在使用lambda
函數處理這種情況,如下所示:
A = 4
solution = solver(lambda t,y:myFun(t,y,A))
我最近在網上看到一些帖子告訴我要避免像瘟疫那樣的lambda
,而Guido自己后悔允許這個功能。 如果lambda
確實很糟糕,那么實現上述方法的“Pythonic”方法有什么用? 沒有lambda
我遇到了無法訪問全局命名空間的問題,即我想這樣做:
A = 4
def myFunNotLambda(t,y):
return myFun(t,y,A)
solution = solver(myFunNotLambda)
但是這樣做的唯一方法似乎是制作A
全局,這肯定比使用lambda
更糟糕
您可以使用functools.partial
,例如:
from functools import partial
A = 4
solution = solver(partial(myFun,A=A))
partial(..)
構造給定一個函數(這里是myFunc
)另一個函數,其中A
參數現在具有A
的默認值。
建議“避免像瘟疫這樣的lambda
”是一種誇張的誇張,最糟糕的是純粹的FUD - 在Python的標准庫中使用grepping lambda
顯示數以千計的匹配。
雖然Guido之前已經表達了對lambda主導的編碼風格的關注 ,但這是在(使用) map
和reduce
類的功能結構的上下文中,使用列表推導和生成器表達式在Python中更好地表達。
然而,當談到創建你需要的臨時函數時,使用lambda
沒有錯; 相反,它是這項工作的最佳工具。 如果您仍想避免使用lambda
關鍵字,可以使用嵌套的def
:
def my_particular_fun(t, y):
return my_fun(t, y, 4)
solution = solver(my_particular_fun)
如其他答案中所示,也可以使用functools.partial
來模擬lambda,盡管它需要將第三個參數的名稱修復為my_fun
。
一個非常有效的方法是使用functools.partial
,但正如已經指出的那樣, partial
只允許你“凍結”最終的args。 如果您還需要其他東西,可以使用閉包輕松實現自己的版本。
這種方法實際上比使用partial
對象更有效,因為當你調用partial
它仍然必須調用你傳入的原始函數,所以每次調用partial
導致兩次調用,並且Python函數/方法調用相對較慢。 如果您很好奇,請查看functools.partial
的Python源代碼 。
在這個例子中,我已經將A
作為函數的第二個(偽)參數,因為partial
很好地處理了它是最后一個arg的情況。
def my_partial(param):
A = param
print('Creating two_arg_func with A ==', A)
def two_arg_func(t, y):
# Do something with all the args
return 'args', t, A, y
return two_arg_func
def test(f):
for u in range(10, 50, 10):
print(f(u, u + 5))
test(my_partial(7))
產量
Creating two_arg_func with A == 7
('args', 10, 7, 15)
('args', 20, 7, 25)
('args', 30, 7, 35)
('args', 40, 7, 45)
我們在my_partial
不需要param
,我們可以使用傳入的arg,因為它是my_partial
的本地:
def my_partial(A):
print('Creating two_arg_func with A ==', A)
def two_arg_func(t, y):
return 'args', t, A, y
return two_arg_func
從您的評論中,我現在明白您希望能夠改變A
當然你可以通過再次調用partial
或my_partial
來做到這一點,但是如果你想修改A
那么不那么有效。
您的注釋表明您要在全局上下文中修改A
,因此您可能只使用全局。 你並不需要真正把代碼修改A
進入全球范圍內,你可以在一個功能包,但你當然需要使用global
指令在修改函數A
。 但是,在任何只讀取A
值的函數中都不需要global
指令。
這是一個簡短的演示。
def two_arg_func(t, y):
# Do something with the args and with A
return 'args', t, A, y
def solve(f):
for u in range(10, 40, 10):
print('SOLVER', f(u, u + 5))
def test(f):
global A
for A in range(7, 10):
print(A)
solve(f)
test(two_arg_func)
產量
7
SOLVER ('args', 10, 7, 15)
SOLVER ('args', 20, 7, 25)
SOLVER ('args', 30, 7, 35)
8
SOLVER ('args', 10, 8, 15)
SOLVER ('args', 20, 8, 25)
SOLVER ('args', 30, 8, 35)
9
SOLVER ('args', 10, 9, 15)
SOLVER ('args', 20, 9, 25)
SOLVER ('args', 30, 9, 35)
然而 ,之前的解決方案有點令人不滿意,因為你的問題的主旨是如何在不使用全局的情況下做到這一點。 所以這里的代碼略有不同。 我們不是將A
放在全局命名空間中,而是將它作為函數屬性附加到two_arg_func
。 我們可以這樣做,因為Python函數是一個對象,它已經擁有一大堆屬性; 您可能熟悉的兩個是__name__
和__doc__
。 無論如何,這是新代碼,它打印與先前版本相同的輸出。
def two_arg_func(t, y):
A = two_arg_func.A
# Do something with the args and with A
return 'args', t, A, y
def solve(f):
for u in range(10, 40, 10):
print('SOLVER', f(u, u + 5))
def test(f):
for A in range(7, 10):
print(A)
f.A = A
solve(f)
test(two_arg_func)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.