Believe it or not—how you answer this question in your day-to-day code reveals your true Python skill level to every master coder who reads your code.
Beginner coders check if a list a
is empty using crude statements like len(a)==0
or a==[]
. While those solve the problem—they check if a list is empty—they are not what a master coder would do. Instead, the most Pythonic way to check if a list (or any other iterable for that matter) is empty is the expression not a
.
You may call it implicit Booleanness (or, more formal, type flexibility): every object in Python can be implicitly converted into a truth value.
Here’s an example in our interactive Python shell—try it yourself!
Exercise: What’s the output of the code if you add one element to the list a
?
Truth Value Testing and Type Flexibility
Python implicitly associates any object with a Boolean value. Here are some examples:
- The integers 1, 2, and 3 are associated to the Boolean
True
. - The integer 0 is associated to the Boolean
False
. - The strings
'hello'
,'42'
, and'0'
are associated to the BooleanTrue
. - The empty string
''
is associated to the BooleanFalse
.
Roughly speaking, each time a Boolean value is expected, you can throw in a Python object instead. The Python object will then be converted to a Boolean value. This Boolean value will be used to decide whether to enter, say, a while
loop or an if
statement. This is called “type flexibility” and it’s one of Python’s core design choices.
Per default, all objects are considered True
if they are semantically non-empty. Empty objects are usually associated to the Boolean False
. More specifically, only if one of the two cases is met, will the result of an object be False
: (i) the __len__()
function returns 0, or (ii) the __bool__()
function returns False
. You can redefine those two methods for each object.
From the Python documentation, here are some common objects that are associated to the Boolean False
:
- Defined constants:
None
andFalse
. - Zero of numerical types:
0
,0.0
,0j
,Decimal(0)
,Fraction(0, 1)
- Empty iterables:
''
,()
,[]
,{}
,set()
,range(0)
Here are some examples:
if []: print('1') if (): print('2') if [()]: print('3') # 3 if 0: print('4') if 0.00: print('5') if 0.001: print('6') # 6 if set(): print('7') if [set()]: print('8') # 8
Again, even if the iterable contains only a single element (that may evaluate to False
like integer 0
), the implicit Boolean conversion will return True
because an empty element is an element nonetheless.
PEP8 Recommendation: How to Check if a List is Empty
As some readers argued with me about how to correctly check for an empty list in Python, here‘s the explicit excerpt from the PEP8 standard (Python’s set of rules about how to write readable code):
For sequences, (strings, lists, tuples), use the fact that empty sequences are false:
# Correct: if not seq: if seq:
# Wrong: if len(seq): if not len(seq):
Performance Evaluations
To see which of the three methods is fastest, I repeated each method 100 times using the timeit
library on my notebook with Intel Core i7 (TM) CPU of 8th Generation, 8GB RAM—yes, I know—and NVIDIA Graphic Card (not that it mattered).
Here’s the code:
import timeit import numpy as np setup = 'a = []' method1 = 'if len(a) == 0: pass' method2 = 'if a == []: pass' method3 = 'if not a: pass' t1 = timeit.repeat(stmt=method1, setup=setup, repeat=100) t2 = timeit.repeat(stmt=method2, setup=setup, repeat=100) t3 = timeit.repeat(stmt=method3, setup=setup, repeat=100) print('Method 1: len(a) == 0') print('avg: ' + str(np.average(t1))) print('var: ' + str(np.var(t1))) print() print('Method 2: a == []') print('avg: ' + str(np.average(t2))) print('var: ' + str(np.var(t2))) print() print('Method 3: not a') print('avg: ' + str(np.average(t3))) print('var: ' + str(np.var(t3))) print()
The third method is the most Pythonic one with type flexibility. We measure the elapsed time of 100 executions of each method. In particular, we’re interested in the average time and the variance of the elapsed time. Both should be minimal.
Our thesis is that the third, most Pythonic method is also the fastest because there’s no need to create a new empty list (like in method 2) or performing nested function calls like in method 1. Method 3 consists only of a single function call: converting the list into a Boolean value with the __bool__
or __len__
methods.
Here’s the result in terms of elapsed average runtime and variance of the runtimes:
Method 1: len(a) == 0 avg: 0.06273576400000003 var: 0.00022597495215430347 Method 2: a == [] avg: 0.034635367999999944 var: 8.290137682917488e-05 Method 3: not a avg: 0.017685209000000004 var: 6.900910317342067e-05
You can see that the third method is not only 50% faster than method 2 and 75% faster than method 3, it also has very little variance. It’s clearly the best method in terms of runtime performance. Being also the shortest method, you can now see why the method is considered to be most “Pythonic”.
Where to Go From Here?
Enough theory. Let’s get some practice!
Coders get paid six figures and more because they can solve problems more effectively using machine intelligence and automation.
To become more successful in coding, solve more real problems for real people. That’s how you polish the skills you really need in practice. After all, what’s the use of learning theory that nobody ever needs?
You build high-value coding skills by working on practical coding projects!
Do you want to stop learning with toy projects and focus on practical code projects that earn you money and solve real problems for people?
🚀 If your answer is YES!, consider becoming a Python freelance developer! It’s the best way of approaching the task of improving your Python skills—even if you are a complete beginner.
If you just want to learn about the freelancing opportunity, feel free to watch my free webinar “How to Build Your High-Income Skill Python” and learn how I grew my coding business online and how you can, too—from the comfort of your own home.