You Can't Handle the Truth!

I got a chance to review some other people’s Python code recently, and there’s one comment I almost always have to give, which is:

if x and if x is not None are not the same!
corollary: if not x and if x is None are also quite different, obviously.

This usually happens when someone assigns None to a variable (say, x) as a sentinel value, and then x may or may not be assigned to. The test is designed to check whether or not x was assigned to, or not.

When you do if x is None, you call the operator is, which checks the identity of x. None is a singleton in Python and all None values are also the exact same instance. When you say if x, something different happens. if expects a boolean, and assuming x is not a boolean, Python automatically calls x’s __nonzero__ method. i.e., if x is actually executed as if x.__nonzero__ (or bool(x)). __nonzero__ is pretty poorly named1, but it’s a method that evaluated a class as a boolean value. It’s one of Python’s Magic Methods. The confusing thing is, that bool(None) returns False, so if x is None, if x works as you expect it to. However, there are other values that are evaluated as False. The most prominent example is an empty list. bool([]) returns False as well. Usually, an empty list has a meaning that is different to None; None means no value while an empty list means zero values. Semantically, they are different. I guess people are just unaware of the semantic difference between the two ways to write the condition.

Here are some useful snippets to demonstrate:

Testing None

>>> x = None 
... if x: 
...     print 'if x' 
... if x is not None: 
...     print 'if x is not None'

Testing an Empty List

>>> x = [] 
... if x: 
...     print 'if x' 
... if x is not None: 
...     print 'if x is not None' 
if x is not None 

Testing a Normal Value

>>> x = 42 
... if x: 
...     print 'if x' 
... if x is not None: 
...     print 'if x is not None'
if x if x is not None 

Testing a Custom Class

>>> class Foo(object): 
...     def __nonzero__(self): 
...         print 'Foo is evaluated to a boolean!'
...         return True 
... 
... x = Foo() 
... if x: 
...     print 'if x' 
... if x is not None: 
...     print 'if x is not None' 
Foo is evaluated to a boolean! 
if x 
if x is not None
  1. Fortunately, the folks working on Python had the sense to change this to __bool__ in Python 3.x! ↩︎

Discuss this post at the comment section below.
Follow me on Twitter , Facebook or Google+

Similar Posts