Format de flotteurs avec la norme module json
Je suis en utilisant le standard module json en python 2.6 pour sérialiser une liste de chars. Cependant, je suis à l'obtention de résultats comme ceci:
>>> import json
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
Je veux la flotte à être formaté avec seulement deux chiffres après la virgule. La sortie devrait ressembler à ceci:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
J'ai essayé de définir ma propre JSON Codeur classe:
class MyEncoder(json.JSONEncoder):
def encode(self, obj):
if isinstance(obj, float):
return format(obj, '.2f')
return json.JSONEncoder.encode(self, obj)
Cela fonctionne pour un seul objet float:
>>> json.dumps(23.67, cls=MyEncoder)
'23.67'
Mais ne parvient pas pour les objets imbriqués:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
Je ne veux pas avoir de dépendances externes, donc je préfère m'en tenir à la norme json module.
Comment puis-je y parvenir?
OriginalL'auteur Manuel Ceron | 2009-09-19
Vous devez vous connecter pour publier un commentaire.
Malheureusement, je crois que vous avez à faire ce singe de correction (qui, à mon avis, indique un défaut de conception dans la bibliothèque standard
json
package). E. g., ce code:émet:
que vous le désirez. Évidemment, il ne devrait être conçue de manière à remplacer
FLOAT_REPR
de sorte que TOUTE représentation d'un flotteur est sous votre contrôle si vous le souhaitez; mais, malheureusement, ce n'est pas la façon dont lejson
paquet a été conçu:-(.Cependant vous le faites, utilisez quelque chose comme %.15g ou %.12g au lieu de %.3f .
J'ai trouvé cet extrait de code dans un programmeur junior du code. Cela aurait créé un très grave mais bug subtil si elle n'avait pas été pris. Pouvez vous s'il vous plaît placer une alerte sur ce code d'expliquer les implications globales de ce monkey patching.
C'est une bonne hygiène de régler lorsque vous avez terminé:
original_float_repr = encoder.FLOAT_REPR
encoder.FLOAT_REPR = lambda o: format(o, '.2f')
print json.dumps(1.0001)
encoder.FLOAT_REPR = original_float_repr
en python 3, cela ne fonctionne plus
OriginalL'auteur Alex Martelli
émet
Pas monkeypatching nécessaire.
pretty_floats
fonction et simplement intégré dans mon code.En Python3 il donne "la Carte de l'objet n'est pas JSON serializable" erreur, mais vous pouvez résoudre la conversion de la carte() pour une liste avec
list( map(pretty_floats, obj) )
c'est parce que dans Python 3
map
retourne un itérateur, pas unlist
Ne fonctionne pas pour moi (Python 3.5.2, simplejson 3.16.0). Essayé avec de l' %.6g et [23.671234556, 23.971234556, 23.871234556], elle affiche toujours le nombre entier.
OriginalL'auteur Tom Wuttke
Si vous êtes à l'aide de Python 2.7, une solution simple est de simplement faire le tour de votre flotte explicitement à la précision souhaitée.
Cela fonctionne parce que Python 2.7 fait flotteur arrondissement plus cohérente. Malheureusement, cela ne fonctionne pas dans la version 2.6 de Python:
Les solutions mentionnées ci-dessus sont des solutions de contournement pour 2.6, mais aucun n'est tout à fait suffisante. Monkey patching json.codeur.FLOAT_REPR ne fonctionne pas si votre exécution Python utilise une version C du module JSON. Le PrettyFloat classe en la personne de Tom Wuttke la réponse de travaux, mais seulement si %g encodage fonctionne à l'échelle mondiale pour votre application. L' %.15g est un peu magique, ça marche parce que la précision est de 17 chiffres significatifs et %g ne pas imprimer des zéros à la fin.
J'ai passé du temps à essayer de faire un PrettyFloat qui a permis de personnalisation de précision pour chaque numéro. C'est à dire, une syntaxe comme
Il n'est pas facile à obtenir ce droit. Héritant de flotteur est maladroit. Héritant de l'Objet et à l'aide d'un JSONEncoder sous-classe avec son propre défaut() la méthode devrait fonctionner, sauf le module json semble assumer tous les types personnalisés devrait être sérialisé comme des chaînes de caractères. C'est à dire: vous vous retrouvez avec le Javascript chaîne "0.33" dans la sortie, pas le nombre de 0,33. Il y a peut être un moyen pour le moment de faire ce travail, mais il est plus difficile qu'il n'y paraît.
Espérons que cela fait passer autour de votre flotte plus léger - j'aime la façon dont nous pouvons éviter de jouer avec le JSON classes qui peut sucer.
OriginalL'auteur Nelson
Vraiment regrettable que
dumps
ne vous autorise pas à faire n'importe quoi à flotteurs. Cependantloads
. Donc, si vous n'avez pas l'esprit de l'extra charge CPU, vous pouvez le jeter à travers le codeur/décodeur/encodeur et obtenir le bon résultat:parse_float
kwarg!Le plus simple suggestion ici qui travaille également en 3.6.
Note l'expression "ne pas l'esprit de la CPU en plus de la charge". Certainement ne pas utiliser cette solution si vous avez beaucoup de données à sérialiser. Pour moi, l'ajout de ce seul fait un programme en faisant un non-trivial de calcul prendre 3X plus longtemps.
OriginalL'auteur Claude
Si vous êtes coincé avec Python 2.5 ou des versions antérieures: Le singe-patch astuce ne semble pas fonctionner avec l'original simplejson module si le C accélérations sont installés:
OriginalL'auteur Carlos Valiente
Vous pouvez faire ce que vous devez faire, mais il n'est pas documenté:
FLOAT_REPR
constante dans lejson.encoder
module.OriginalL'auteur Ned Batchelder
Voici une solution qui a fonctionné pour moi en Python 3 et ne nécessite pas de monkey patching:
De sortie est:
Il copie les données, mais avec des arrondis de flotteurs.
OriginalL'auteur jcoffland
Alex Martelli est la solution pour les mono-thread applications, mais peut ne pas fonctionner pour le multi-thread applications qui ont besoin de contrôler le nombre de décimales par thread. Voici une solution qui devrait fonctionner en multi threaded apps:
Vous pouvez simplement définir l'encodeur.thread_local.decimal_places pour le nombre de décimales que vous souhaitez, et le prochain appel à json.décharges() dans ce thread va utiliser que le nombre de décimales
OriginalL'auteur Anton I. Sipos
Si vous avez besoin de le faire en python 2.7 sans écraser le mondial json.codeur.FLOAT_REPR, voici un moyen.
Puis, dans python 2.7:
En python 2.6, il ne fonctionne pas comme Matthieu Schinckel points ci-après:
Cela fonctionne à tous les niveaux d'imbrication?
OriginalL'auteur Mike Fogel
Pour:
Contre:
Complexité quadratique.
OriginalL'auteur Sam Watkins
Lors de l'importation de la norme json d'un module, il suffit de changer la valeur par défaut codeur FLOAT_REPR. Il n'est pas vraiment la nécessité d'importer ou de créer de l'Encodeur instances.
Parfois est également très utile pour la sortie json la meilleure représentation de python peut deviner avec str. Cela permettra de s'assurer signifficant chiffres ne sont pas ignorés.
OriginalL'auteur F Pereira
Je suis d'accord avec @Nelson, héritant de flotteur est un peu étrange, mais peut-être une solution qui ne touche que le
__repr__
fonction peut être forgiveable. J'ai fini par utiliser ladecimal
paquet pour cette reformater flotteurs en cas de besoin. L'avantage est que cela fonctionne dans tous les contextes oùrepr()
est appelé, de même aussi quand simplement l'impression des listes de stdout par exemple. Aussi, la précision est d'exécution configurable, après la collecte de données a été créé. Inconvénient est bien sûr que vos données doivent être converties à cette classe integer (que malheureusement vous ne semblez pas à monkey patchfloat.__repr__
). Pour que je lui fournisse une brève fonction de conversion.Le code:
Exemple d'utilisation:
OriginalL'auteur user1556435