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

时间:2016-06-23 13:31:42

标签: python

假设我得到一个字符串到eval

temperature*x

我有两组变量 - 简单的变量:

easy_ns = {'x':3, 'y':4}

更难的一个:

harder = ['temperature', 'sumofall']

每个都需要花费大量时间来计算,除非它们是用户提供的表达式的一部分,否则我不想计算它们

E.g。我不想开始检测“温度”,除非我知道它是必需的

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

如何在评估之前从我的eval字符串中获取变量列表

我知道我可以尝试:eval()除了:我会得到一个:

NameError: name 'temperature' is not defined

是否有提取确切变量名称的pythonic方法?

是否有一种很好的方法来构建懒惰评估的命名空间?

这样的东西
namespace = {'x':3, 'y':4, 'temperature':lazy_temperature_function}

所以只有在评估我的表达时

res=eval('temperature*x')

是我的惰性温度函数,叫做

当然 - 我绝对必须使用'eval' - 这就是我发布这些问题的原因

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

2 个答案:

答案 0 :(得分:4)

如果你真的需要,你可以使用ast模块解析代码。 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_<name>的函数,否则此代码段会将值加载到当前作用域之外。如果<name>部分出现在表达式中,将调用此函数。

答案 1 :(得分:0)

你可以把它变成一个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

这样,除非你有意识地想要,否则你不会寻找执行。

但是,如果温度存在,您还可以确保仅输入lambda函数。你可以通过在文件开头为None指定温度来做到这一点,如果需要,只需输入lambda:

temperature = None
if temperature:
  a()
# do something else

如果您不想使用parens`fn() - 您还可以使用属性构建一个类来执行此操作。

class a(object):
  @property
  def temp_calc(self):
    return self.temp*5

通过这种方式,您可以执行以下操作:

temp_obj = a()
temp_obj.temp_calc

这将返回错误,因为您没有&#34; temp&#34;属性。但是如果需要,你可以分配它:

temp_obj.temp = 5
temp_obj.temp_calc
> 25

这里有很多选择,我希望这些帮助。