En kodare

Anders Hovmöller
GitHub
twitter
email

Keyword argument confusion in Python

2017-05-09

(If you read this and feel more confused, that means you now know more, it IS confusing!)

There’s a lot of confusion among Python programmers on what exactly “keyword arguments” are. Let’s go through some confusions. This is a statement I’ve come across:

If you somehow are writing for a Python 3 only codebase, I highly recommend making all your keyword arguments keyword only, especially keyword arguments that represent “options”.

There are many problems with this sentence. The first is that this is mixing up “arguments” (i.e. things at the call site) and “parameters” (i.e. things you declare when defining a function). So:

def foo(a, b): # <- a and b are "parameters" or "formal arguments"  
    pass

foo(1, 2) # <- 1 and 2 are arguments to foo, that match a and b

This confusion is common among programmers. I also use the word “argument” when I mean “parameter”, because normally in conversation we can tell the difference in context. Even the documentation in the Python standard library uses these as synonyms.

The code above is the basic case with normal parameters and positional arguments. But we were talking about keyword arguments so let’s talk about those too:

def bar(
        a,    # <- this parameter is a normal python parameter
        b=1,  # <- this is a parameter with a default value
        *,    # <- all parameters after this are keyword only
        c=2,  # <- keyword only argument with default value
        d):   # <- keyword only argument without default value
    pass

So far so good. Now, let’s think about the statement we started with:

I highly recommend making all your keyword arguments keyword onlyThat implies there are keyword arguments that are not keyword only arguments. That’s sort of correct, but also very wrong. Let’s have some examples of usages of bar :

bar(1)         # one positional argument
bar(1, 2)      # two positional arguments
bar(a=1)       # one keyword argument
bar(a=1, b=2)  # two keyword arguments
bar(1, d=2)    # one positional and one keyword argument

The trick here is to realize that a “keyword argument” is a concept of the call site, not the declaration. But a “keyword only argument” is a concept of the declaration, not the call site. Super confusing!

There are also parameters that are positional only. The function sum in the standard library is like this: according to the documentation it looks like this: sum(iterable[, start]) But there’s a catch!

>>> sum(iterable=[1, 2])  
Traceback (most recent call last):  
File "<stdin>", line 1, in <module>  
TypeError: sum() takes no keyword arguments

And the start parameter can’t be used as a keyword argument either, even though it’s optional!

Recap

(I’m using “argument” here even though “parameter” or “formal argument” would be more correct, but the Python standard library uses these all as synonyms so I will too, so my wording matches the documentation.)

Python functions can have:

When calling Python functions you can have:

It’s very simple at the call site, but a lot more complex at the function definition, and how call site arguments are mapped to the declaration is quite complex.

Summary

Python appears simple because most of these rules and distinctions are so well thought out that many programmers can go years in a professional career and believe defaults arguments and keyword arguments are the same, and never get bitten by this incorrect belief.

« Sam Harris [was] wrong about encryption Django comefrom considered harmful »