如何检查并解析字符串形式的Python函数以提取验证指定信息
当然可以实现!不过因为咱们处理的是字符串形式的函数代码,得结合静态代码分析和(可选的)动态执行思路来应对不同复杂度的场景——毕竟实际函数可能会有条件分支、变量赋值、甚至外部依赖这类复杂逻辑。下面分需求拆解具体实现方案:
1. 验证函数返回值是否为Dict[str, str]
这里分两种方案,各有优劣,你可以根据场景选择:
方案A:静态代码分析(推荐,安全无执行风险)
利用Python的ast模块(抽象语法树)解析函数字符串,直接提取返回类型注解并验证。这种方式不需要执行代码,适合处理未知或不可信的函数字符串。
示例代码:
import ast import importlib from typing import Dict, get_type_hints def validate_return_type(func_str: str) -> bool: # 解析函数字符串为AST tree = ast.parse(func_str) # 找到函数定义节点(假设字符串里只有一个函数) func_def = tree.body[0] if not isinstance(func_def, ast.FunctionDef): raise ValueError("输入字符串不是有效的函数定义") # 临时写入文件以便解析类型提示 import tempfile import os with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: f.write(func_str) temp_path = f.name try: # 导入临时模块并获取类型提示 spec = importlib.util.spec_from_file_location("temp_func", temp_path) temp_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(temp_module) hints = get_type_hints(temp_module.my_function) return hints.get('return') == Dict[str, str] finally: os.unlink(temp_path)
注意:如果函数用了
from typing import Dict或者别名(比如from typing import Dict as StrDict),get_type_hints会自动处理这些导入逻辑,比直接解析AST节点的字符串更准确。
方案B:动态执行(适合可信代码)
如果函数是你自己编写的、可以安全执行的,那就更简单:直接把字符串转为可执行函数,然后检查其返回注解,甚至可以执行后验证实际返回值的类型。
示例代码:
from typing import Dict, get_type_hints def validate_return_type_dynamic(func_str: str) -> bool: # 把函数字符串加入全局命名空间 exec(func_str, globals()) # 获取函数对象 func = globals()['my_function'] # 检查返回注解 hints = get_type_hints(func) return hints.get('return') == Dict[str, str] # 额外:如果需要验证实际返回值的运行时类型(比如函数里有条件返回) # result = func() # return isinstance(result, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in result.items())
警告:
exec会执行任意代码,绝对不要用在不可信的函数字符串上,会有代码注入风险。
2. 获取返回值中字典的键(转为字符串列表)
这个需求的复杂度取决于函数返回的是字面量字典还是动态生成的字典:
情况1:返回的是字面量字典(静态分析即可)
如果函数直接返回一个字典字面量(比如你的示例return {"a": 1}),可以用ast直接解析return语句的内容,提取键:
示例代码:
import ast def extract_dict_keys_static(func_str: str) -> list[str]: tree = ast.parse(func_str) func_def = tree.body[0] # 找到return语句 return_stmt = None for stmt in func_def.body: if isinstance(stmt, ast.Return): return_stmt = stmt break if not return_stmt: raise ValueError("函数没有return语句") # 检查return的是否是字典字面量 if isinstance(return_stmt.value, ast.Dict): # 提取所有键的字符串值 keys = [] for key in return_stmt.value.keys: # 处理字符串类型的键 if isinstance(key, ast.Constant) and isinstance(key.value, str): keys.append(key.value) # 如果键是变量或表达式,静态分析无法直接获取,需要动态执行 else: raise ValueError("字典键不是静态字符串,无法通过静态分析提取") return keys else: raise ValueError("函数返回的不是字典字面量")
情况2:返回的是动态生成的字典(需要动态执行)
如果函数返回的是变量、或者通过计算生成的字典(比如return some_dict、return {x: y for x in range(3)}),静态分析就无能为力了,这时候只能执行函数后获取返回值的键:
示例代码:
def extract_dict_keys_dynamic(func_str: str) -> list[str]: exec(func_str, globals()) func = globals()['my_function'] result = func() if not isinstance(result, dict): raise ValueError("函数返回的不是字典") return list(result.keys())
同样注意:仅在函数可信时使用此方法,避免执行恶意代码。
总结
- 对于返回值类型验证:优先用静态分析(
ast+get_type_hints),安全且能处理大多数类型注解场景;如果需要验证实际返回值的运行时类型,再考虑动态执行。 - 对于提取字典键:如果是静态字面量字典,用
ast解析即可;如果是动态生成的字典,只能在安全前提下执行函数后提取。
内容的提问来源于stack exchange,提问作者J.Horcasitas

