Two Python curiosities

I learn something new every day.

First, the sort of thing you could do to mess with someone when they step away from their Python interactive shell to go use the restroom:

>>> False = True
>>> False
>>> if False: print "Woo!"

Sure enough, True and False are just names in the builtin namespace that can be rebound. This capability seems pretty dangerous, so I'd expect to see something like it in C or in Lisp, but its presence in Python surprised me a bit.

Second, the following puzzle stumped me for a while when I first saw it. Why does the following expression evaluate as it does?

>>> "string" in [] == False

It's not a precedence issue, because that result is not consistent with either of the parenthesizations:

>>> ("string" in []) == False
>>> "string" in ([] == False)
Traceback (most recent call last):
  File "", line 1, in 
TypeError: argument of type 'bool' is not iterable

It's actually a consequence of Python's support for chained comparison operators. That is, just as

5 < a <= 7

desugars to

5 < a and a <= 7,

the first expression above desugars to "string" in [] and [] == False, which is False.

It's good that the comparison operators (< <= > >= == != in is) work in this consistent way, but it can trip up Python novices who have not yet learned that they really have to write the more Pythonic expression "string" not in [] if they want to stay out of trouble.


  1. Once upon a time, Python did not have a boolean type. Some programmers thought this was kind of lame, and simulated it by setting a few variables:

    True = 1
    False = 0

    Eventually, Python did get a boolean type, but in the name of backwards compatibility, True and False were left as normal variables, so that old code that assigned them would still work.

    As of Python 3, this has been fixed, True and False are keywords, as they should have been from the beginning.

  2. > they really have to write the more Pythonic expression "string" not in []

    I don't get it, why ?

  3. The alternative -- "string" in [] == False -- looks equivalent but doesn't do what most people think it does (as described above).