Ich verwende das Standard- JSON-Modul in Python 2.6, um eine Liste von Floats zu serialisieren. Ich erhalte jedoch folgende Ergebnisse:
>>> import json
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
Ich möchte, dass die Gleitkommazahlen nur aus zwei Dezimalstellen bestehen. Die Ausgabe sollte folgendermaßen aussehen:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
Ich habe versucht, meine eigene JSON-Encoder-Klasse zu definieren:
class MyEncoder(json.JSONEncoder):
def encode(self, obj):
if isinstance(obj, float):
return format(obj, '.2f')
return json.JSONEncoder.encode(self, obj)
Dies funktioniert für ein einziges Float-Objekt:
>>> json.dumps(23.67, cls=MyEncoder)
'23.67'
Aber schlägt für verschachtelte Objekte fehl:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
Ich möchte keine externen Abhängigkeiten haben, deshalb bleibe ich lieber beim Standard-JSON-Modul.
Wie kann ich das erreichen?
quelle
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
23.67
zu sehen, wie dies.2f
nicht beachtet wird.emittiert
Kein Monkeypatching notwendig.
quelle
pretty_floats
Funktion entfernt und sie einfach in meinen anderen Code integriert.list( map(pretty_floats, obj) )
map
der Iterator zurückgegeben wird, nicht einlist
Wenn Sie Python 2.7 verwenden, besteht eine einfache Lösung darin, Ihre Floats einfach explizit auf die gewünschte Genauigkeit zu runden.
Dies funktioniert, weil Python 2.7 die Float-Rundung konsistenter gemacht hat . Leider funktioniert dies in Python 2.6 nicht:
Die oben genannten Lösungen sind Problemumgehungen für 2.6, aber keine ist völlig ausreichend. Das Affen-Patching json.encoder.FLOAT_REPR funktioniert nicht, wenn Ihre Python-Laufzeit eine C-Version des JSON-Moduls verwendet. Die PrettyFloat-Klasse in Tom Wuttkes Antwort funktioniert, aber nur, wenn die% g-Codierung für Ihre Anwendung global funktioniert. % .15g ist ein bisschen magisch, es funktioniert, weil die Float-Genauigkeit 17 signifikante Stellen beträgt und% g keine nachgestellten Nullen druckt.
Ich habe einige Zeit damit verbracht, ein PrettyFloat zu erstellen, mit dem die Genauigkeit für jede Zahl angepasst werden kann. Dh eine Syntax wie
Es ist nicht einfach, das richtig zu machen. Das Erben vom Schwimmer ist umständlich. Das Erben von Object und das Verwenden einer JSONEncoder-Unterklasse mit einer eigenen default () -Methode sollte funktionieren, außer das json-Modul scheint davon auszugehen, dass alle benutzerdefinierten Typen als Zeichenfolgen serialisiert werden sollten. Dh: Sie erhalten die Javascript-Zeichenfolge "0.33" in der Ausgabe, nicht die Zahl 0.33. Es mag noch einen Weg geben, diese Arbeit zu machen, aber es ist schwieriger als es aussieht.
quelle
Wirklich unglücklich,
dumps
dass Sie nichts tun können, um zu schweben. Tut esloads
jedoch. Wenn Ihnen die zusätzliche CPU-Auslastung nichts ausmacht, können Sie sie durch den Encoder / Decoder / Encoder werfen und das richtige Ergebnis erzielen:quelle
parse_float
Kwarg!Hier ist eine Lösung, die in Python 3 für mich funktioniert hat und kein Affen-Patching erfordert:
Ausgabe ist:
Es kopiert die Daten, jedoch mit gerundeten Gleitkommazahlen.
quelle
Wenn Sie mit Python 2.5 oder früheren Versionen nicht weiterkommen: Der Monkey-Patch-Trick scheint mit dem ursprünglichen simplejson-Modul nicht zu funktionieren, wenn die C-Beschleunigungen installiert sind:
quelle
Sie können tun, was Sie tun müssen, aber es ist nicht dokumentiert:
quelle
FLOAT_REPR
imjson.encoder
Modul keine Konstante gesehen .Die Lösung von Alex Martelli funktioniert für Apps mit einem Thread, möglicherweise jedoch nicht für Apps mit mehreren Threads, die die Anzahl der Dezimalstellen pro Thread steuern müssen. Hier ist eine Lösung, die in Multithread-Apps funktionieren sollte:
Sie können encoder.thread_local.decimal_places lediglich auf die gewünschte Anzahl von Dezimalstellen setzen, und der nächste Aufruf von json.dumps () in diesem Thread verwendet diese Anzahl von Dezimalstellen
quelle
Wenn Sie dies in Python 2.7 tun müssen, ohne den globalen json.encoder.FLOAT_REPR zu überschreiben, haben Sie folgende Möglichkeiten.
Dann in Python 2.7:
In Python 2.6 funktioniert es nicht ganz, wie Matthew Schinckel unten ausführt:
quelle
Vorteile:
Nachteile:
Quadratische Komplexität.
quelle
Beim Importieren des Standard-JSON-Moduls reicht es aus, den Standard-Encoder FLOAT_REPR zu ändern. Encoder-Instanzen müssen nicht wirklich importiert oder erstellt werden.
Manchmal ist es auch sehr nützlich, als json die beste Darstellung auszugeben, die Python mit str erraten kann. Dadurch wird sichergestellt, dass wichtige Ziffern nicht ignoriert werden.
quelle
Ich stimme @Nelson zu, dass das Erben von float umständlich ist, aber vielleicht ist eine Lösung, die nur die
__repr__
Funktion berührt , verzeihbar. Am Ende habe ich dasdecimal
Paket verwendet, um Floats bei Bedarf neu zu formatieren. Der Vorteil ist, dass dies in allen Kontexten funktioniert, in denenrepr()
aufgerufen wird, beispielsweise auch beim einfachen Drucken von Listen auf stdout. Außerdem kann die Genauigkeit zur Laufzeit konfiguriert werden, nachdem die Daten erstellt wurden. Nachteil ist natürlich, dass Ihre Daten in diese spezielle Float-Klasse konvertiert werden müssen (da Sie leider keinen Affen-Patch zu haben scheinenfloat.__repr__
). Dafür stelle ich eine kurze Konvertierungsfunktion zur Verfügung.Der Code:
Anwendungsbeispiel:
quelle
Mit numpy
Wenn Sie tatsächlich sehr lange Schwimmer haben, können Sie diese mit numpy richtig auf- und abrunden:
'[23.67, 23.97, 23.87]'
quelle
Ich habe gerade fjson veröffentlicht , eine kleine Python-Bibliothek, um dieses Problem zu beheben. Installieren mit
und verwenden Sie genau wie
json
mit der Hinzufügung desfloat_format
Parameters:quelle