使用带有未定义变量的eval()

时间:2011-08-11 14:06:39

标签: python

我想创建一个Python脚本,它接受字典的字符串表示,并输出表示字典项的元组列表。问题是,我希望它接收尚未定义的变量。一个例子说明了这一点:

输入:{'test': test}

输出:[('test': test)]

我已经创建了一些要在字典中读取的代码并定义了尚未定义的变量,但是当我eval()结果时,它会替换变量的实际值而不是变量名。

以下是代码:

import sys
import re

if __name__ == "__main__":
    instr = sys.argv[1]
    success = False
    while not success:
        try:
            indict = eval(instr)
            success = True
        except NameError, e:
            defname = re.search("name '([^\']*)' is not defined", str(e))
            locals()[defname.group(1)] = 0
    print indict

我希望,对于上面定义的输入值,它打印出来的字典将完美地匹配输入字符串,但是它替换为test的值为0。因此脚本打印出来:

{'test': 0}

我已尝试ast.literal_eval,但它会为文字中的任何变量名称抛出ValueError: malformed string,即使已定义它。

因此,我的问题是:有没有办法在不丢失变量名的情况下转换输入字典?

5 个答案:

答案 0 :(得分:2)

我会使用ast模块,但不使用literal_eval。走遍树,找到变量引用时,只需将变量名称插入输出中即可。

答案 1 :(得分:1)

你可以欺骗eval为你提供你想要的东西:

class Reflector(object):
    def __getitem__(self, name):
        return name

s = "{'test':test}"

print eval(s, globals(), Reflector())

产生

{'test': 'test'}

关于eval的危险性的常见警告虽然:如果你对这些字符串的来源有任何疑问,那么你就是在为黑客攻打自己。

答案 2 :(得分:0)

我认为您遇到的问题是您要评估变量的名称而不是该变量的值。特别是,{'test':test}永远不会是打印字典的输出,因为test不是python中的值,它可能是变量的名称。最接近的是分配

locals()[defname.group(1)] = defname.group(1)

获取

{'test':'test'}

答案 3 :(得分:0)

我认为一个合理的解决方法是传递eval以有用的方式保留未定义变量的东西。您可以将任何dict子类传递给eval,并且可以覆盖dict的__missing__方法以返回“未定义的变量”对象:

>>> class Var(object):
...     def __init__(self, name):
...         self.name = name
...     def __repr__(self):
...         return "Var(%r)" % (self.name,)
... 
>>> class UndefSubst(dict):
...     def __missing__(self, key):
...         return Var(key)
... 
>>> foo = 'bar'
>>> eval("{'baz': quux, 1: foo}", UndefSubst(locals()))
{1: 'bar', 'baz': Var('quux')}

答案 4 :(得分:0)

我通过在唯一字符串中替换变量名来解决了这个问题。这产生了期望的结果:

import sys
import re

if __name__ == "__main__":
    instr = sys.argv[1]
    subs = []
    success = False
    while not success:
        try:
            indict = eval(instr)
            success = True
        except NameError, e:
            defname = re.search("name '([^\']*)' is not defined", str(e)).group(1)
            subs.append(defname)
            locals()[defname] = '//substitute{%d}' % (len(subs) - 1)
    outlist = indict.items()
    outstr = str(outlist)
    for i, sub in enumerate(subs):
        outstr = outstr.replace("'//substitute{%d}'" % i, sub)
    print outstr

输入:{"test": test}

输出:[('test', test)]

输入:{"test": [{"test": test}, {word: "word"}]}

输出:[('test', [{'test': test}, {word: 'word'}])](请注意,这是可取的,我不希望压缩内部字典。)

一个小缺点是我永远不能在输入字符串中的任何地方使用任何替换字符串,但我有理由相信这不会成为我希望使用此脚本的问题。