格式浮动标准json模块

Man*_*ron 89 python floating-point formatting json

我在python 2.6中使用标准的json模块来序列化浮点数列表.但是,我得到的结果如下:

>>> import json
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
Run Code Online (Sandbox Code Playgroud)

我希望浮点数只用两位小数进行格式化.输出应如下所示:

>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
Run Code Online (Sandbox Code Playgroud)

我尝试过定义自己的JSON Encoder类:

class MyEncoder(json.JSONEncoder):
    def encode(self, obj):
        if isinstance(obj, float):
            return format(obj, '.2f')
        return json.JSONEncoder.encode(self, obj)
Run Code Online (Sandbox Code Playgroud)

这适用于唯一的浮动对象:

>>> json.dumps(23.67, cls=MyEncoder)
'23.67'
Run Code Online (Sandbox Code Playgroud)

但嵌套对象失败:

>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
Run Code Online (Sandbox Code Playgroud)

我不想有外部依赖,所以我更喜欢坚持使用标准的json模块.

我怎样才能做到这一点?

Ale*_*lli 75

不幸的是,我相信你必须通过猴子修补来实现这一点(我认为这表明标准库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])
Run Code Online (Sandbox Code Playgroud)

发出:

23.67
[23.67, 23.97, 23.87]
Run Code Online (Sandbox Code Playgroud)

如你所愿.显然,应该有一种架构方式来覆盖,FLOAT_REPR以便如果您希望浮动的每个表示都在您的控制之下; 但不幸的是,这不是json包装的设计方式:-(.

  • 但是,如果这样做,请使用%.15g或%.12g而不是%.3f. (23认同)
  • 我在一个初级程序员的代码中找到了这个片段.如果它没有被捕获,这将产生一个非常严重但微妙的错误.你能不能对这段代码发出警告,解释这个猴子修补的全局含义. (23认同)
  • 完成后将它设置回来是很好的卫生:`original_float_repr = encoder.FLOAT_REPR``secoder.FLOAT_REPR = lambda o:format(o,'.2f')``print json.dumps(1.0001)``encoder. FLOAT_REPR = original_float_repr` (12认同)
  • 使用Python的C版JSON编码器,此解决方案在Python 2.7中不起作用. (9认同)
  • 在python 3中,这不再起作用了 (6认同)
  • 正如其他人指出的那样,这至少在 Python 3.6+ 中不再有效。给 `23.67` 添加几个数字,看看 `.2f` 是如何不被遵守的。 (6认同)
  • 请注意,`original_float_repr`解决方案不是线程安全的。 (2认同)
  • 这在 python 3.8 中不起作用。格式被忽略。0.3 和 0.34566 以这种方式打印,而不是保留 2 位小数 (2认同)

小智 58

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]))
Run Code Online (Sandbox Code Playgroud)

发射

[23.67, 23.97, 23.87]
Run Code Online (Sandbox Code Playgroud)

不需要monkeypatching.

  • 对我不起作用(Python 3.5.2,simplejson 3.16.0)。用 %.6g 和 [23.671234556, 23.971234556, 23.871234556] 试了一下,它仍然打印整数。 (5认同)
  • 我喜欢这个解决方案; 更好的集成,并与2.7一起使用.因为我自己正在构建数据,所以我删除了`pretty_floats`函数并简单地将它集成到我的其他代码中. (2认同)

Nel*_*son 26

如果您使用的是Python 2.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'
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为Python 2.7使浮点舍入更加一致.不幸的是,这在Python 2.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'
Run Code Online (Sandbox Code Playgroud)

上面提到的解决方案是2.6的解决方法,但没有一个是完全足够的.如果您的Python运行时使用C版本的JSON模块,则猴子修补json.encoder.FLOAT_REPR不起作用.Tom Wuttke的答案中的PrettyFloat类可用,但前提是%g编码适用于您的应用程序.%.15g有点神奇,它起作用,因为浮点精度是17位有效数字而%g不打印尾随零.

