关于python 3.x:是否有可能采用这种使用字典的方法来查找/评估许多函数及其相应的args之一?

Is it possible to adapt this approach of using dictionaries to find/evaluate one of many functions and its corresponding args?

假设一个有几个单独的函数来评估一些给定的数据。与其使用冗余的if/else循环,不如使用字典键来查找特定的函数及其相应的参数。我觉得这是可能的,但我不知道如何使这工作。作为一个简单的例子(我希望能适应我的情况),考虑下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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

但是,

1
print(pick_function(1))

返回错误消息

1
2
3
  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循环?


要用最小的更改来修复代码,请将return func(x, args)更改为return func(x, *args)。我认为这就是安东·VBR在评论中的建议。

但是,我认为您的代码可以通过使用*("splat")和**("double splat")。位置/关键字参数解包这样的运算符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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


您试图混合命名参数和未命名参数!

根据经验,如果使用**符号将dict传递给函数,则可以使用**kwargs值访问参数。但是,如果使用*符号将元组传递给funcion,则可以使用*args值访问参数。

我按照以下方式编辑了方法:

1
2
3
4
5
6
7
8
9
10
11
 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

所以,在第一次通话中,你有:

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

因为您正在传递命名参数。

在第二次执行中,您将拥有:

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

我知道你想找到一个没有if/else的解决方案,但是你必须区分这两种情况。


如果我理解你的要求,我不知道你想用1,2…不管怎样,作为字典的键。您可以将函数名和要直接使用的参数列表传递给函数,并按如下方式使用:

1
2
3
4
def use_function(func, argList):
    return (func(*argList))

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

或:

1
2
3
4
def use_function_2(func, argDict):
    return (func(**argDict))

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

如果你愿意的话,你还可以用字典来保存与函数对应的数字。就像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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))

然而,这开始变得有点令人困惑。我会确保你花点时间再仔细检查一下,这是你真正想做的。