2017-10-07 21 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

相关问题