Как я могу локализовать формат python Decimal и сохранить его точность?

Как я могу форматировать Decimal с помощью языкового стандарта?

Чтобы описать примеры, я пытаюсь определить функцию f, такую, что в англоязычной локали:

  • f(Decimal('5000.00')) == '5,000.00'

  • f(Decimal('1234567.000000')) == '1,234,567.000000'

Некоторые вещи, которые не работают:

  • f = str не использует локаль; f(Decimal('5000.00')) == '5000.00'

  • f = lambda d: locale.format('%f', d) не сохраняет десятичную точность; f(Decimal('5000.00')) == '5000.000000'

  • f = lambda d: locale.format('%.2f', d) использует фиксированную точность, и это не то, что я хочу; f(Decimal('1234567.000000')) == '1234567.00'

Ответ 1

Чтение через источник модуля decimal, Decimal.__format__ обеспечивает полную поддержку PEP 3101, и все, что вам нужно сделать, это выбрать правильный тип представления. В этом случае вам нужен тип :n. Согласно спецификации PEP 3101, этот :n обладает следующими свойствами:

'n' - Число. Это то же самое, что и "g", за исключением того, что он использует               текущую настройку локали, чтобы вставить соответствующие               номера разделителей.

Это проще, чем другие ответы, и позволяет избежать проблемы с поплавком в моем исходном ответе (сохраняется ниже):

>>> import locale
>>> from decimal import Decimal
>>> 
>>> def f(d):
...     return '{0:n}'.format(d)
... 
>>> 
>>> locale.setlocale(locale.LC_ALL, 'en_us')
'en_us'
>>> print f(Decimal('5000.00'))
5,000.00
>>> print f(Decimal('1234567.000000'))
1,234,567.000000
>>> print f(Decimal('123456700000000.123'))
123,456,700,000,000.123
>>> locale.setlocale(locale.LC_ALL, 'no_no')
'no_no'
>>> print f(Decimal('5000.00'))
5.000,00
>>> print f(Decimal('1234567.000000'))
1.234.567,000000
>>> print f(Decimal('123456700000000.123'))
123.456.700.000.000,123

Оригинал, неправильный ответ

Вы можете просто указать строке формата, чтобы использовать такую ​​же точность, как и в десятичном знаке, и использовать языковой форматтер:

def locale_format(d):
    return locale.format('%%0.%df' % (-d.as_tuple().exponent), d, grouping=True)

Обратите внимание, что это работает, если у вас есть десятичная цифра, соответствующая действительному числу, но не работает корректно, если десятичное число NaN или +Inf или что-то в этом роде. Если это возможности на вашем входе, вам нужно будет учитывать их в методе формата.

>>> locale.setlocale(locale.LC_ALL, 'en_US')
'en_US'
>>> locale_format(Decimal('1234567.000000'))
'1,234,567.000000'
>>> locale_format(Decimal('5000.00'))
'5,000.00'
>>> locale.setlocale(locale.LC_ALL, 'no_no')
'no_no'
>>> locale_format(Decimal('1234567.000000'))
'1.234.567,000000'
>>> locale_format(Decimal('5000.00'))
'5.000,00'
>>> locale_format(Decimal('NaN'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in locale_format
TypeError: bad operand type for unary -: 'str'