使用标准的json模块格式化浮点数

2024-04-27 08:42:55 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用python 2.6中的标准json module序列化浮动列表。但是,我得到的结果是这样的:

>>> 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模块。

我怎样才能做到这一点?


Tags: 对象importselfjsonobj列表标准return
3条回答

如果您使用的是Python2.7,一个简单的解决方案是简单地将浮点显式舍入到所需的精度。

>>> sys.version
'2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)]'
>>> json.dumps(1.0/3.0)
'0.3333333333333333'
>>> json.dumps(round(1.0/3.0, 2))
'0.33'

这是因为Python 2.7生成了float rounding more consistent。不幸的是,这在Python2.6中不起作用:

>>> sys.version
'2.6.6 (r266:84292, Dec 27 2010, 00:02:40) \n[GCC 4.4.5]'
>>> json.dumps(round(1.0/3.0, 2))
'0.33000000000000002'

上面提到的解决方案是2.6的解决方案,但没有一个是完全足够的。如果Python运行时使用json模块的C版本,则Monkey patching json.encoder.FLOAT_REPR不起作用。Tom Wuttke答案中的PrettyFloat类可以工作,但前提是%g编码对应用程序全局有效。.15g有点神奇,因为浮点精度是17个有效数字,%g不打印尾随零。

我花了一些时间尝试制作一个PrettyFloat,它允许为每个数字定制精度。例如

>>> json.dumps(PrettyFloat(1.0 / 3.0, 4))
'0.3333'

要把这件事做好并不容易。从float继承是很尴尬的。从对象继承并使用JSONEncoder子类及其自己的default()方法应该可以工作,除了json模块似乎假定所有自定义类型都应该序列化为字符串。Ie:最终输出的是Javascript字符串“0.33”,而不是数字0.33。也许还有一种方法可以让它发挥作用,但它比看上去更难。

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 map(pretty_floats, obj)  # in Python3 do: list(map(pretty_floats, obj))
    return obj

print simplejson.dumps(pretty_floats([23.67, 23.97, 23.87]))

发射

[23.67, 23.97, 23.87]

不需要修补。

不幸的是,我认为您必须通过monkey补丁来实现这一点(在我看来,这表明标准库json包中存在设计缺陷)。E、 g.此代码:

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包的设计方式:-(。

相关问题 更多 >