Używam standardowego modułu json w Pythonie 2.6 do serializacji listy pływaków. Jednak otrzymuję takie wyniki:
>>> import json
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
Chcę, aby liczby zmiennoprzecinkowe były sformatowane za pomocą tylko dwóch cyfr dziesiętnych. Wynik powinien wyglądać następująco:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
Próbowałem zdefiniować własną klasę JSON Encoder:
class MyEncoder(json.JSONEncoder):
def encode(self, obj):
if isinstance(obj, float):
return format(obj, '.2f')
return json.JSONEncoder.encode(self, obj)
Działa to dla jedynego obiektu pływającego:
>>> json.dumps(23.67, cls=MyEncoder)
'23.67'
Ale niepowodzenie w przypadku obiektów zagnieżdżonych:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
Nie chcę mieć zewnętrznych zależności, więc wolę trzymać się standardowego modułu json.
Jak mogę to osiągnąć?
źródło
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
aby zobaczyć, jak.2f
nie jest przestrzegany.emituje
Nie jest konieczne śledzenie małp.
źródło
pretty_floats
funkcję i po prostu zintegrowałem ją z innym kodem.list( map(pretty_floats, obj) )
map
zwraca iterator, a nielist
Jeśli używasz Pythona 2.7, prostym rozwiązaniem jest po prostu jawne zaokrąglenie pływaków do żądanej precyzji.
Działa to, ponieważ w Pythonie 2.7 zaokrąglanie zmiennoprzecinkowe jest bardziej spójne . Niestety to nie działa w Pythonie 2.6:
Wymienione powyżej rozwiązania są obejściami dla wersji 2.6, ale żadne nie są w pełni wystarczające. Łata małpy json.encoder.FLOAT_REPR nie działa, jeśli środowisko wykonawcze Pythona używa wersji C modułu JSON. Klasa PrettyFloat w odpowiedzi Toma Wuttke działa, ale tylko wtedy, gdy kodowanie% g działa globalnie dla twojej aplikacji. % .15g jest trochę magiczne, działa, ponieważ precyzja float to 17 cyfr znaczących, a% g nie wypisuje zer końcowych.
Spędziłem trochę czasu, próbując stworzyć PrettyFloat, który pozwoliłby dostosować precyzję dla każdej liczby. To znaczy składnia taka jak
Nie jest łatwo zrobić to dobrze. Dziedziczenie po float jest niezręczne. Dziedziczenie z Object i używanie podklasy JSONEncoder z własną metodą default () powinno działać, z wyjątkiem tego, że moduł json wydaje się zakładać, że wszystkie niestandardowe typy powinny być serializowane jako ciągi. To znaczy: w wyniku otrzymujesz ciąg Javascript „0,33”, a nie liczbę 0,33. Być może jest jeszcze sposób, aby to zadziałało, ale jest trudniejsze, niż się wydaje.
źródło
Naprawdę niefortunne, że
dumps
nie pozwala ci nic zrobić, aby pływać. Jednakloads
tak. Więc jeśli nie masz nic przeciwko dodatkowemu obciążeniu procesora, możesz rzucić go przez koder / dekoder / koder i uzyskać właściwy wynik:źródło
parse_float
kwargu!Oto rozwiązanie, które działało dla mnie w Pythonie 3 i nie wymaga małpiego łatania:
Wynik to:
Kopiuje dane, ale z zaokrąglonymi zmiennymi.
źródło
Jeśli utkniesz w Pythonie 2.5 lub wcześniejszych wersjach: Trik z małpą poprawką nie wydaje się działać z oryginalnym modułem simplejson, jeśli przyspieszenia C są zainstalowane:
źródło
Możesz zrobić to, co musisz, ale nie jest to udokumentowane:
źródło
FLOAT_REPR
stałej wjson.encoder
module.Rozwiązanie Alexa Martelliego będzie działać w przypadku aplikacji jednowątkowych, ale może nie działać w przypadku aplikacji wielowątkowych, które muszą kontrolować liczbę miejsc dziesiętnych w wątku. Oto rozwiązanie, które powinno działać w aplikacjach wielowątkowych:
Możesz po prostu ustawić encoder.thread_local.decimal_places na żądaną liczbę miejsc dziesiętnych, a następne wywołanie json.dumps () w tym wątku użyje tej liczby miejsc dziesiętnych
źródło
Jeśli chcesz to zrobić w Pythonie 2.7 bez nadpisywania globalnego json.encoder.FLOAT_REPR, oto jeden sposób.
Następnie w Pythonie 2.7:
W Pythonie 2.6 nie działa, jak zauważa Matthew Schinckel poniżej:
źródło
Plusy:
Cons:
Kwadratowa złożoność.
źródło
Podczas importu standardowego modułu json wystarczy zmienić domyślny koder FLOAT_REPR. Naprawdę nie ma potrzeby importowania ani tworzenia instancji programu Encoder.
Czasami jest również bardzo przydatne do wypisania jako json najlepszej reprezentacji, jaką Python może odgadnąć za pomocą str. Dzięki temu istotne cyfry nie zostaną zignorowane.
źródło
Zgadzam się z @Nelsonem, że dziedziczenie po float jest niewygodne, ale być może rozwiązanie, które dotyczy tylko
__repr__
funkcji, można wybaczyć. Skończyło siędecimal
na tym, że w tym celu użyłem pakietu do ponownego sformatowania pływaków, gdy było to konieczne. Zaletą jest to, że działa to we wszystkich kontekstach, w którychrepr()
jest wywoływane, więc także podczas zwykłego drukowania list na przykład na standardowe wyjście. Ponadto precyzja jest konfigurowalna w czasie wykonywania, po utworzeniu danych. Wadą jest oczywiście to, że twoje dane muszą zostać przekonwertowane do tej specjalnej klasy float (ponieważ niestety nie możesz wydawać się małpą łatkąfloat.__repr__
). W tym celu podaję krótką funkcję konwersji.Kod:
Przykład użycia:
źródło
Używanie numpy
Jeśli faktycznie masz naprawdę długie elementy zmiennoprzecinkowe, możesz je poprawnie zaokrąglić w górę / w dół za pomocą numpy:
'[23.67, 23.97, 23.87]'
źródło
Właśnie wydałem fjson , małą bibliotekę Pythona, aby rozwiązać ten problem. Zainstaluj za pomocą
i używaj tak jak
json
, z dodatkiemfloat_format
parametru:źródło