如何用正则或pandas内置解析器提取pandas.eval()中的变量
问题描述
我有大量可在脚本中调用并通过pandas.eval()求值的变量,这个方法本质是数学求值器。但执行eval()前需要从多数据源组装DataFrame,因此必须先解析给定表达式,找出所有命名变量,执行搜索方法合并到DataFrame后,再执行eval()。
- 曾尝试用正则表达式实现,但变量名含空格时会出问题,且我对正则不熟悉。
- 查看
pandas.eval()源码后,尝试复用pandas的解析方法,但发现pandas本身也未解决变量名含空格的问题。 - 倾向于复用pandas解析器(最稳健方案),但调用
pandas.core.computation.expr.Expr类时一直报错:pandas.core.computation.ops.UndefinedVariableError: name 'var1' is not defined,但pandas.eval()执行相同字符串却无此错误,对此感到困惑。
需求
获取eval表达式中所有允许含空格的变量列表,理想输入输出如下:
eval_string = "(var1 / var with spaces 2) + var with spaces 3" parsed_vars = magic_of_stack_overflow(eval_string) parsed_vars
输出:
["var1","var with spaces 2","var with spaces 3"]
更理想的是直接复用后续传入表达式的pandas原生解析器。
环境:Python 3.12,pandas 1.3.5
解决方案:复用pandas解析器提取变量(支持含空格的变量名)
通过构造自定义变量访问器和命名空间,绕开未定义变量的错误,同时捕获解析过程中识别到的所有变量名,完全复用pandas原生解析逻辑。
示例代码
import pandas as pd from pandas.core.computation.expr import Expr from pandas.core.computation.scope import Scope class CaptureVariables: def __init__(self): self.vars = set() def __getitem__(self, name): # 捕获被访问的变量名 self.vars.add(name) # 返回占位值避免解析报错 return 0 def extract_eval_variables(eval_string): capture = CaptureVariables() # 注入自定义命名空间,让所有变量访问指向Capture实例 scope = Scope(globals={}, locals=capture) try: # 构造Expr实例触发解析 expr = Expr(eval_string, scope=scope) expr.parse() except Exception: # 忽略求值阶段错误,仅保留已解析的变量 pass # 返回去重后的变量列表 return list(capture.vars) # 测试 eval_string = "(var1 / var with spaces 2) + var with spaces 3" parsed_vars = extract_eval_variables(eval_string) print(parsed_vars) # 输出: ['var1', 'var with spaces 2', 'var with spaces 3']
原理说明
CaptureVariables类通过__getitem__方法拦截所有变量访问请求,记录变量名并返回占位值,避免解析时因变量未定义报错。- 利用pandas的
Scope类将自定义变量访问器注入解析环境,确保解析器能正常遍历表达式的抽象语法树(AST)。 - 完全复用pandas原生解析逻辑,能正确识别带空格的变量名(前提是表达式无语法错误)。
注意事项
- 若表达式存在语法错误,解析会抛出异常,但仍能捕获已解析到的变量。
- 该方法适配pandas 1.3.5版本,不同版本的
Expr和Scope接口可能略有差异,可查看对应版本源码调整实现。
内容的提问来源于stack exchange,提问作者turbonate
相关产品推荐
相关产品推荐

