2017-10-07 49 views
2

假設人們有幾個單獨的函數來評估某些給定的數據。而不是使用冗餘的if/else循環,決定使用字典鍵來查找特定的函數及其相應的參數。我覺得這是可能的,但我無法弄清楚如何使這項工作。作爲一個簡單的例子(我希望能爲我的情況調整),考慮下面的代碼:是否有可能適應這種使用字典來查找/評估衆多函數之一及其相應參數的方法?

def func_one(x, a, b, c=0): 
    """ arbitrary function """ 
    # c is initialized since it is necessary in func_two and has no effect in func_one 
    return a*x + b 

def func_two(x, a, b, c): 
    """ arbitrary function """ 
    return a*x**2 + b*x + c 

def pick_function(key, x=5): 
    """ picks and evaluates arbitrary function by key """ 
    if key != (1 or 2): 
     raise ValueError("key = 1 or 2") 

    ## args = a, b, c 
    args_one = (1, 2, 3) 
    args_two = (4, 5, 3) 

    ## function dictionary 
    func_dict = dict(zip([1, 2], [func_one, func_two])) 

    ## args dictionary 
    args_dict = dict(zip([1, 2], [args_one, args_two])) 

    ## apply function to args 
    func = func_dict[key] 
    args = args_dict[key] 

    ## my original attempt >> return func(x, args) 
    return func(x, *args) ## << EDITED SOLUTION VIA COMMENTS BELOW 

print(func_one(x=5, a=1, b=2, c=3)) # prints 7 

但是,

print(pick_function(1)) 

返回一條錯誤消息

File "stack_overflow_example_question.py", line 17, in pick_function 
    return func(x, args) 
TypeError: func_one() missing 1 required positional argument: 'b' 

顯然,不所有的args正在通過字典。我嘗試過從args_oneargs_two(如pick_function中定義的)添加/刪除額外括號和假名的各種組合。這種方法成果豐碩嗎?有沒有其他方便(在可讀性和速度方面)的方法,不需要很多if/else循環?

+0

嘗試將其更改爲:**返回func(x,* args)**(*等於開箱變量) –

+0

您是否想要放置一個摔跤運算符?它已經以你評論的形式出現了。 – mikey

回答

2

要以最少的更改修復代碼,請將return func(x, args)更改爲return func(x, *args)。我覺得這是Anton vBR is suggesting的評論。


不過,我覺得你的代碼可以通過 使用*( 「圖示」)和**進一步簡化位置/關鍵字argument unpacking operators像這樣("double-splat"?):

def func_one(x, a, b, c=0): 
    """ arbitrary function """ 
    # c is initialized since it is necessary in func_two and has no effect in func_one 
    return a*x + b 

def func_two(x, a, b, c): 
    """ arbitrary function """ 
    return a*x**2 + b*x + c 

def func(key, *args, **kwargs): 
    funcmap = {1: func_one, 2: func_two} 
    return funcmap[key](*args, **kwargs) 

def pick_function(key, x=5): 
    """ picks and evaluates arbitrary function by key """ 
    argmap = {1: (1, 2, 3), 2: (4, 5, 3)} 
    return func(key, x, *argmap[key]) 

print(func_one(x=5, a=1, b=2, c=3)) 
# 7 
print(pick_function(1)) 
# 7 
print(func(1, 5, 1, 2, 3)) 
# 7 
print(func(1, b=2, a=1, c=3, x=5)) 
# 7 
+0

這似乎是那些張貼的最簡單的解決方案。如果您不介意,我會在明天提出一些相關的後續問題。 – mikey

1

如果我明白你在問什麼,我不知道你想用1,2 ...作爲你的字典鍵。你可以通過函數的名稱,你想直接使用的功能,並使用它的方式像args來列表:

def use_function(func, argList): 
    return (func(*argList)) 

print(use_function(func_one, [5, 1, 2, 3])) 

或:

def use_function_2(func, argDict): 
    return (func(**argDict)) 

print(use_function_2(func_one, {'a':1, 'b':2,'x':5, 'c':3})) 

,如果你喜歡你仍然可以使用字典來保存與功能相對應的數字。這將是這樣的:

def pick_function_2(key, x=5): 
    """ picks and evaluates arbitrary function by key """ 
    if key != (1 or 2): 
     raise ValueError("key = 1 or 2") 

    ## args = a, b, c 
    args_one = [1, 2, 3] 
    args_two = [4, 5, 3] 

    ## function dictionary 
    func_dict = dict(zip([1, 2], [func_one, func_two])) 

    ## args dictionary 
    args_dict = dict(zip([1, 2], [args_one, args_two])) 

    ## apply function to args 
    func = func_dict[key] 
    args = args_dict[key] 

    return func(*([x] + args)) 

print(pick_function_2(1)) 

然而,這開始變得有點混亂。我會確保你花一些時間,並仔細檢查,這實際上是你想要做的。

+0

我可以在大約一個小時內嘗試一下並感謝它。我正在嘗試這種方法,因爲我正在比較具有相同可最小化錯誤度量標準函數的多個數據集(例如:chi square和mle),每個數據集都有不同的可輸入參數。儘管我總是對替代方法感到好奇。 – mikey

+0

如果我像上一個例子那樣使用splat運算符,那麼它會按順序覆蓋這些參數,是否正確?如果是這樣,這是否意味着被調用函數(func-one或func_two)的所有(非初始化?)輸入都必須在args中? – mikey

+1

是的,*運算符依次轉換參數。 **運算符根據關鍵字轉換它們,所以順序無關緊要。如果您提前知道參數的名稱,您可以使用**,否則,是的,您需要將它們放入*參數中並將它們解壓縮。 @darioSka似乎有一個很好的答案,如果這是你想要做的。或者如果你想混合起來。 – wsad597

1

您正在嘗試混合命名參數和未命名的參數!作爲經驗法則,如果您使用**符號將函數傳遞給函數,則可以使用** kwargs值訪問參數。但是,如果使用*表示法將元組傳遞給函數,則可以使用* args值訪問參數。

我通過以下方式編輯的方法:

def func_one(*args, **kwargs): 
    """ arbitrary function """ 
    # c is initialized since it is necessary in func_two and has no effect in func_one 
    if args: 
     print("args: {}".format(args)) 
     x, other_args, *_ = args 
     a, b , c = other_args 
    elif kwargs: 
     print("kwargs: {}".format(kwargs)) 
     x, a , b , c = kwargs['x'], kwargs['a'], kwargs['b'], kwargs['c'] 
    return a*x + b 

所以,在第一次調用您有:

print(func_one(x=5, a=1, b=2, c=3)) # prints 7 
kwargs: {'b': 2, 'a': 1, 'x': 5, 'c': 3} 
7 

由於您所傳遞命名的參數。

在第二個執行,你必須:

print(pick_function(1)) 
args: (5, (1, 2, 3)) 
7 

我知道你想找到一個解決方案,而if/else語句,但你必須theese兩種情況之間進行區分。

+0

我需要了解btwn args和kwargs的區別。這是照亮,謝謝。 – mikey

+0

所以如果我想要一些參數而不是其他參數(例如:c不是function_one中允許的輸入),那麼kwargs是要走的路(因爲它們基本上被命名爲變量/參數)。那是對的嗎? – mikey

+0

@mikey是的,這是最安全的方式 – darioSka

相關問題