2009-06-30 3 views
13

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

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

Я ожидал, что это правильно преобразовать 2.0, но после прочтения через PEP 327 я понимаю, там были некоторые причины не неявно преобразований поплавка Decimal, но не следует, что случае он должен поднять 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' 

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

так моя questi

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

детали системы: Python 2.5.2 на Ubuntu 8.04.1

ответ

24

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

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

Исключение было введено только для сложных чисел, что делало их несопоставимыми ни с чем, но это было еще много лет назад, когда мы были иногда кавалерами, нарушая превосходный код пользователя. В настоящее время мы гораздо более строгие в отношении обратной совместимости в рамках основного выпуска (например,вдоль 2.* линии, так и отдельно по 3.* один, хотя Несовместимость являются разрешены между 2 и 3 - на самом деле это целая точка имея серию 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, поскольку я надеюсь, что это интересный способ конвертировать удвоения в бесконечные прецизионные дроби через Farey. Если цены, которые вы имеете дело с четками не более чем центы, используйте '%.2f' % x, а не repr(x)! -)

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

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

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

+1

Этот ответ немного устарел: 'decimal.Decimal' и' float' численно сопоставимы в 2.7. «Изменено в версии 2.7: сравнение между экземпляром float x и экземпляром Decimal y теперь возвращает результат, основанный на значениях x и y. В более ранних версиях x

1

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

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

+0

Я редактировал первый вопрос немного, по праву имею в виду, что не собираю исключение, если он не может преобразовать в сравнении –

+0

Ага, я вижу. Да, это имеет другое обоснование, и я согласен с тем, что создание исключения представляется более явным. Но опять-таки это вопрос вкуса. –

2

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

>>> 'abc' > 123 
True 

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

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

>>> print (1.1 + 2.2 == 3.3) 
False 
+2

'abc'> 123 вызывает исключение TypeError в Python 3. Какая правильная вещь, IMHO. –

Смежные вопросы