Python 参数化提取到的内容怎么让 Python 识别成函数

sunzhang · 2023年08月11日 · 最后由 fengling520 回复于 2023年09月03日 · 6749 次阅读

跪求各位大佬,能不能帮忙解答一下

我的需求是:

  • 我使用 yaml 写用例,里面有使用参数化如:${get_phone()},这个 get_phone() 是自己封装的一个方法,获取手机号,我的接口需要传入随机的手机号

我的想法是:

  • 通过正则表达式把 get_phone() 提取出来,然后让 Python 识别到 get_phone() 是一个函数,然后执行函数拿到函数返回值,
  • 然后把函数返回值跟原来的内容进行替换,
  • 这样用例就能拿到手机号

我的问题是:

  • 提取出来的函数,怎么能让 Python 识别出来,可以正常执行函数
共收到 18 条回复 时间 点赞

函数名加一个约定的标志 带这个标志的就可以尝试当成函数解析 其实就是一个 switch case 就可以了

我理解的是你现在参数化里面有一块是 ${get_phone()},然后你用正则表达式把{}里面的这个 get_phone() 拿出来了,但是你想把这个提取出来的字符串当作函数执行是吧?
我这有个方法可以试下,你现在拿出来的 get_phone() 是个字符串类型的,你可以用 eval 再解包一次,因为你现在有这个函数,解包后应该就能识别出来了,我之前用过一次这样的方式,可行。

fiskeryang 回复

就是不知道怎么函数解析,只要凡是通过我这个正则提取出来就进行函数解析,问题是不知道怎么解析,怎么让 Python 知道他是一个函数呢

eval(“get_phone()”)

lc0118 回复

正解,eval() 函数可以满足

sunzhang 回复

//参考一下 用 java 写的

// 从 ${}中匹配到的参数 如果是以__开头则尝试进行函数匹配 这里的参数是 ${__yesterday(yyyy-MM-dd)}

