如何获取eval表达式使用的变量?mock方案遇比较符报错如何解决?
解决方案
方案1:重写MagicMock支持比较运算符
继承MagicMock实现所有比较运算符的魔法方法,返回实例自身即可避免报错,同时不影响原有的__getitem__调用记录逻辑:
import mock class MockDict(mock.MagicMock): # 实现所有比较运算符,返回自身避免类型错误 def __lt__(self, other): return self def __gt__(self, other): return self def __le__(self, other): return self def __ge__(self, other): return self def __eq__(self, other): return self def __ne__(self, other): return self def get_eval_vars(expr): mock_var = MockDict() eval(expr, {"__builtins__": {}}, mock_var) # 去重后返回,避免同一个变量被多次调用返回重复结果 return list({args[0][0] for args in mock_var.__getitem__.call_args_list}) # 测试用例 print(get_eval_vars("a+b+10")) # 输出 ['a', 'b'] print(get_eval_vars("a<10")) # 输出 ['a']
该方案适配原有实现逻辑,改动量小,缺点是如果遇到MagicMock未实现的其他运算符(如位运算、成员运算)仍可能触发报错。
方案2:基于AST语法树解析(更稳定,无需执行表达式)
该方案不需要实际执行eval表达式,仅通过解析Python抽象语法树提取变量名,完全不存在运算符支持问题,也规避了执行表达式的潜在安全风险,是更推荐的实现:
import ast def get_eval_vars(expr): variables = set() # 遍历语法树所有节点 for node in ast.walk(ast.parse(expr, mode='eval')): # 匹配变量加载节点,排除函数调用、赋值等其他类型节点 if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load): variables.add(node.id) return list(variables) # 测试用例 print(get_eval_vars("a+b+10")) # 输出 ['a', 'b'] print(get_eval_vars("a<10")) # 输出 ['a'] print(get_eval_vars("(a * b) >= c and d in [1,2,3]")) # 输出 ['a', 'b', 'c', 'd']
内容的提问来源于stack exchange,提问作者Iñigo Moreno
相关产品推荐
相关产品推荐

