Skip to content
Theory · Functions as values · lesson 1 of 10

*args and **kwargs

What you'll learn

What you'll learn:

  • write functions that take any number of arguments with *args and **kwargs;
  • pass a function as an argument and write a short function with lambda;
  • explain how a closure keeps the outer function's variables and changes them with nonlocal;
  • write a decorator that adds behavior to any function.
A funnel *prices gathers the coins, however many fall, into one pack tuple

Picture a funnel over a box. However many coins fall into it—one, three or none—they all end up in one pack. A parameter with a star works like that funnel: it gathers all the extra arguments into one tuple.

Why would you need it? The print function takes any number of arguments: one value, three, or none.

An ordinary function fixes the number of its positional parameters in the def line. An extra argument stops the program with a TypeError. A star before a parameter lifts this limit:

Python
def total(*prices):
    return sum(prices)


print(total(250, 100, 50))  # 400
print(total(7))  # 7
print(total())  # 0

Let's walk through the example line by line:

  1. def total(*prices) declares one parameter with a star—our funnel.
  2. total(250, 100, 50) puts three numbers into the tuple (250, 100, 50), and sum returns 400.
  3. total(7) gathers a tuple of one number, (7,), and the sum is 7.
  4. total() gets an empty tuple (), and the sum of an empty tuple is 0.

A tuple and a dict

A parameter can have one star or two. Their number decides which arguments go into the parameter and what Python collects them into.

One star: a tuple

A parameter with one star collects all extra positional arguments into a tuple. You work with it like any tuple: loop over it with for, pass it to sum or len. If there are no extra arguments, the tuple is empty, not None.

Two stars: a dict

A parameter with two stars collects extra keyword arguments into a dict. The argument's name becomes the key, and its value the value. With no extra keyword arguments, the dict is empty too.

You can put both funnels into one function:

Python
def show(first, *args, **kwargs):
    print(first, args, kwargs)


show(1, 2, 3, color="red")  # 1 (2, 3) {'color': 'red'}
show(1)  # 1 () {}

The parameters in the def line come in this order:

  1. ordinary parameters;
  2. *args;
  3. **kwargs—always last.

Ordinary parameters get their arguments first, and *args receives only what is left. In the first call, 1 goes to first, and args is (2, 3). In the second call, nothing is left over, so args and kwargs are empty.

The names args and kwargs are a convention, not a rule. Only * and ** carry the meaning, so def total(*prices) works the same way.

This is where the funnel picture stops working: the coins in the pack do not get mixed up. A tuple keeps the arguments in exactly the order they were passed.

A star in a call

In a call, the star works the other way around: it does not gather, it spreads. This is the same unpacking as in the lesson on tuples:

  • * spreads a list or a tuple into separate positional arguments: print(*[3, 1, 2]) is the same as print(3, 1, 2) and prints 3 1 2;
  • ** spreads a dict into keyword arguments: print(1, 2, **{"sep": "-"}) is the same as print(1, 2, sep="-") and prints 1-2.

Together they let a function accept any arguments and pass them to another function unchanged. The show_price function below prints the prefix price: and passes everything it received to price:

Python
def price(amount, currency="USD"):
    return f"{amount} {currency}"


def show_price(*args, **kwargs):
    print("price:", price(*args, **kwargs))


show_price(250)  # price: 250 USD
show_price(250, currency="EUR")  # price: 250 EUR

Let's take the second call step by step:

  1. show_price(250, currency="EUR") puts 250 into args and currency="EUR" into kwargs.
  2. price(*args, **kwargs) spreads them back and becomes the call price(250, currency="EUR").
  3. price returns 250 EUR, and show_price prints it after the prefix.

Interviewers like to ask this: "What are *args and **kwargs?" A good answer has three parts:

  • *args collects extra positional arguments into a tuple, and **kwargs collects extra keyword ones into a dict;
  • if nothing extra was passed, they are an empty tuple and dict, not None;
  • they let a function take any number of arguments or pass them on.

Common mistake

A function with a star is given a ready-made list:

Python
def total(*prices):
    return sum(prices)


print(total([250, 100]))  # ✗
TEXT
TypeError: unsupported operand type(s) for +: 'int' and 'list'

The list is one argument, so prices is ([250, 100],): a tuple of one element, the list. You dropped a whole wallet into the funnel instead of coins, and the pack holds one item. sum tries to add this list to the number 0 and stops. Unpack the list in the call: total(*[250, 100]) returns 350.

Key points

  • *args collects extra positional arguments into a tuple, **kwargs collects extra keyword ones into a dict.
  • With no extra arguments, they are empty—() and {}, not None.
  • In a call, * and ** spread a list and a dict into separate arguments.
  • The pair *args, **kwargs passes any arguments on to another function unchanged.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way