Estou usando o módulo json padrão em python 2.6 para serializar uma lista de flutuadores. No entanto, estou obtendo resultados como este:
>>> import json
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
Eu quero que os flutuadores sejam formatados com apenas dois dígitos decimais. A saída deve ser semelhante a esta:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
Tentei definir minha própria classe 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)
Isso funciona para um único objeto flutuante:
>>> json.dumps(23.67, cls=MyEncoder)
'23.67'
Mas falha para objetos aninhados:
>>> json.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
Não quero ter dependências externas, então prefiro ficar com o módulo json padrão.
Como posso conseguir isso?
fonte
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
para ver como.2f
não é respeitado.emite
Nenhum monkeypatching necessário.
fonte
pretty_floats
função e simplesmente a integrei em meu outro código.list( map(pretty_floats, obj) )
map
retorna o iterador, não umlist
Se você estiver usando o Python 2.7, uma solução simples é simplesmente arredondar seus flutuadores explicitamente para a precisão desejada.
Isso funciona porque o Python 2.7 tornou o arredondamento de flutuação mais consistente . Infelizmente, isso não funciona no Python 2.6:
As soluções mencionadas acima são soluções alternativas para o 2.6, mas nenhuma é totalmente adequada. O Monkey patching json.encoder.FLOAT_REPR não funciona se o tempo de execução do Python usa uma versão C do módulo JSON. A classe PrettyFloat na resposta de Tom Wuttke funciona, mas apenas se a codificação% g funcionar globalmente para seu aplicativo. O% .15g é um pouco mágico, ele funciona porque a precisão do float é de 17 dígitos significativos e% g não exibe zeros à direita.
Passei algum tempo tentando fazer um PrettyFloat que permitisse customização de precisão para cada número. Ou seja, uma sintaxe como
Não é fácil acertar. Herdar da flutuação é estranho. Herdar de Object e usar uma subclasse JSONEncoder com seu próprio método default () deve funcionar, exceto que o módulo json parece assumir que todos os tipos personalizados devem ser serializados como strings. Ou seja: você acaba com a string Javascript "0,33" na saída, não o número 0,33. Pode haver uma maneira de fazer isso funcionar, mas é mais difícil do que parece.
fonte
É uma pena que
dumps
não permita que você faça nada para flutuar. No entanto,loads
sim. Portanto, se você não se importar com a carga extra da CPU, poderá jogá-la no codificador / decodificador / codificador e obter o resultado correto:fonte
parse_float
kwarg!Aqui está uma solução que funcionou para mim no Python 3 e não requer patching do macaco:
O resultado é:
Ele copia os dados, mas com flutuações arredondadas.
fonte
Se você estiver travado com o Python 2.5 ou versões anteriores: o truque do monkey-patch não parece funcionar com o módulo simplejson original se os speedups C estiverem instalados:
fonte
Você pode fazer o que precisa, mas não está documentado:
fonte
FLOAT_REPR
constante nojson.encoder
módulo.A solução de Alex Martelli funcionará para aplicativos single threaded, mas pode não funcionar para aplicativos multi-threaded que precisam controlar o número de casas decimais por thread. Aqui está uma solução que deve funcionar em aplicativos multiencadeados:
Você pode simplesmente definir encoder.thread_local.decimal_places para o número de casas decimais que você deseja, e a próxima chamada para json.dumps () nesse segmento usará esse número de casas decimais
fonte
Se você precisar fazer isso no python 2.7 sem substituir o json.encoder.FLOAT_REPR global, aqui está uma maneira.
Então, no python 2.7:
No python 2.6, ele não funciona exatamente como Matthew Schinckel aponta abaixo:
fonte
Prós:
Contras:
Complexidade quadrática.
fonte
Ao importar o módulo json padrão, basta alterar o codificador padrão FLOAT_REPR. Não há realmente a necessidade de importar ou criar instâncias do Encoder.
Às vezes, também é muito útil gerar como json a melhor representação que o python pode adivinhar com str. Isso garantirá que os dígitos significativos não sejam ignorados.
fonte
Concordo com @Nelson que herdar de float é estranho, mas talvez uma solução que apenas toque a
__repr__
função possa ser perdoada. Acabei usando odecimal
pacote para reformatar os flutuadores quando necessário. A vantagem é que isso funciona em todos os contextos em querepr()
está sendo chamado, então também ao simplesmente imprimir listas no stdout, por exemplo. Além disso, a precisão é configurável em tempo de execução, após a criação dos dados. A desvantagem é, claro, que seus dados precisam ser convertidos para esta classe especial de float (como infelizmente você não consegue fazer um monkey patchfloat.__repr__
). Para isso, forneço uma breve função de conversão.O código:
Exemplo de uso:
fonte
Usando numpy
Se você realmente tiver flutuadores muito longos, poderá arredondá-los para cima / para baixo corretamente com numpy:
'[23.67, 23.97, 23.87]'
fonte
Acabei de lançar o fjson , uma pequena biblioteca Python para corrigir esse problema. Instale com
e usar apenas como
json
, com a adição dofloat_format
parâmetro:fonte