【问题标题】:How do I detect variables in a python eval expression如何检测 python eval 表达式中的变量
【发布时间】:2016-06-23 13:31:41
【问题描述】:

假设我得到一个要评估的字符串

temperature*x

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

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

还有一个更难的:

harder = ['temperature', 'sumofall']

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

例如除非我知道需要,否则我不想开始检测“温度”

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

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

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

NameError: name 'temperature' is not defined

有没有一种pythonic方法来提取确切的变量名?

有没有一种很好的方法来构建你的命名空间以进行延迟评估?

类似

namespace = {'x':3, 'y':4, 'temperature':lazy_temperature_function}

所以只有当我的表达式被评估时

res=eval('temperature*x')

是我的惰性温度函数调用

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

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

【问题讨论】:

  • 你为什么要这样对自己?这听起来很难编程和使用。你真正想做的是什么?
  • 您是否考虑过绝对不尝试评估字符串?
  • 我拒绝相信你“绝对必须使用 eval”。您或您正在使用其输出的其他程序员做出了一个糟糕的决定,使您陷入了困境。如果可能,您应该撤消该决定。
  • 如果temperature 在您对字符串调用 eval 的范围内的任何地方都不是变量,您如何拥有它?我不知道你到底想做什么。
  • “有没有一种很好的方法来构建你的命名空间以进行惰性求值?” - 你的命名空间没有理由不能成为具有 @ 惰性实现的字典包装器987654328@.

标签: python


【解决方案1】:

如果您真的需要,您可以使用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

这个 sn-p 会将值加载到当前范围之外,除非有一个名为 lazy_<name> 的函数。如果<name> 部分出现在表达式中,将调用此函数。

【讨论】:

  • 投反对票的人愿意详细说明吗?只是好奇。
  • 我在stackoverflow.com/questions/37995920/… 中发布了一个修改过的问题,希望能吸引更多建设性的反馈。也请在那里发布您的答案。我想我可能会在我的实现中使用你的答案的两个部分
  • 懒惰的 eval 代码似乎不起作用。如果更改惰性返回值,代码的评估结果不会改变
【解决方案2】:

您可以将其设为 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

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

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

这样,你可以做到以下几点:

temp_obj = a()
temp_obj.temp_calc

这将返回一个错误,因为您没有“temp”属性。但如果需要,您可以分配它:

temp_obj.temp = 5
temp_obj.temp_calc
> 25

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-23
    • 1970-01-01
    • 2019-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多