Python: Wat

Sat 08 March 2014 by Peter Ward

You’ve all seen wat, right? Of course you have.

Of course, Python doesn’t have any Wats, right? Well, not so much.

Let’s talk about Python!

>>> 'toys"r"us'

Makes sense. So if we change those outer quotes to double quotes, we should get a syntax error, right?

>>> "toys"r"us"


Let’s talk about Python!

>>> 42**True
>>> 6 * False

This is because before True and False were available as builtins, people would write True = 1 and False = 0 at the top of their Python files. When they were introduced, they made bool a subclass of int, True == 1 and False == 0.

So of course, you can do arithmetic with booleans.


Let’s talk about Python!

>>> data = {
...     1: 'one',
...     '1': 'won',
...     True: 'true',
... }

Right, it’s a dictionary with three items: the keys have different types, but the basic idea is that we map objects to some string representation of them. Right?

>>> data
{1: 'true', '1': 'won'}

There are three behaviours of dictionaries which come into play here. The first is that keys in dictionaries compare equal if they have the same hash value (__hash__), and compare equal (__eq__). The second is that dictionary literals are evaluated by starting with an empty dictionary, and inserting each item in order (left to right). The third is that if you are inserting an item into a dictionary and the key is already there, the value will be replaced, but not the key.

Which means that in the example above, this is what happens:

>>> data = {}
>>> data[1] = 'one'
# {1: 'one'}
>>> data['1'] = 'won'
# {1: 'one', '1': 'won'}
>>> data[True] = 'true'
# {1: 'true', '1': 'won'}

Wait, why did the last insertion replace the value for 1? Well because hash(True) == hash(1), and True == 1, of course.


Enough making fun of languages that suck, let’s talk about JSON!

>>> import json
>>> data = {'1': 'won', 1: 'true'}
>>> json.dumps(data)
'{"1": "won", "1": "true"}'

This is valid JSON.


Let’s talk about Python.

>>> 1 > 2 is False

Uh, wait, but 1 > 2 isn’t true, so it should be False, right? Like this:

>>> (1 > 2) is False

Hmmm, that works, maybe it’s just a precedence thing:

>>> 1 > (2 is False)

Nope (and note the re-occurence of False == 0 in this). This is actually a case of Python’s chained comparisons:

>>> 1 > 2 and 2 is False

Chained comparisons are actually awesome…

a t-rex playing

…but if you ever use it with disparate operators like this, Wat.

And let’s finish up with some Python!

This was fixed in Python 3, but there’s still plenty of Python 2 programmers just waiting to bump into this one.

>>> class GiantSpiders:
...    count = 0
...    legs = [
...        'leg %d' % count
...        for count in range(8)
...    ]
>>> GiantSpiders.count

Arrrghh! Help, spiders!


Also, the one which didn’t qualify

Time objects representing midnight are falsey. Except, it's not quite as simple as that. Unfortunately, this only qualifies for an honourable mention: there’s an open bug for this issue, so it might be fixed.