标题: eval()遭遇"NameError: name 'true' is not defined"
创建: 2019-06-21 11:17 更新: 2022-08-05 11:13 链接: https://scz.617.cn/python/201906211117.txt
有个别人开发的Python程序,某些人一直用得好好的,某天突然提示:
"NameError: name 'true' is not defined"
受人之托尝试解决。我不是Python达人,临时抱佛脚,用printf大法排查。关于这个, 说个题外话:
《别再用print输出来调试代码了》 https://mp.weixin.qq.com/s/6jwAdHFT7eQIW8zVoFbeSA https://github.com/cool-RR/PySnooper (可以pip安装)
后来排查到是用eval()解析JSON格式的字符串时报错。合理猜测,被解析的JSON格式 字符串于某日开始发生了变化。
这是个老问题,先说解决办法。
将
eval( s )
改成
eval( s, {"true":True,"false":False,"null":None} )
话说用eval()太危险,要是数据源插个格式化、删除、远控或者信息收集回传功能代 码下来,用这个Python程序的人可怎么活啊?对付前三者,可以用虚拟机,提前建好 快照以防万一,对付第四者就要注意了,不要在本地任何文件中留下个人信息。言尽 于此,自求多福。
处理JSON格式字符串,一般有两种办法,eval()和json.loads(),下面对二者进行一 些对比。
eval()、json.loads()均可将JSON格式的str转成dict、list等object。
$ python3
import json s='{"one":1,"two":2,"three":3}' e=eval(s) j=json.loads(s) type(s)
type(e) type(j) e {'one': 1, 'two': 2, 'three': 3} j s='[{"one":1,"two":2,"three":3},{"four":4,"five":5,"six":6}]' e=eval(s) j=json.loads(s) type(s)
type(e) type(j) e [{'one': 1, 'two': 2, 'three': 3}, {'four': 4, 'five': 5, 'six': 6}] j [{'one': 1, 'two': 2, 'three': 3}, {'four': 4, 'five': 5, 'six': 6}]
eval()、json.loads()相当于对str反序列化,相应的序列化操作是json.dumps(), 即将dict、list等object转成str。
eval()与json.loads()并不等价,eval()功能强于json.loads(),也更不安全,事实 上所有语言的eval()都是一个危险功能,尤其形参s为他人所控时。eval()处理形参s 时,先将s理解成Python代码,对之解释执行,这会将s转成PVM(Python虚拟机)字节 码。json.loads()只对形参s进行字符串解析处理,消耗的资源小于eval()。
让我们把目标局限于对str反序列化。json.loads()不认str内部的单引号,只认双引 号,eval()无此限制。
s='{"name":\'scz\'}' e=eval(s) j=json.loads(s) Traceback (most recent call last): ... json.decoder.JSONDecodeError: Expecting value: line 1 column 9 (char 8) e {'name': 'scz'} type(e)
s="{'name':'scz'}" e=eval(s) j=json.loads(s) Traceback (most recent call last): ... json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1) e {'name': 'scz'} type(e)
Python2的json.loads()将双引号中的字符串转成unicode而不是str,这是个坑。
$ python2
import json s='{"name":"scz"}' e=eval(s) j=json.loads(s) e {'name': 'scz'} j {u'name': u'scz'} e['name'] 'scz' j['name'] u'scz' type(e['name'])
type(j['name'])
$ python3
import json s='{"name":"scz"}' e=eval(s) j=json.loads(s) e {'name': 'scz'} j {'name': 'scz'} e['name'] 'scz' j['name'] 'scz' type(e['name'])
type(j['name'])
Python3的json.loads()将双引号中的字符串转成str,与eval()在此动作上没有区别。 据我实测,处理中文时json.loads()兼容性不如eval(),所以有些人还是冒着大风险 使用eval()。
eval()与json.loads()另有一个坑爹的区别。
$ python3
import json s='[true,false,null]' e=eval(s) Traceback (most recent call last): ... NameError: name 'true' is not defined j=json.loads(s) j [True, False, None] type(j)
json.loads()认true、false、null这种写法,反序列化之后转成True、False、None。 eval()不认,报错:
NameError: name 'true' is not defined
Python2、Python3的eval()都有这病,可以治。
s='[true,false,null]' e=eval(s,{"true":True}) Traceback (most recent call last): ... NameError: name 'false' is not defined e=eval(s,{"true":True,"false":False}) Traceback (most recent call last): ... NameError: name 'null' is not defined e=eval(s,{"true":True,"false":False,"null":None}) e [True, False, None] type(e)
上例给eval()指定了第二形参globals,也可以使用第三形参locals。
s='[true,false,null]' e=eval(s,None,{"true":True,"false":False,"null":None}) e [True, False, None]
强烈建议在Python3中使用json.loads(),尽最大可能避免使用eval()。