标题: 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 {'one': 1, 'two': 2, 'three': 3} >>> 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()。