Деактивирайте научната нотация в изхода на python json.dumps

json.dumps извежда малки плаващи или десетични стойности, използвайки научна нотация, което е неприемливо за приложението json-rpc, към което се изпраща този изход.

>>> import json
>>> json.dumps({"x": 0.0000001})
'{"x": 1e-07}'

Вместо това искам този изход:

'{"x": 0.0000001}'

Би било идеално да се избегне въвеждането на допълнителни зависимости.


person Dust    schedule 21.09.2013    source източник
comment
Бих се поколебал да го нарека JSON-RPC приложение, ако то всъщност не разбира JSON...   -  person Ignacio Vazquez-Abrams    schedule 21.09.2013
comment
или плува за този въпрос...   -  person tacaswell    schedule 21.09.2013
comment
Единственият ви избор е да го конвертирате в низ. Ще свърши ли работа?   -  person Burhan Khalid    schedule 21.09.2013
comment
вижте stackoverflow.com/q/1447287/989121   -  person georg    schedule 21.09.2013


Отговори (4)


Един начин за форматиране

evil = {"x": 0.00000000001}

е да открадне "f" формататора на Decimal. Това е единственият лесен начин, който открих, който избягва както проблеми с изрязването, така и експоненти, но не пести място.

class FancyFloat(float):
    def __repr__(self):
        return format(Decimal(self), "f")

За да го използвате, можете да направите енкодер, който "децимализира" входа

class JsonRpcEncoder(json.JSONEncoder):
    def decimalize(self, val):
        if isinstance(val, dict):
            return {k:self.decimalize(v) for k,v in val.items()}

        if isinstance(val, (list, tuple)):
            return type(val)(self.decimalize(v) for v in val)

        if isinstance(val, float):
            return FancyFloat(val)

        return val

    def encode(self, val):
        return super().encode(self.decimalize(val))

JsonRpcEncoder().encode(evil)
#>>> '{"x": 0.00000000000999999999999999939496969281939810930172340963650867706746794283390045166015625}'

или, разбира се, можете да преместите децимализирането във функция и да я извикате преди json.dumps.

Така бих го направил, дори и да е куц метод.

person Veedrac    schedule 21.09.2013

Не мога да намеря отговор, за да избегна проблема, който преобразува 0.00001 в 1e-5, затова написах функция pretty_float_json_dumps. Работи добре в моя проект! Надявам се, че може да помогне на някого!!

def pretty_float_json_dumps(json_obj):
    dumps_str = ""

    if isinstance(json_obj, dict): 
        dumps_str += "{"
        for k,v in json_obj.items():
            dumps_str += json.dumps(k)+":"
            if isinstance(v, float): 
                float_tmp_str = ("%.16f" % v).rstrip("0")
                dumps_str += (float_tmp_str+'0' if float_tmp_str.endswith('.') else float_tmp_str) + ','
            elif isinstance(v, list) or isinstance(v, dict): 
                dumps_str += pretty_float_json_dumps(v)+','
            else:
                dumps_str += pretty_float_json_dumps(v)+','
        if dumps_str.endswith(','):
            dumps_str = dumps_str[:-1]
        dumps_str += "}"
    elif isinstance(json_obj, list): 
        dumps_str += "["
        for v in json_obj:
            if isinstance(v, float): 
                float_tmp_str = ("%.16f" % v).rstrip("0")
                dumps_str += (float_tmp_str+'0' if float_tmp_str.endswith('.') else float_tmp_str) + ','
            elif isinstance(v, list) or isinstance(v, dict): 
                dumps_str += pretty_float_json_dumps(v)+','
            else:
                dumps_str += pretty_float_json_dumps(v)+','
        if dumps_str.endswith(','):
            dumps_str = dumps_str[:-1]
        dumps_str += "]"
    else:
        dumps_str += json.dumps(json_obj)
    return dumps_str
person lee ray    schedule 09.05.2019
comment
Тай. Редактирах грешка при именуване и изглежда, че работи добре. Имате ли представа как работи това? - person lucid_dreamer; 02.03.2020

Това е допълнителен коментар, а не пълен отговор за избягване на научна нотация в json.dumps. С допълнителен кръг от анализ, опцията parse_float на json.loads може да помогне с някои неща, напр.

$ python
Python 3.7.10 | packaged by conda-forge | (default, Feb 19 2021, 16:07:37) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import json
>>> data = {"x": 0.0000001}
>>> json.dumps(data)
'{"x": 1e-07}'
>>> data = json.loads(json.dumps(data), parse_float=lambda x: round(float(x), 6))
>>> json.dumps(data)
'{"x": 0.0}'

Това само избягва научна нотация за малки стойности, които са закръглени до нула. Въпреки че това не е ефективно за големи масиви от данни, може да помогне в някои случаи на употреба. Това не избягва напълно научната нотация.

>>> data = {"x": 0.00001}
>>> data = json.loads(json.dumps(data), parse_float=lambda x: round(float(x), 6))
>>> json.dumps(data)
'{"x": 1e-05}'
person Darren Weber    schedule 23.03.2021

намерени някъде в интернет

import json
from json import encoder
encoder.FLOAT_REPR = lambda o: format(o, '.2f')

след това

>>> json.dumps({'gps': [51.5, 17.5]})
'{"gps": [51.5, 17.5]}'
person test30    schedule 16.11.2017
comment
Това не изглежда да работи: json.dumps({'a': 1/2000000}) ми дава '{"a": 5e-07}', докато въпросът беше как да се отърва от научната нотация. - person bartaelterman; 19.03.2018