Python 格式随标准json模块浮动

Python 格式随标准json模块浮动,python,json,formatting,floating-point,Python,Json,Formatting,Floating Point,我正在使用Python2.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]' 我希

我正在使用Python2.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]'
我希望浮点数的格式仅为两个十进制数字。输出应如下所示:

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


我如何才能做到这一点?

您可以做您需要做的事情,但没有记录:

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

您可以做您需要做的事情,但没有记录:

>>> import json
>>> json.encoder.FLOAT_REPR = lambda f: ("%.2f" % f)
>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
注意:这在任何最新版本的Python中都不起作用。

不幸的是,我认为您必须通过猴子补丁来实现这一点(在我看来,这表明标准库
json
包中存在设计缺陷)。例如,该代码:

导入json
从json导入编码器
编码器.FLOAT_REPR=lambda o:格式(o,'.2f')
打印(json.dumps(23.67))
打印(json.dumps([23.67,23.97,23.87]))
发射:

23.67
[23.67, 23.97, 23.87]
如你所愿。显然,应该有一种体系结构的方法来覆盖
FLOAT\u REPR
,这样,如果您愿意,FLOAT的每个表示都在您的控制之下;但不幸的是,json包不是这样设计的:-(.

注意:这在任何最新版本的Python中都不起作用。

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

导入json
从json导入编码器
编码器.FLOAT_REPR=lambda o:格式(o,'.2f')
打印(json.dumps(23.67))
打印(json.dumps([23.67,23.97,23.87]))
发射:

23.67
[23.67, 23.97, 23.87]

如您所愿。显然,应该有一种体系结构方法来覆盖
FLOAT\u REPR
,这样,如果您愿意,FLOAT的每个表示都在您的控制之下;但不幸的是,这不是
json
包的设计方式:-(.

如果您坚持使用Python 2.5或更早的版本:如果安装了C加速卡,猴子补丁技巧似乎不适用于原始的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]'
>>> 
$python
Python 2.5.4(r254:679162009年1月20日11:06:13)
linux2上的[GCC 4.2.1(SUSE Linux)]
有关详细信息,请键入“帮助”、“版权”、“信用证”或“许可证”。
>>>导入simplejson
>>>简单版__
'2.0.9'
>>>simplejson.\u加速
>>>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=无
>>>simplejson.dumps([23.67,23.97,23.87])
'[23.67, 23.97, 23.87]'
>>> 

如果您一直使用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]'
>>> 
$python
Python 2.5.4(r254:679162009年1月20日11:06:13)
linux2上的[GCC 4.2.1(SUSE Linux)]
有关详细信息,请键入“帮助”、“版权”、“信用证”或“许可证”。
>>>导入simplejson
>>>简单版__
'2.0.9'
>>>simplejson.\u加速
>>>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=无
>>>simplejson.dumps([23.67,23.97,23.87])
'[23.67, 23.97, 23.87]'
>>> 
导入simplejson
类PrettyFloat(float):
定义报告(自我):
返回“%.15g”%self
def pretty_浮动(obj):
如果存在(obj,浮动):
返回预浮点数(obj)
elif isinstance(obj,dict):
在obj.items()中为k,v返回dict((k,pretty_float(v)))
elif isinstance(对象(列表,元组)):
返回列表(映射(漂亮的浮动,obj))
返回obj
打印(simplejson.dumps(pretty_float([23.67,23.97,23.87]))
散发

无需进行修补。

导入simplejson
类PrettyFloat(float):
定义报告(自我):
返回“%.15g”%self
def pretty_浮动(obj):
如果存在(obj,浮动):
返回预浮点数(obj)
elif isinstance(obj,dict):
在obj.items()中为k,v返回dict((k,pretty_float(v)))
elif isinstance(对象(列表,元组)):
返回列表(映射(漂亮的浮动,obj))
返回obj
打印(simplejson.dumps(pretty_float([23.67,23.97,23.87]))
散发


无需进行修补。

如果您使用的是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'
这是因为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'
上面提到的解决方案是2.6的变通方法,但没有一个是完全足够的。如果Python运行时使用C版本的json模块,Monkey patching json.encoder.FLOAT_REPR就不起作用。Tom Wuttke的答案中的PrettyFloat类起作用,但只有在%g编码对应用程序全局起作用的情况下,%.15g有点神奇,它是有效的ks,因为浮点精度为17位有效数字,%g不打印尾随零

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

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

要做到这一点并不容易。从float继承很难。从Object继承并使用带有自己default()方法的JSONEncoder子类应该可以,但json模块似乎认为所有自定义类型都应该序列化为字符串。例如:最终的Javascript字符串为“0.33”在输出中,不是数字0.33。可能还有一种方法可以实现这一点,但它比看起来更难。

如果您使用的是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'
这是因为Python 2.7制作的。不幸的是,这在Python 2.6中不起作用:
import json
json.encoder.FLOAT_REPR = lambda o: format(o, '.2f')

json.dumps([23.67, 23.97, 23.87]) #returns  '[23.67, 23.97, 23.87]'
import json
json.dumps([23.67, 23.9779, 23.87489])
# output is'[23.670000000000002, 23.977900000000002, 23.874890000000001]'

json.encoder.FLOAT_REPR = str
json.dumps([23.67, 23.9779, 23.87489])
# output is '[23.67, 23.9779, 23.87489]'
>>> json.dumps(json.loads(json.dumps([.333333333333, .432432]), parse_float=lambda x: round(float(x), 3)))
'[0.333, 0.432]'
import decimal
C = decimal.getcontext()

class decimal_formatted_float(float):
   def __repr__(self):
       s = str(C.create_decimal_from_float(self))
       if '.' in s: s = s.rstrip('0')
       return s

def convert_to_dff(elem):
    try:
        return elem.__class__(map(convert_to_dff, elem))
    except:
        if isinstance(elem, float):
            return decimal_formatted_float(elem)
        else:
            return elem
>>> import json
>>> li = [(1.2345,),(7.890123,4.567,890,890.)]
>>>
>>> decimal.getcontext().prec = 15
>>> dff_li = convert_to_dff(li)
>>> dff_li
[(1.2345,), (7.890123, 4.567, 890, 890)]
>>> json.dumps(dff_li)
'[[1.2345], [7.890123, 4.567, 890, 890]]'
>>>
>>> decimal.getcontext().prec = 3
>>> dff_li = convert_to_dff(li)
>>> dff_li
[(1.23,), (7.89, 4.57, 890, 890)]
>>> json.dumps(dff_li)
'[[1.23], [7.89, 4.57, 890, 890]]'
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]))
[23.63, 23.93, 23.84]
pip install fjson
{
  "a": 1,
  "b": 3.141593e+00
}
>>> json_dumps_with_two_digit_float({'a': 1.0})
'{"a": 1.00}'
from unittest.mock import patch
import json
# We need to ensure that c encoder will not be launched
@patch('json.encoder.c_make_encoder', None)
def json_dumps_with_two_digit_float(some_object):
    # saving original method
    of = json.encoder._make_iterencode
    def inner(*args, **kwargs):
        args = list(args)
        # fifth argument is float formater which will we replace
        args[4] = lambda o: '{:.2f}'.format(o)
        return of(*args, **kwargs)
    
    with patch('json.encoder._make_iterencode', wraps=inner):
        return json.dumps(some_object)