簡體   English   中英

Pythonic方法參數化一個函數(沒有lambda)

[英]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主導的編碼風格的關注 ,但這是在(使用) mapreduce類的功能結構的上下文中,使用列表推導和生成器表達式在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 當然你可以通過再次調用partialmy_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM