technology

Having None of it

Pop quiz: What’s the result of

123 > None

in Python?

One would hope it would be something sensible (Like ValueError!), but unfortunately one would be disappointed. In fact, the answer is True. In fact, if you play around, you’ll discover that the following is in fact true:

None and -1 > None and float('nan') > None

Now, I don’t know about you, but to me this behaviour was a bit of a surprise. Doubly so if you’re the sort of person that had code along the lines of

# Note: amount we owe person will return None if it's a blue moon
if amount_we_owe_person() > some_number_or_other:
    cost_us_all_money()

Which, regrettably, we did. Luckily, very few people were affected, but this highlighted to me an area of Python’s type system I had never before taken the time to examine thoroughly.

So let’s dig in.

Luckily, Python is open source, so we can relatively easily figure out what exactly is happening when we call built in functions on this type or the other type. After a little bit of grepping, we can see that first python attempts to call the method tp_compare, if at least one of the two PyObjects in question implements it. This is a pretty important method, as it is what allows for the implicit conversions between floats, ints and longs; all of which are different types but really ought to be treated as ‘numbers’ when all a programmer wants to do is add the darn things together or check which is greater.

However, if you aren’t using types that define a comparison method, python will fall back to this little function:

To be fully fair to Python and to Guido van R, it is far better to have a defined ordering of objects than nothing at all. To be unfair, why on earth would you let me check if my frozenset is ‘greater than’ a dictionary containing the single value ‘cat: fleas’. (If you’re curious, a frozen set is in fact greater than a dict).

clown-on-computer

Guido van R, Circa 1991, Designing Python’s Type System

As we can see from the snippet, the ordering of Python types when compared is as follows:

  • None is smaller than everything
  • Numbers are smaller than everything that isn’t None
  • All other types are compared using the string comparison on the name of the two types under test

So let’s grab a quick list of all Python’s types:

And generate the Truest atrocity we can:

import time


class AClass(object):
    pass


def AFunction():
    pass


print (
    (None < 1.0 < {} < AFunction < time < set() < AClass) and
    ("dict" < "function" < "module" < "set" < "type")
)

Try it yourself. I think you’ll find that it’s

True

Discussion

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s