3

I wanted to get a better understanding of OOP in Python and wrote a bit of code describing (infinite) ordinal arithmetic. I defined a class named Omega() with the usual comparison operators (==, <=, etc.), addition and multiplication.

I thought I'd check whether (as should be true) the first infinite ordinal added to itself was less-than-or-equal-to the first uncountable ordinal. Launching the interactive shell, here's what I found:

    >>> a, b = Omega(), Omega(1)
    >>> (a+a) <= b
    False
    >>> (a+a) <= b
    True
    >>> (a+a) <= b
    False

The same expression produces different truth values.

I continued to test the expression and could not spot any pattern. If I re-interpret the code, I find that repeatedly testing the expression produces a different sequence of True/False values.

What could be causing this behaviour?

If it's relevant, I'm using CPython 2.7.5 on Windows 8.1.

Here's the Python code I ran: http://pastebin.com/XPqMphBw

11
  • @SethMMorton No, I'm not importing any modules.
    – Alex Riley
    Commented Aug 8, 2014 at 18:41
  • Sounds like you need to throw in some print statements in your methods to see what values the variables actually contain each time you do the comparison. Commented Aug 8, 2014 at 18:41
  • @Padraic Cunningham - Fair enough, it could be an issue with my system rather than what I've written.
    – Alex Riley
    Commented Aug 8, 2014 at 18:46
  • In your code, index and copies sometimes appear to be integers, other times instances of Omega. It's not clear how your implementation works.
    – chepner
    Commented Aug 8, 2014 at 18:46
  • 2
    Can replicate. No idea what's going on but I don't want other people to ignore it based on that comment. Printing (a+a) <= b, (a+a).__dict__, b.__dict__ shows no difference in the dicts between True and False results. Agree that the types of copies,index,power are confusing. All the comparison code expects them to be Omegas, but all the assignments are to ints. Commented Aug 8, 2014 at 18:49

2 Answers 2

6

I believe you overloaded the <= and >= operators incorrectly. Instead of:

def __leq__(self, other):
# ...
def __geq__(self, other):

use this instead:

def __le__(self, other):
# ...
def __ge__(self, other):

After making these changes and running this in Python 3.4.1, I get:

>>> a, b = Omega(), Omega(1)
>>> (a+a) <= b
True
>>> (a+a) <= b
True
>>> (a+a) <= b
True
3
  • Ah! I've just tried this modification and it seems to work. Thanks! Any idea of the reason behind the unpredictable truth value before though?
    – Alex Riley
    Commented Aug 8, 2014 at 19:05
  • No idea unfortunately. I couldn't reproduce your error; instead, I would get: TypeError: unorderable types: Omega() <= Omega().
    – Adriano
    Commented Aug 8, 2014 at 19:09
  • 3
    A speculation. Since __geq__ is not a special name, you actually did not define a comparison operator. Python 2.x returns random garbage based on object identity in this case; you generated new transient objects by calculating a+a, and some of them "compared" to b differently. Python 3.x in absence of ordering operator clearly says 'type <whatever> is unorderable'.
    – 9000
    Commented Aug 8, 2014 at 19:22
1

Like @Padraic Cunningham, I also cannot replicate your problem (under Python 2.7.5 on Mac OS X). It gives me consistent answers.

You would do well to give your objects a comprehensible __repr__ method so that they are easily printed for debugging purposes. For example:

def __repr__(self):
    innards = ", ".join(str(v) for v in [self.index, self.power, self.copies])
    return "{0}({1})".format(self.__class__.__name__, innards)

Printing a would then show Omega(0, 1, 1). A slightly fancier version might be:

def __repr__(self):
    innards = "index={index}, power={power}, copies={copies}".format(**self.__dict__)
    return "{0}({1})".format(self.__class__.__name__, innards)

I also note that your code is probably not computing "less than or equal" the way you think it is. You have methods __leq__ and __geq__ defined, but those are not part of the Python data model. You want (and need) __le__ and __ge__ instead. If those are not defined, a combination of __eq__ and __lt__ are called instead. That combination generally has logical equivalency, if you're using a standard algebraic definition of <=, but in this case... It's at least a place to check.

1
  • Thank you, I'm using __le__ and __ge__ now and it appears to be giving consistent values. I did have a __repr__ method (I must have cut it out by mistake when I pasted to PasteBin) to print out LaTeX representations of Omega(). I think I had <= muddled with the LaTeX \leq...
    – Alex Riley
    Commented Aug 8, 2014 at 19:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.