Python: Booleans and None
You Can't Handle the Truth!
I would still love to hear your feedback in the comments below. Enjoy!
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 xandif x is not Noneare not the same!
corollary:if not xandif x is Noneare 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 NoneFortunately, the folks working on Python had the sense to change this to
__bool__in Python 3.x! ↩︎
Follow me on Twitter and Facebook