Python for Programmers: Kwargs
Welcome to the Kwargs lesson!
This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!
By default, function arguments are positional. The first argument is associated to the first function argument, the second argument to the second function argument, and so on.
>
def cat_age(name, age):return f"{name} is {age}"- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
cat_age("Keanu", 2)Result:
'Keanu is 2'
It's easy to mix up the argument order when calling functions.
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
cat_age(2, "Keanu")Result:
'2 is Keanu'
Python gives us a way to make our function calls more explicit: we can specify the arguments' names in the function call itself. When we label the arguments in this way, we can pass them in any order.
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
cat_age(name="Keanu", age=2)Result:
'Keanu is 2'
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
cat_age(age=2, name="Keanu")Result:
'Keanu is 2'
These are called keyword arguments, or "kwargs" for short, by contrast to the more common "positional arguments".
We can mix positional arguments and keyword arguments in the same function call.
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
cat_age("Keanu", age=2)Result:
'Keanu is 2'
There's an important restriction on kwarg syntax: kwargs must come after positional arguments. If we try to put kwargs before the positional arguments, that's a
SyntaxError.- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
cat_age(name="Keanu", 2)Result:
SyntaxError: positional argument follows keyword argument (<string>, line 4)
Kwargs must match the argument names in the function's definition. If we change the name of the function argument, any function calls referencing the old name will cause a
TypeError.- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
cat_age(name="Keanu", age_in_years=2)Result:
TypeError: cat_age() got an unexpected keyword argument 'age_in_years'
Good argument names are especially important when the arguments are passed as kwargs. There are two reasons for that.
First, we see the name every time we pass it as a kwarg, so that name appears in much more code. If it's a confusing name, that confusion can happen every time we pass it as a kwarg.
Second, arguments that are passed as kwargs are more difficult to rename. If we rename the
ageargument, existing positional calls likecat_age("Keanu", 2)will continue to work. But kwargs calls likecat_age("Keanu", age=3)will now fail because the argument is no longer namedage. If we ever change the argument name in the function's definition, we'll have to update all of the calls that use kwargs. Using a good name up front makes future renames less likely.Sometimes we want to force callers to pass arguments as keywords. In these cases, we can declare some arguments as keyword-only.
In the function argument list, a
*means that all arguments following the*must be passed as keyword arguments. In the next example, theageargument is keyword-only. Trying to passageas a positional argument is an error.>
def cat_age(name, *, age):return f"{name} is {age}"cat_age("Keanu", 2)Result:
TypeError: cat_age() takes 1 positional argument but 2 were given
Note that the
*in the function's argument list looks like it might be another argument, but it's not. The functioncat_agestill has two arguments.>
def cat_age(name, *, age):return f"{name} is {age}"cat_age("Keanu", age=2)Result:
'Keanu is 2'
The built-in
.sortmethod on lists is a good example of kwargs in practice. It sorts list elements in increasing order.>
my_list = [4, 2, 3]my_list.sort()my_listResult:
[2, 3, 4]
We can sort in reverse order via the
reversekwarg.>
my_list = [4, 2, 3]my_list.sort(reverse=True)my_listResult:
[4, 3, 2]
But we can't pass the
reverseargument positionally! Trying to do that is an error.>
my_list = [4, 2, 3]my_list.sort(True)my_listResult:
TypeError: sort() takes no positional arguments
Using keyword-only arguments is a tradeoff. It forces callers to write more explicit code, which usually makes that code easier to read. But specifying the argument names in each call is also more verbose. Keyword-only arguments make sense in some cases, but using them in every function definition would be a bad idea.
In the case of
.sort'sreverseargument, the keyword-only argument definitely helps to keep.sortcalls readable. Imagine that you've never heard of thereverseargument, and you come acrosssome_list.sort(True). What does thatTruemean? We'd have to look at the documentation to find out. Butsome_list.sort(reverse=True)is quite clear, even if you've never seen thereverseargument before.Here's a code problem:
The code below calls
log_vet_visitwith a pet name and an owner name. However, the arguments are in the wrong order. Change the calls to pass the arguments as keyword arguments, so Python knows which argument is which.(Don't change the argument order! That's not necessary.)
vet_visits = []def log_vet_visit(owner_name, pet_name):vet_visits.append({"owner": owner_name,"pet": pet_name,})log_vet_visit(pet_name="Ms. Fluff", owner_name="Amir")log_vet_visit(pet_name="Keanu", owner_name="Betty")assert vet_visits == [{"owner": "Amir", "pet": "Ms. Fluff"},{"owner": "Betty", "pet": "Keanu"},]- Goal:
No errors.
- Yours:
No errors.