使用eval()与未定义变量
我想写一个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 个回答
你可以通过一些技巧让 eval
返回你想要的结果:
class Reflector(object):
def __getitem__(self, name):
return name
s = "{'test':test}"
print eval(s, globals(), Reflector())
这样做会得到:
{'test': 'test'}
不过,使用 eval
还是有一些风险的:如果你对这些字符串的来源有任何疑虑,那就可能会面临被黑客攻击的风险。
我会使用ast
模块,但不使用literal_eval
。遍历这个树结构,当你找到一个变量的引用时,就把这个变量的名字放到输出结果里。
我通过用独特的字符串替换变量名来解决了这个问题。这样做得到了我想要的结果:
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'}])]
(注意,这样的结果是我想要的,我不想把里面的字典合并在一起)。
唯一的小缺点是,我不能在输入字符串的任何地方使用我选择的替换字符串,但我相当确定这不会影响我想用这个脚本做的事情。