使用eval()与未定义变量

2 投票
5 回答
2687 浏览
提问于 2025-04-16 23:23

我想写一个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 个回答

2

你可以通过一些技巧让 eval 返回你想要的结果:

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

s = "{'test':test}"

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

这样做会得到:

{'test': 'test'}

不过,使用 eval 还是有一些风险的:如果你对这些字符串的来源有任何疑虑,那就可能会面临被黑客攻击的风险。

2

我会使用ast模块,但不使用literal_eval。遍历这个树结构,当你找到一个变量的引用时,就把这个变量的名字放到输出结果里。

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'}])](注意,这样的结果是我想要的,我不想把里面的字典合并在一起)。

唯一的小缺点是,我不能在输入字符串的任何地方使用我选择的替换字符串,但我相当确定这不会影响我想用这个脚本做的事情。

撰写回答