如何检测python eval表达式中的变量

如何检测python eval表达式中的变量,python,Python,假设我得到了一个要求值的字符串 temperature*x 我有两组变量-简单的一组: easy_ns = {'x':3, 'y':4} 更难的是: harder = ['temperature', 'sumofall'] 每一个都需要花费大量的时间来计算,我不想计算它们,除非它们是用户提供的表达式的一部分 例如,我不想开始检测“温度”,除非我知道这是必需的 我的命名空间中可能有一些“便宜”的变量,但其他变量我希望尽可能推迟计算 如何在求值之前从eval字符串中获取变量列表 res=eva

假设我得到了一个要求值的字符串

temperature*x
我有两组变量-简单的一组:

easy_ns = {'x':3, 'y':4}
更难的是:

harder = ['temperature', 'sumofall']
每一个都需要花费大量的时间来计算,我不想计算它们,除非它们是用户提供的表达式的一部分

例如,我不想开始检测“温度”,除非我知道这是必需的

我的命名空间中可能有一些“便宜”的变量,但其他变量我希望尽可能推迟计算

如何在求值之前从eval字符串中获取变量列表

res=eval('temperature*x')
我知道我可以试试:eval(),除了:我会得到:

NameError: name 'temperature' is not defined
有没有一种pythonic方法来提取确切的变量名

有没有一种很好的方法来构建用于延迟计算的命名空间

差不多

namespace = {'x':3, 'y':4, 'temperature':lazy_temperature_function}
所以只有当我的表达式被计算时

res=eval('temperature*x')
我的惰性温度函数被称为

当然,我必须使用“eval”,这就是我为什么要发布这些问题的原因


该场景是,我得到一个包含一组键和值的输入文件,然后用户可以提供一个表达式,他希望我根据这些值和一些生成的变量的组合来计算,我不想计算这些变量,除非用户将它们包括在他/她的表达式中

如果确实需要,您可以,使用模块解析代码。
ast.parse
帮助程序将为您提供代码的ast树表示:

import ast
code = "temperature*x"
st = ast.parse(code)
for node in ast.walk(st):
    if type(node) is ast.Name:
        print(node.id)
这将打印:

temperature
x
这只提取变量名,就像你说的。这似乎是第一步,但我不确定你想做什么,所以也许换一种方法更好

编辑:如果我正确理解您的问题,您希望仅当某些值出现在表达式中时才计算它们吗?我试过这样的方法:

>>> import ast
>>> code = "temperature*x"
>>> x = 5
>>> def lazy_temperature():
    return 78
... 
>>> names = [node.id for node in ast.walk(ast.parse(code))
             if type(node) is ast.Name]
>>> ns = {name: (globals()['lazy_%s' % name])()
                 if ('lazy_%s' % name) in globals()
                 else globals()[name] 
          for name in names}
>>> ns
{'x': 5, 'temperature': 78}
>>> eval(code, ns)
390

此代码段将从当前范围外加载值,除非有一个名为
lazy\uu
的函数。如果表达式中出现
部分,将调用此函数。

您可以将其设置为lambda函数,并在需要时执行它,例如:

a = lambda : 5*temperature
a()
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "<stdin>", line 1, in <lambda>
> NameError: global name 'temperature' is not defined
temperature = 100
a()
> 500

如果您不想使用parens`fn()-您还可以构建一个类来使用属性来实现这一点

class a(object):
  @property
  def temp_calc(self):
    return self.temp*5
通过这种方式,您可以执行以下操作:

temp_obj = a()
temp_obj.temp_calc
这将返回一个错误,因为您没有“temp”属性。但如果需要,您可以指定:

temperature = None
if temperature:
  a()
# do something else
temp_obj.temp = 5
temp_obj.temp_calc
> 25


这里有很多选择,我希望这几个能帮上忙。

你为什么要这样做?这听起来很难编程,也很难使用。你到底想做什么?你有没有考虑过绝对不去计算字符串?我不相信你“绝对必须使用eval”。或者你或者你正在使用其输出的另一个程序员做出了一个糟糕的决定,这让你陷入了困境。如果可能的话,您应该撤销该决定。如果温度在您调用字符串eval的范围内不是一个变量,那么如何计算温度?我不知道您实际上在尝试做什么。“有没有一种很好的方法来构建用于惰性评估的命名空间?”-没有理由您的命名空间不能是一个带有
\uuuu getitem\uuuu
惰性实现的字典包装器。下面的投票人是否愿意详细说明?只是好奇。我在中发布了一个修改后的问题,希望能吸引更多建设性的反馈。请把你的答案也贴在那里。我想我可能会在我的实现中使用你答案的两个部分。看起来懒惰的评估代码不起作用。如果更改延迟返回值,则代码的eval结果不会更改