Party Tricks: Empty List Expansion

Let’s say we’ve got a bunch of functions accessed through a dictionary, like this:

def f0():
    return 0
def f1(a0):
    return 0
def f2(a0, a1):
    return 0

funcs = {
    'f0': f0,
    'f1': f1,
    'f2': f2
}

We call these functions like this:

funcs['f0']()

Now assume we want to call these functions with user-provided arguments. Say, for example, that the user wants to call f1 with argument a. They pass the string 'f1 a'. We might do something like

arg_str = 'f1 a'
args = args_str.split(' ')
funcs[args[0]](args[1])

Another user might want to call f2 with arguments a, b. They pass the string 'f2 a b', and we do

args_str = 'f2 a b'
args = args_str.split(' ')
funcs[args[0]](args[1], args[2])

and so on.

If you know much Python, you’ll recognize this as a perfect use case for list expansion. Instead of explicitly passing each argument to the function, we can do

args_str = 'f2 a b'
args = args_str.split(' ')
funcs[args[0]](*args[1:])

The * operator is the key. I won’t go too deep into the semantics, but there are lots of cool things you can do with it.

In our use case, the operator replaces the list by pulling each element from it out into the list’s enclosing scope, in the order in which it appears in the list. Syntactically, it does something like f(*[1, 2, 3]) -> f(1, 2, 3).

What I want to draw attention to today is how the * operator reacts to empty lists (or more generally, empty iterables). Let’s go back to our example. Let’s say we start with two variables, f and args. The former is a string containing the name of the function we want to call, and the latter is a possibly empty list containing the arguments for the function.

We’d like to do

funcs[f](*args)

and have it work for all of our cases.

Clearly this will work in the case of f1 and f2, but what about in the case of f0? Well, I’m here to tell you that Python does the right thing and expands the empty list into… nothing. Try running this:

def no_args():
    return 0
x = []
print(no_args(*x))

It should print 0 with no complaints.

So that’s it, TLDR is that python does the right thing here.