Python从字符串中调用带有getattr和args/kwargs的函数



我想用getattr(或者其他什么?(从这样的字符串中调用一个模块函数:

import bar
funcStr = "myFunc("strParam", 123, bar.myenum.val1, kwarg1="someString", kwarg2=456, kwarg3=bar.myenum.val2)"
[function, args, kwargs] = someParsingFunction(funcStr)
# call module function
getattr(bar, function)(*args, **kwargs)

如何从字符串中提取argskwargs,以便将它们传递给getattr?我尝试了使用Pythonast模块的literal_eval方法。但是ast不能评估模块bar的枚举。SO上的所有其他示例都传递了一个只有字符串的kwargs映射。尤其是它们从不解析字符串中的参数。或者有其他方法可以直接从字符串中调用函数吗?

编辑:python脚本从文件中读取函数字符串。因此不建议在此使用eval

EDIT2:使用python 3.6.3

第3版:感谢前两个答案,我提出了两个想法。在解析输入字符串中的argskwargs之后,有两种可能性可以获得正确类型的参数。

  1. 我们可以使用ast.literal_eval(<value of the argument>)。对于kwarg2中的标准类型的参数,它将返回所需的值。如果这种情况发生,枚举将发生这种情况,那么我们将在bar模块上使用getattr并获取枚举。如果此情况也例外,则该字符串无效
  2. 我们可以使用inspect模块并迭代myFunc的参数。然后,对于每个argkwarg,我们将检查该值是否是myFunc参数(类型(的实例。如果是,我们将arg/karg强制转换为myFunc参数类型。否则,我们将引发异常,因为给定的arg/karg不是myFunc参数的实例。这个解决方案比第一个更灵活

这两种解决方案感觉更像是一种变通方法。第一次测试似乎奏效了。我稍后会在这里发布我的结果。

这有帮助吗?

funcStr = r"""myFunc("strParam", 123, bar.myenum.val1, kwarg1="someString", kwarg2=456, kwarg3=bar.myenum.val2)"""
def someParsingFunction(s):
func, s1 = s.split('(', 1)
l = s1.replace('\','').strip(')').split(', ')
arg_ = [x.strip('"') for x in l if '=' not in x]
kwarg_ = {x.split('=')[0]:x.split('=')[-1] for x in l if '=' in x}
return func, arg_, kwarg_
class bar:
def myFunc(self, *args, **kwargs):
print(*args)
print(kwargs)
[function, args, kwargs] = someParsingFunction(funcStr)
getattr(bar, function)(*args, **kwargs)
# 123 bar.myenum.val1
# {'kwarg1': '"someString"', 'kwarg2': '456', 'kwarg3': 'bar.myenum.val2'}

或者

funcStr = r"""myFunc("strParam", 123, bar.val1, kwarg1="someString", kwarg2=456, kwarg3=bar.val2)"""
def someParsingFunction(s):
func, s1 = s.split('(', 1)
l = s1.replace('\','').strip(')').split(', ')
arg_ = [x.strip('"') for x in l if '=' not in x]
kwarg_ = {x.split('=')[0]:x.split('=')[-1] for x in l if '=' in x}
return func, arg_, kwarg_
class Bar:
def __init__(self):
self.val1 = '111'
[function, args, kwargs] = someParsingFunction(funcStr)
bar = Bar()
obj_name = 'bar' + '.'
args = [bar.__getattribute__(x.split(obj_name)[-1]) if x.startswith(obj_name) else x for x in args]
print(args)
def get_bar_args(arg_str):
"""
example:
arg_str='bar.abc.def'
assumess 'bar' module is imported
"""
from functools import reduce
reduce(getattr, arg_str.split('.')[1:], bar)
def parseFuncString(func_str):
'''
example: func_str = "myFunc("strParam", 123, bar.myenum.val1, kwarg1="someString", kwarg2=456, kwarg3=bar.myenum.val2)"
'''
import re
all_args_str = re.search("(.*)((.*))", func_str)
all_args = all_args_str.group(2).split(',')
all_args = [x.strip() for x in all_args]
kwargs = {kw.group(1): kw.group(2) for x in all_args if (kw:=re.search('(^w+)=(.*)$', x))}
pargs = [x for x in all_args if not re.search('(^w+)=(.*)$', x)]
pargs = [get_bar_args(x) if x.startswith('bar.') else x for x in pargs]
kwargs = {k: get_bar_args(v) if v.startswith('bar.') else v for k, v in kwargs.items()}
print(f'{all_args=}n{kwargs=}n{pargs=}')
func_name = func_str.split("(")[0]
return func_name, pargs, kwargs

最新更新