我花了一些时间尝试制作一个PrettyFloat,允许自定义每个数字的精度.就是这样的语法

>>> json.dumps(PrettyFloat(1.0 / 3.0, 4))
'0.3333'
Run Code Online (Sandbox Code Playgroud)

要做到这一点并不容易.继承浮动很尴尬.继承自Object并使用带有自己的default()方法的JSONEncoder子类应该可以工作,除了json模块似乎假设所有自定义类型都应序列化为字符串.即:你最终得到输出中的Javascript字符串"0.33",而不是数字0.33.可能有一种方法可以使这项工作,但它比它看起来更难.


Cla*_*ude 13

真的很不幸,dumps不允许你做浮动任何事情.不过loads.因此,如果您不介意额外的CPU负载,您可以通过编码器/解码器/编码器将其抛出并获得正确的结果:

>>> json.dumps(json.loads(json.dumps([.333333333333, .432432]), parse_float=lambda x: round(float(x), 3)))
'[0.333, 0.432]'
Run Code Online (Sandbox Code Playgroud)

  • 这里最简单的建议也适用于 3.6。 (3认同)

Car*_*nte 9

如果您遇到Python 2.5或更早版本:如果安装了C加速,那么Monkey-patch技巧似乎不适用于原始的simplejson模块:

$ python
Python 2.5.4 (r254:67916, Jan 20 2009, 11:06:13) 
[GCC 4.2.1 (SUSE Linux)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import simplejson
>>> simplejson.__version__
'2.0.9'
>>> simplejson._speedups
<module 'simplejson._speedups' from '/home/carlos/.python-eggs/simplejson-2.0.9-py2.5-linux-i686.egg-tmp/simplejson/_speedups.so'>
>>> simplejson.encoder.FLOAT_REPR = lambda f: ("%.2f" % f)
>>> simplejson.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
>>> simplejson.encoder.c_make_encoder = None
>>> simplejson.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
>>> 
Run Code Online (Sandbox Code Playgroud)


Mik*_*ail 8

使用numpy

如果你实际上有很长的浮点数,你可以使用 numpy 正确地将它们向上/向下舍入:

import json 

import numpy as np

data = np.array([23.671234, 23.97432, 23.870123])

json.dumps(np.around(data, decimals=2).tolist())
Run Code Online (Sandbox Code Playgroud)

'[23.67, 23.97, 23.87]'


Ned*_*der 7

您可以执行您需要执行的操作,但未记录:

>>> import json
>>> json.encoder.FLOAT_REPR = lambda f: ("%.2f" % f)
>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
Run Code Online (Sandbox Code Playgroud)

  • 看起来很整洁,但似乎不适用于Python 3.6。特别是在json.encoder模块中没有看到FLOAT_REPR常量。 (2认同)

jco*_*and 6

这是在Python 3中对我有用的解决方案,不需要猴子补丁:

import json

def round_floats(o):
    if isinstance(o, float): return round(o, 2)
    if isinstance(o, dict): return {k: round_floats(v) for k, v in o.items()}
    if isinstance(o, (list, tuple)): return [round_floats(x) for x in o]
    return o


json.dumps(round_floats([23.63437, 23.93437, 23.842347]))
Run Code Online (Sandbox Code Playgroud)

输出为:

[23.63, 23.93, 23.84]
Run Code Online (Sandbox Code Playgroud)

它复制数据,但具有四舍五入的浮点数。


Nic*_*mer 5

我刚刚发布了fjson,一个小型 Python 库来解决这个问题。安装与

pip install fjson
Run Code Online (Sandbox Code Playgroud)

并像 一样使用json,并添加参数float_format

pip install fjson
Run Code Online (Sandbox Code Playgroud)
{
  "a": 1,
  "b": 3.141593e+00
}
Run Code Online (Sandbox Code Playgroud)