десятичное сравнение Python - PullRequest
13 голосов
/ 30 июня 2009

десятичное сравнение питона

>>> from decimal import Decimal
>>> Decimal('1.0') > 2.0
True

Я ожидал, что он правильно конвертирует 2.0, но после прочтения PEP 327 я понимаю, что были некоторые причины для неявного преобразования числа с плавающей запятой в десятичную, но в этом случае не следует вызывать TypeError как это делает в этом случае

>>> Decimal('1.0') + 2.0
Traceback (most recent call last):
  File "<string>", line 1, in <string>
TypeError: unsupported operand type(s) for +: 'Decimal' and 'float'

как и все остальные операторы / -% // и т. Д.

так что мои вопросы

  1. это правильное поведение? (не вызывать исключения в cmp)
  2. Что, если я получу свой собственный класс и прямо поплавковый конвертер в основном Десятичные (repr (float_value)), являются есть какие-нибудь предостережения? мой вариант использования предполагает только сравнение цен

Сведения о системе: Python 2.5.2 в Ubuntu 8.04.1

Ответы [ 3 ]

24 голосов
/ 30 июня 2009

Re 1, это действительно то поведение, которое мы разработали - правильное или неправильное, каким оно может быть (извините, если это испортило ваш вариант использования, но мы пытались быть общими!).

В частности, долгое время случалось так, что каждый объект Python мог подвергаться сравнению неравенства с любыми другими - объекты типов, которые на самом деле не сравнимы, сравниваются произвольно (последовательно в данном прогоне, не обязательно между прогонами); Основным вариантом использования была сортировка разнородного списка для группировки в нем элементов по типу.

Исключение было введено только для комплексных чисел, что делало их несопоставимыми с чем-либо - но это было еще много лет назад, когда мы время от времени проявляли осторожность в нарушении совершенно хорошего пользовательского кода. В настоящее время мы гораздо строже относимся к обратной совместимости в основном выпуске (например, вдоль линии 2.* и отдельно по линии 3.*, хотя несовместимости допускаются между 2 и 3 - действительно, это вся суть в серии a 3.*, что позволяет нам исправлять прошлые проектные решения даже несовместимыми способами.

Произвольные сравнения оказались более сложными, чем они того стоят, вызывая путаницу среди пользователей; и группировка по типу теперь может быть легко получена, например, с аргументом key=lambda x: str(type(x)) для sort; поэтому в Python 3 сравнения между объектами разных типов, если только сами объекты не разрешают это в методах сравнения, вызывают исключение:

>>> decimal.Decimal('2.0') > 1.2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: Decimal() > float()

Другими словами, в Python 3 это ведет себя именно так, как вы думаете; но в Python 2 это не так (и никогда не будет в любом Python 2.*).

Re 2, с вами все будет в порядке - хотя, посмотрите на gmpy , что, я надеюсь, является интересным способом преобразования двойных чисел в дроби бесконечной точности через деревья Фари. Если цены, с которыми вы имеете дело, не превышают центов, используйте '%.2f' % x вместо repr(x)! -)

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

def to_decimal(float_price):
    return decimal.Decimal('%.2f' % float_price)

, поскольку после получения полученное десятичное число является совершенно обычным.

2 голосов
/ 30 июня 2009

Сравнение больше, чем работает, потому что по умолчанию оно работает для всех объектов.

>>> 'abc' > 123
True

Decimal прав только потому, что он правильно следует спецификации. Был ли спецификация правильной, это отдельный вопрос. :)

Только обычные предостережения при работе с числами с плавающей точкой, кратко суммированные: остерегайтесь краевых случаев, таких как отрицательный ноль, +/- бесконечность и NaN, не проверяйте равенство (связанное со следующей точкой) и считайте немного математически неточно.

>>> print (1.1 + 2.2 == 3.3)
False
1 голос
/ 30 июня 2009

Если это «правильно», это вопрос мнения, но обоснование того, почему не существует автоматического преобразования, существует в ПКП, и это было принято решение. Суть в том, что вы не всегда можете точно преобразовать число с плавающей точкой в ​​десятичную. Поэтому преобразование не должно быть неявным. Если вы в своем приложении знаете, что у вас никогда не будет достаточно значительных чисел, чтобы это могло повлиять на вас, создание классов, допускающих такое неявное поведение, не должно быть проблемой.

Кроме того, одним из основных аргументов является то, что реальных вариантов использования не существует. Вероятно, будет проще, если вы будете везде использовать десятичную дробь.

...