Efficiently Checking for an Empty List in Python

Frank Scholl
The Startup
Published in
4 min readNov 22, 2019

I stumbled on a story on Medium that detailed three different ways to check for an empty list in Python. While the information provided is technically correct, I felt the resolution of method choice left something to be desired. Notably, it doesn’t indicate the preferred method noted by the language developers or account for code formatting standards. With all of that considered, there is a best method of checking for an empty list in Python, and I can prove it.

Methods of Comparison

I’ll use the same three methods from the source since they are the most common methods considered: comparing to an empty list, checking the length, and using an implicit boolean conversion.

def explicit_list(a):
if a == []:
return True
return False
def explicit_len(a):
if len(a) == 0:
return True
return False
def implicit_bool(a):
if a:
return True
return False

How Python Interprets These Comparisons

As you may already know, Python first converts lexical text into bytecode before the interpreter executes it. Using Python’s dis module, we can look at the bytecode generated for each method and examine how they really work.

>>> import dis
>>> def explicit_list(a):
... if a == []:
... return True
... return False
...
>>> dis.dis(explicit_list)
2 0 LOAD_FAST 0 (a)
2 BUILD_LIST 0
4 COMPARE_OP 2 (==)
6 POP_JUMP_IF_FALSE 12
3 8 LOAD_CONST 1 (True)
10 RETURN_VALUE
4 >> 12 LOAD_CONST 2 (False)
14 RETURN_VALUE

This is confusing if you’ve never looked at bytecode before. The number on the far left is the line number corresponding to the lexical function code. Line 1 is the method definition, so it lacks any bytecode in this analysis. Note that for all of the methods above, lines 3 and 4 are identical, so we’ll focus only on line 2.

Explicit List

...     if a == []:
2 0 LOAD_FAST 0 (a)
2 BUILD_LIST 0
4 COMPARE_OP 2 (==)
6 POP_JUMP_IF_FALSE 12

Four operations occur:

  • LOAD_FAST finds our variable a in memory puts it on the top of the stack.
  • BUILD_LIST allocates a new list in memory with a length of 0 and adds it to the top of the stack.
  • COMPARE_OP takes the top 2 items off the top of the stack and checks to see if they are equal. The way lists are compared to see if they are equal in python is by iterating through both lists and verifying that each element is equal. The result is placed on the top of the stack.
  • POP_JUMP_IF_FALSE takes the first element off the top of the stack and jumps to the indicated byte number if the value is false.

Of the three methods, this is the most complex way to check for a list being empty. It creates new objects in memory that garbage collection needs to later remove, and it invokes a loop through the lists for element comparison.

Explicit Length

...     if len(a) == 0:
2 0 LOAD_GLOBAL 0 (len)
2 LOAD_FAST 0 (a)
4 CALL_FUNCTION 1
6 LOAD_CONST 1 (0)
8 COMPARE_OP 2 (==)
10 POP_JUMP_IF_FALSE 16

For this method, six operations occur:

  • LOAD_GLOBAL finds the definition of len and adds it to the top of the stack.
  • LOAD_FAST, again, finds our variable a and adds it to the top of the stack.
  • CALL_FUNCTION grabs 1 parameter of the stack and then calls the next element on the stack passing that variable (i.e. it will pop a and pass it to len). The result of the function is then placed on the top of the stack.
  • LOAD_CONST puts a constant 0 on the top of the stack.
  • COMPARE_OP works the same as before and compares the result of len to the constant 0.
  • POP_JUMP_IF_FALSE works exactly the same.

This method isn’t bad. It is simple, readable, and requires minimal memory operations, but the operation takes place entirely in Python.

Implicit Boolean

...     if a:
2 0 LOAD_FAST 0 (a)
2 POP_JUMP_IF_FALSE 8

Just two operations here:

  • LOAD_FAST puts a on the top of the stack.
  • POP_JUMP_IF_FALSE removes the top value and jumps ahead if it is false.

This method makes the others look bloated. It is very efficient and clearly shows the language was designed to function with implicit boolean conversion.

This seems too much like magic. What’s really happening here?

When the bytecode is interpreted at runtime the POP_JUMP_IF_FALSE examines the object in C and gets an appropriate boolean value based on the object. That means whatever is done behind the scenes has native support in C for determining if the list is empty. If you look into the source code of Python, you’ll find that the comparison is looking at the C object’s size property and returning True if it is greater than 0 or False if it isn’t. Thus, it is a streamlined implementation of the explicit length method implemented by the list object in C.

Don’t Just Take My Word For It

From the python documentation:

By default, an object is considered true unless its class defines either a __bool__() method that returns False or a __len__() method that returns zero, when called with the object.

From the PEP8 Style Guide:

For sequences, (strings, lists, tuples), use the fact that empty sequences are false.Yes: if not seq:
if seq:

No: if len(seq):
if not len(seq):

Final Thoughts

Empirically, the best way to check for an empty list in Python is by implicit boolean conversion. Does there exist a case or two where the length method is more readable? Sure, but such cases are very rare.

--

--

Frank Scholl
The Startup

Frank has been an active member in Information Security communities for over 10 years. He is also a lifelong Python enthusiast and developer.