Pattern matchFuncPattern = Pattern.compile("__(.+?\\(.*?\\))");
        Matcher matcherFunc = matchFuncPattern.matcher(param);
        while (matcherFunc.find()) {
            String methodStr = matcherFunc.group(1);
            String method = methodStr.substring(0, methodStr.indexOf("("));
            //urlencode 函数直接使用整个methodParam,参数中有可能会带有逗号
            String methodParam = methodStr.substring(methodStr.indexOf("(") + 1, methodStr.lastIndexOf(")"));
            //其他函数使用逗号来区分多个参数
            String[] methodParams = methodParam.split(",");
            //funcValue用来存储内置函数生成的值
            String funcValue = "";
            switch (method.toLowerCase()) {
                case "yesterday":
                //使用函数生成的值替换占位符,这里是 2023-08-10

上面大佬说的😀

eval()

感谢大佬的协助,使用 eval() 解决了我的问题,

又出现个问题,正则提取出来的 Python 函数执行前需要先导入,否则都在提示不存在

  • 我的解决方式是:我把所有用到的函数写在一个模块 A 里,
  • 然后在调用函数的模块提前把这 A 提前手动导入
  • 这个方式有点笨

不知道有没有更智能一点的方式

用 eval 简单点:

1、eval
def get_sum(a,b):
return a+b
s=eval('get_sum')(2,3)
print(s)

# 类中的方法
class myC:

def get_sum(self,a,b):
return a+b
s=eval('myC().get_sum')(1,2)
print(s)

2 如果类中也可以用 getattr 方法获取
class Context():

x=10
def get_name(self,*names):
for name in names:
print("hello "+name +"!")

def setattr_y(self):
setattr(self,"y",'yy')

c=Context()

# 带可变参数执行对象的方法
value="yq,ym,yy"
getattr(Context(),"get_name")(*value.split(','))

3、也可以通过 vars 从模块字典中取。

这种其实最适合让 GPT 回答

sunzhang 回复

你可以尝试使用 Python 中的自动模块导入功能来更智能地管理函数的导入。Python 提供了importlib模块,它可以在运行时动态地导入模块。这样你可以根据需要选择性地导入函数,而不需要提前手动导入整个模块。

下面是一个示例代码,演示了如何使用importlib动态地导入函数:

import importlib

def execute_function(module_name, function_name, *args):
    try:
        module = importlib.import_module(module_name)
        function = getattr(module, function_name)
        return function(*args)
    except (ImportError, AttributeError):
        # 处理导入错误或函数不存在的情况
        return None

# 以调用模块A中的函数func1为例
result = execute_function('moduleA', 'func1', arg1, arg2)
if result is not None:
    # 执行成功
    print(result)
else:
    # 函数不存在或导入失败
    print("函数不存在或导入失败")

在上面的示例中,execute_function函数接受一个模块名、函数名以及所需的参数。它使用importlib.import_module来动态地导入名为module_name的模块,然后使用getattr函数从该模块中获取名为function_name的函数。然后,你可以执行该函数,并处理可能的导入错误和函数不存在的情况。

通过使用importlib模块,你可以更灵活地处理函数导入,并根据需要选择性地导入特定的函数,而无需手动导入整个模块。

要让 Python 识别并执行函数,你可以使用eval()函数或者exec()语句。这两个功能强大但也潜在地危险,因为它们可以执行任意的 Python 代码。确保你从可信任的源头获取函数名和代码,避免安全风险。

下面是一个示例代码,展示了如何使用re模块和eval()函数来解析 YAML 文件,并执行函数:

import re

# 假设这是你的 YAML 用例
yaml_content = """
- name: 接口测试
  endpoint: /api/xyz
  data:
    phone: ${get_phone()}
"""

# 假设这是你的自定义函数
def get_phone():
    # 执行获取手机号的逻辑,并返回
    phone_number = "1234567890"
    return phone_number

# 使用正则表达式匹配函数名
pattern = re.compile(r"\${([\w_]+)\(\)}")

# 替换函数调用为实际的返回值
def replace_func(match):
    # 获取函数名
    func_name = match.group(1)

    # 根据函数名执行函数并返回结果
    result = eval(func_name)  # 使用 eval() 执行函数

    return str(result)

# 替换函数调用为实际的返回值
replaced_yaml = re.sub(pattern, replace_func, yaml_content)

print(replaced_yaml)

在上面的示例中,我们首先定义了一个get_phone()函数来获取手机号。然后,我们使用正则表达式和re模块来匹配${get_phone()}这样的函数调用,并使用eval()函数执行函数并替换调用。

请注意,上面的示例只是一个简单的示范,你可能需要根据自己的具体需求进行调整。另外,使用eval()函数或者exec()语句需要谨慎,确保只执行可信任的代码。

大海 回复

感谢大佬的不吝赐教,本人还有一个疑问:

result = execute_function('moduleA', 'func1', arg1, arg2) 这个调用导包的操作是放在什么时候,

用例 yaml 中只能提取到方法名 ${get_phone()},那模块名从哪里来呢
我只能想到在 yaml 中这么写 ${moduleA.get_phone()},
提取到内容后 moduleA.get_phone(),分解出模块名和函数名

是这么理解吗

sunzhang 回复

是的,你的理解是正确的。在解析用例 YAML 文件时,你可以将模块名和函数名都包含在参数中,例如 ${moduleA.get_phone()}

在解析用例时,你可以通过正则表达式或其他方法提取出 ${moduleA.get_phone()},然后对其进行解析和分解得到模块名和函数名。

以下是一个示例代码,演示了如何解析 ${moduleA.get_phone()} 并执行对应的函数:

import re

# 解析并执行函数
def execute_function(module_name, function_name, *args):
    # 模拟执行函数的逻辑
    result = f"Executing {module_name}.{function_name} with arguments: {args}"
    return result

# 原始用例内容
test_case = """
api: /user/register
data:
  mobile: ${moduleA.get_phone()}
  name: John Doe
"""

# 使用正则表达式匹配并提取函数调用
regex_pattern = r'\$\{(.+?)\}'
matches = re.findall(regex_pattern, test_case)

# 遍历匹配结果,解析模块名和函数名,并执行对应的函数
for match in matches:
    module_function = match.strip()
    module_name, function_name = module_function.split('.', 1)  # 分解模块名和函数名
    function_args = ()  # 如果需要传参,可以解析参数列表
    function_result = execute_function(module_name, function_name, *function_args)  # 执行函数
    test_case = test_case.replace(f"${{{module_function}}}", function_result)

print(test_case)

在这个示例中,我们将 ${moduleA.get_phone()} 作为函数调用的格式,解析出模块名和函数名。然后,我们调用 execute_function() 来执行对应的函数。

需要注意的是,这里只是一个简单的示例,假设函数的模块名是固定的。如果模块名是动态的,你可能需要根据实际情况进行相应的调整。另外,对于函数参数也可以在解析时进行进一步处理,以满足你的需求。

sunzhang 回复

在这种情况下,你需要在解析 YAML 文件的时候,根据需要执行的函数名称来确定所需导入的模块。可以通过预先定义一个映射关系,将函数名称与对应的模块进行关联。

以下是一个示例代码,演示了如何根据函数名动态导入模块并执行对应的函数:

import re
import importlib

# 解析并执行函数
def execute_function(module_name, function_name, *args):
    # 动态导入模块
    module = importlib.import_module(module_name)
    # 获取函数对象
    function = getattr(module, function_name)
    # 执行函数
    result = function(*args)
    return result

# 原始用例内容
test_case = """
api: /user/register
data:
  mobile: ${moduleA.get_phone()}
  name: John Doe
"""

# 使用正则表达式匹配并提取函数调用
regex_pattern = r'\$\{(.+?)\}'
matches = re.findall(regex_pattern, test_case)

# 遍历匹配结果,解析模块名和函数名,并执行对应的函数
for match in matches:
    module_function = match.strip()
    module_name, function_name = module_function.split('.', 1)  # 分解模块名和函数名
    function_args = ()  # 如果需要传参,可以解析参数列表
    function_result = execute_function(module_name, function_name, *function_args)  # 执行函数
    test_case = test_case.replace(f"${{{module_function}}}", function_result)

print(test_case)

在这个示例中,我们使用 importlib.import_module() 方法根据模块名动态导入模块。然后,我们使用 getattr() 方法获取函数对象,并执行该函数。

需要注意的是,这里使用的是动态导入模块的方式,你需要确保模块名是可靠的、可信任的,并且模块包含了需要调用的函数。另外,对于函数的参数也可以在解析时进行进一步处理,以满足你的需求。

可以用反射

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册