views:

267

answers:

2

This is somehow related to my question Why is ''>0 True in Python?

In Python 2.6.4:

>> Decimal('0') > 9999.0
True

From the answer to my original question I understand that when comparing objects of different types in Python 2.x the types are ordered by their name. But in this case:

>> type(Decimal('0')).__name__ > type(9999.0).__name__
False

Why is Decimal('0') > 9999.0 == True then?

UPDATE: I usually work on Ubuntu (Linux 2.6.31-20-generic #57-Ubuntu SMP Mon Feb 8 09:05:19 UTC 2010 i686 GNU/Linux, Python 2.6.4 (r264:75706, Dec 7 2009, 18:45:15) [GCC 4.4.1] on linux2). On Windows (WinXP Professional SP3, Python 2.6.4 (r264:75706, Nov 3 2009, 13:23:17) [MSC v.1500 32 bit (Intel)] on win32) my original statement works differently:

>> Decimal('0') > 9999.0
False

I even more puzzled now. %-(

+12  A: 

Because the decimal module does not compare against any type except long, int, and Decimal. In all other cases, decimal silently returns the "not something it knows about object" as greater. You can see this behavior in the _convert_other() function of decimal.py

Silly, silly Decimal class.

Oh, see http://bugs.python.org/issue2531 as well.

So, here is what happens:

  • The interpreter calls the Decimal.__gt__ comparison function.
  • Decimal.__gt__ calls Decimal._convert_other to convert the incoming float to a Decimal.
  • Decimal._convert_other doesn't understand floats. The implementation down in Decimal._convert_other explicitly checks for long, int, and Decimal types of the operand. Yes, this is a bug, in that unexpected library implementations cause bugs further down the line. It would be cleaner to do the right thing or even just through a TypeException. Instead it throughs the same NotImplemented that would happen comparing a Decimal to, say, a hash of Employee records.
  • A few other comparison operations are tried. Comparison gives up.
  • The default comparison, down in CPython's Objects/object.c/default_3way_compare get called.
  • In Python 3, this rightly barfs. In Python 2, it compares the id() functions.
  • On Windows, a case insensitive comparison is used (sort of). On modern systems, a case sensitive comparison is used.
  • So you get different results.

Are we there yet?

Charles Merriam
Tx for setting me right, so I deleted my (misleading) answer and +1'd yours in gratitude;-).
Alex Martelli
@charles: Why then it works differently on different platforms?
parxier
No clue. I don't have Windows. Try pulling up the _convert_other() on both and comparing them.
Charles Merriam
@Charles Merram: This isn't quite right. Decimal doesn't return the "not something it knows about object" as greater: it returns `NotImplemented`. Since the float side of the comparison also returns `NotImplemented`, Python falls back to the default rules it applies when neither operand knows how to compare with the other; this ends up effectively comparing `id(float)` and `id(Decimal)`, so the result just depends on the addresses in memory of the `float` and `Decimal` types (which explains platform differences). The gory details are in `default_3way_compare` in Objects/object.c.
Mark Dickinson
Hmm.. Sounds reasonable and I'll look at it.
Charles Merriam
@Mark: your comment **is** the answer to my question, how can I accept it?
parxier
@Mark: I extracted your comments as an accepted answer. Thank you!
parxier
+1  A: 
def __gt__(self, other, context=None):
    other = _convert_other(other)
    if other is NotImplemented:
        return other
    ans = self._compare_check_nans(other, context)
    if ans:
        return False
    return self._cmp(other) > 0


def _convert_other(other, raiseit=False):
    """Convert other to Decimal.

    Verifies that it's ok to use in an implicit construction.
    """
    if isinstance(other, Decimal):
        return other
    if isinstance(other, (int, long)):
        return Decimal(other)
    if raiseit:
        raise TypeError("Unable to convert %s to Decimal" % other)
    return NotImplemented
Casey