Estoy usando el módulo json estándar en python 2.6 para serializar una lista de flotantes. Sin embargo, obtengo resultados como este:
>>> import json
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
Quiero que los flotantes estén formateados con solo dos dígitos decimales. La salida debería verse así:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
He intentado definir mi propia clase de codificador JSON:
class MyEncoder(json.JSONEncoder):
def encode(self, obj):
if isinstance(obj, float):
return format(obj, '.2f')
return json.JSONEncoder.encode(self, obj)
Esto funciona para un único objeto flotante:
>>> json.dumps(23.67, cls=MyEncoder)
'23.67'
Pero falla para los objetos anidados:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
No quiero tener dependencias externas, por lo que prefiero seguir con el módulo json estándar.
¿Cómo puedo conseguir esto?
fuente
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
ver cómo.2f
no se respeta.emite
No es necesario parchear los monos.
fuente
pretty_floats
función y simplemente la integré en mi otro código.list( map(pretty_floats, obj) )
map
devuelve un iterador, no unlist
Si está utilizando Python 2.7, una solución simple es simplemente redondear sus flotadores explícitamente a la precisión deseada.
Esto funciona porque Python 2.7 hizo que el redondeo flotante fuera más consistente . Desafortunadamente, esto no funciona en Python 2.6:
Las soluciones mencionadas anteriormente son alternativas para 2.6, pero ninguna es del todo adecuada. El parche de mono json.encoder.FLOAT_REPR no funciona si el tiempo de ejecución de Python usa una versión C del módulo JSON. La clase PrettyFloat en la respuesta de Tom Wuttke funciona, pero solo si la codificación% g funciona globalmente para su aplicación. El% .15g es un poco mágico, funciona porque la precisión flotante es de 17 dígitos significativos y% g no imprime ceros finales.
Pasé algún tiempo tratando de hacer un PrettyFloat que permitiera personalizar la precisión de cada número. Es decir, una sintaxis como
No es fácil hacerlo bien. Heredar de flotar es incómodo. Heredar de Object y usar una subclase JSONEncoder con su propio método default () debería funcionar, excepto que el módulo json parece asumir que todos los tipos personalizados deberían serializarse como cadenas. Es decir: terminas con la cadena Javascript "0.33" en la salida, no el número 0.33. Puede que todavía haya una forma de hacer que esto funcione, pero es más difícil de lo que parece.
fuente
Realmente lamentable que
dumps
no te permita hacer nada con los flotadores. Sin embargo loloads
hace. Entonces, si no le importa la carga adicional de la CPU, puede pasarla por el codificador / decodificador / codificador y obtener el resultado correcto:fuente
parse_float
kwarg!Aquí hay una solución que funcionó para mí en Python 3 y no requiere parches de mono:
La salida es:
Copia los datos pero con flotadores redondeados.
fuente
Si está atascado con Python 2.5 o versiones anteriores: el truco del parche de mono no parece funcionar con el módulo simplejson original si las aceleraciones de C están instaladas:
fuente
Puede hacer lo que necesite hacer, pero no está documentado:
fuente
FLOAT_REPR
constante en eljson.encoder
módulo.La solución de Alex Martelli funcionará para aplicaciones de un solo subproceso, pero es posible que no funcione para aplicaciones de subprocesos múltiples que necesitan controlar la cantidad de decimales por subproceso. Aquí hay una solución que debería funcionar en aplicaciones de subprocesos múltiples:
Simplemente puede establecer encoder.thread_local.decimal_places en el número de lugares decimales que desee, y la próxima llamada a json.dumps () en ese hilo usará ese número de lugares decimales
fuente
Si necesita hacer esto en Python 2.7 sin anular el json.encoder.FLOAT_REPR global, aquí hay una forma.
Luego, en Python 2.7:
En python 2.6, no funciona del todo como Matthew Schinckel señala a continuación:
fuente
Pros:
Contras:
Complejidad cuadrática.
fuente
Al importar el módulo json estándar, basta con cambiar el codificador predeterminado FLOAT_REPR. Realmente no es necesario importar o crear instancias de Encoder.
A veces también es muy útil generar como json la mejor representación que Python puede adivinar con str. Esto asegurará que no se ignoren los dígitos significativos.
fuente
Estoy de acuerdo con @Nelson en que heredar de float es incómodo, pero quizás una solución que solo toque la
__repr__
función podría ser perdonable. Terminé usando eldecimal
paquete para reformatear los flotadores cuando fuera necesario. La ventaja es que esto funciona en todos los contextos donderepr()
se está llamando, por lo que también cuando simplemente se imprimen listas en stdout, por ejemplo. Además, la precisión se puede configurar en tiempo de ejecución, después de que se hayan creado los datos. La desventaja es, por supuesto, que sus datos deben convertirse a esta clase flotante especial (ya que, desafortunadamente, parece que no puede parchearfloat.__repr__
). Para eso proporciono una breve función de conversión.El código:
Ejemplo de uso:
fuente
Usando numpy
Si realmente tiene flotadores realmente largos, puede redondearlos hacia arriba / abajo correctamente con numpy:
'[23.67, 23.97, 23.87]'
fuente
Acabo de lanzar fjson , una pequeña biblioteca de Python para solucionar este problema. Instalar con
y use como
json
, con la adición delfloat_format
parámetro:fuente