从解析字符串创建functools.partial的*args和**kwargs

1 投票
2 回答
4649 浏览
提问于 2025-04-16 05:23

我收到了很多字符串列表,想把它们转换成方法调用,使用的是functools.partial这个工具:

[ 'object1.method1(arg1, arg2, 4, key=value)', 'object2.method2(6, arg1)' ...]

用正则表达式生成functools.partial需要的'func'参数很简单,但我在把括号里的字符串转换成有效的*args和**kwargs(也就是位置参数和关键字参数)时遇到了麻烦,没办法轻松搞定。

每个列表项都是有效的Python代码。我就是想不出一个快速简单的方法,把像'arg1, arg2, 4, key=value'这样的字符串转换成functools.partial可以使用的格式。我到底漏掉了什么呢?

更新:

抱歉,我忘了说一些重要的信息。这些参数在这个过程的范围内不是有效的标识符,所以用'eval'方法不行。不过,它们在最终使用这些部分对象的范围内是正确的,所以可以作为字面量“复制”。

我现在的做法返回的是一个字符串'arg1, arg2, 4, this=that'。如果直接传给functools.partial,它会把它当作一个单独的字符串参数来处理。

嗯……越描述我越意识到,除非这些标识符在这个范围内有效,否则是没办法做到的……

2 个回答

0

使用eval函数很简单,只需要把函数名换成你自己定义的名字,就不需要用正则表达式了。

def get_args_and_kwargs(*args, **kwargs):
    return args, kwargs

def convert_str_to_args_and_kwargs(s):
    return eval(s.replace(s[:s.find('(')], 'get_args_and_kwargs'))

for s in your_list:
    args, kwargs = convert_str_to_args_and_kwargs(s)
2

我在写另一个回答,因为如果我改动旧的回答,评论就不会和内容匹配了。如果这个回答更好,我会撤回之前的。下面的内容应该可以工作。

操作原理:

  1. 获取你的函数
  2. 分析字符串以提取参数
  3. 把每个参数和它自己的评估放在一个部分中
  4. 把一个包装器、函数和部分参数放在另一个部分中
  5. 在需要的范围内执行

下面的代码主要集中在第2到第4步。假设已经导入了需要的库。当然,由于在范围内增加了额外的代码,还会有命名冲突的问题。


def wrapper(func_, *args, **kw):
    new_args = []
    new_kw = {}
    for arg in args:
        if type(arg)==functools.partial:
            arg = arg()
        new_args.append(arg)
    for key in kw:
        value = kw[key]
        if type(value)==functools.partial:
            value = value()
        new_kw[key]=value
     return func_(*new_args, **new_kw)

orig_str = 'object1.method1(arg1, arg2, 4, key=value)'
argstr = re.search('.+\((.+)\)', orig_str).group(1)
args = []
kw = {}
for x in argstr.split(','):
    if '=' in x:
        key, value = x.strip().split('=')
    else:
        value = x.strip()
        key = None            
    value = functools.partial(eval, value)
    if key:
        kw[key]=value
    else:
        args.append(value)

what_you_want = functools.partial(wrapper, func, *args, **kw)

撰写回答