使用标准json模块格式化浮点数
我在使用标准的 json模块,版本是python 2.6,想把一个浮点数列表转换成字符串。但是,我得到的结果是这样的:
>>> import json
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
我希望浮点数只保留两位小数,输出应该像这样:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
我尝试自己定义一个JSON编码器类:
class MyEncoder(json.JSONEncoder):
def encode(self, obj):
if isinstance(obj, float):
return format(obj, '.2f')
return json.JSONEncoder.encode(self, obj)
这个方法对单个浮点数有效:
>>> json.dumps(23.67, cls=MyEncoder)
'23.67'
但是对于嵌套对象就不行了:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
我不想依赖外部库,所以我更想使用标准的json模块。
我该怎么做才能实现这个呢?
18 个回答
37
真是太遗憾了,dumps
这个函数不能对浮点数做任何处理。不过 loads
是可以的。所以如果你不介意多消耗一点CPU资源的话,可以把数据先通过编码器,再通过解码器,最后再通过编码器,这样就能得到正确的结果:
>>> json.dumps(json.loads(json.dumps([.333333333333, .432432]), parse_float=lambda x: round(float(x), 3)))
'[0.333, 0.432]'
63
import simplejson
class PrettyFloat(float):
def __repr__(self):
return '%.15g' % self
def pretty_floats(obj):
if isinstance(obj, float):
return PrettyFloat(obj)
elif isinstance(obj, dict):
return dict((k, pretty_floats(v)) for k, v in obj.items())
elif isinstance(obj, (list, tuple)):
return list(map(pretty_floats, obj))
return obj
print(simplejson.dumps(pretty_floats([23.67, 23.97, 23.87])))
发出
[23.67, 23.97, 23.87]
不需要猴子补丁。
95
注意:这个方法在任何较新版本的Python中都不管用。
很遗憾,我认为你需要通过一种叫做“猴子补丁”的方式来解决这个问题(在我看来,这说明了标准库中json
包的设计缺陷)。比如,下面这段代码:
import json
from json import encoder
encoder.FLOAT_REPR = lambda o: format(o, '.2f')
print(json.dumps(23.67))
print(json.dumps([23.67, 23.97, 23.87]))
会输出:
23.67
[23.67, 23.97, 23.87]
正如你所希望的那样。显然,应该有一种合理的方式来重写FLOAT_REPR
,这样你就可以控制每一个浮点数的表示方式;但不幸的是,json
包并不是这样设计的。