Unravelling the `pass` statement

This is the next post in my series on Python's syntactic sugar. It's unfortunately been a while since my last post due to Python 3.10 and PyCon US 2021 taking up a lot of my time. But with those no longer being a distraction, I can get back into a rhythm of doing posts, so I'm going to do probably the easiest bit of Python syntax that I can unravel: pass.

The definition for pass is extremely simple:

pass is a null operation — when it is executed, nothing happens.

That means pass does as much as "pass" or 42 does on their own: absolutely nothing but take up a line of syntax. You can look at the bytecode and see it does absolutely nothing (loading and returning None is automatically appended to all functions, so you can ignore that bit):

>>> import dis
>>> def spam(): pass
... 
>>> dis.dis(spam)
  1           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE
Disassembly of pass

The reason pass even exists is so that you can syntactically signal that something is purposefully empty versus accidentally leaving something out. For instance, in the following if statement:

if x:
    pass
else:
    "pass"
Example of if statement is do-nothing branches

In the first block you know for certain that nothing is supposed to be there. But in that second block using "pass", you can't be certain that the line was actually supposed to be y = "pass" or any other valid use of the "pass" string.

The use of pass is also uniquely Python due to our lack of curly braces. In most languages, if you wanted an empty block, you would just have { /* This space intentially left blank. */ } . But since we don't have curly braces ( from __future__ import curly_braces makes that abundantly clear 😉) we need some other way to make an empty block work, and that way is pass.

While it's easy to replace pass syntactically since it's purely a syntactic construct, it does provide a simple yet useful bit of semantics for developers.