runtime
71 / 250
lesson 71 Advanced Functions & Recursion · intermediate

Any number of arguments: *args and **kwargs

A starred parameter collects any number of positional arguments into a tuple; a double-starred one collects named arguments into a dictionary.

You'll be able to
  • Collect extra positional arguments with *name, and know that they arrive as a tuple
  • Collect named arguments with **name as a dictionary, and walk it with items()
  • Spread a list into separate arguments with * and a dictionary into named ones with ** at the call
The idea

print takes one value or ten; max takes two numbers or twenty. Your own functions can do the same. A parameter written with one star, *readings, collects every extra positional argument into a tuple, an unchangeable sequence you can loop over, index, and pass to sum or len. With no extra arguments it is an empty tuple, never missing.

Run each example to see what it prints. Running examples doesn't count toward progress.

example-1.py · The average of any number of readings
def average(*readings):
    print(type(readings), readings)
    if not readings:
        return None
    return sum(readings) / len(readings)

print(average(6.0, 7.5, 3.0))
print(average())
week = [4.0, 6.0]
print(average(*week))

Because *args is a tuple, it can't be changed in place. Build a new list from it if you need to add or remove items.

example-2.py · Appending to the collected arguments
def add_tags(*tags):
    tags.append("new")
    return tags

print(add_tags("sale", "summer"))

Two stars, **conditions, collect every named argument the definition doesn't list into a dictionary: the names become keys. This is how search functions accept whichever filters the caller cares about. In a definition the order is fixed: ordinary parameters, then *args, then any parameters with defaults, then **kwargs.

example-3.py · Search flights by any fields
def find_flights(flights, **conditions):
    return [f["flight"] for f in flights if all(f[field] == value for field, value in conditions.items())]

flights = [
    {"flight": "UA2017", "origin": "LAS", "airline": "UA"},
    {"flight": "MQ3443", "origin": "MCI", "airline": "MQ"},
    {"flight": "UA1552", "origin": "LAS", "airline": "UA"},
    {"flight": "DL1270", "origin": "PDX", "airline": "DL"},
]
print(find_flights(flights, origin="LAS"))
print(find_flights(flights, origin="LAS", airline="DL"))
print(find_flights(flights))

When to use it: when callers naturally pass a varying number of similar values (average(…), merge_tags(…)), or optional named settings you pass along. Mistakes to avoid: treating *args as a list (it is a tuple, so append fails); forgetting the star at the call (average(week) passes one list); and reaching for *args when a single list parameter would be clearer, for example when the caller already has the values in a list.

Review
  • *args collects extra positional arguments into a tuple (empty when there are none).
  • **kwargs collects extra named arguments into a dictionary; walk it with items().
  • At a call, *list spreads a list into arguments and **dict spreads a dictionary into named ones.
  • Next: parameters that must be passed by name, so calls stay readable.
Practise this

Loading your progress…

To master this lesson, solve both challenges without hints or the answer, and pass the concept check.

Any number of arguments: *args and **kwargs — Advanced Functions & Recursion · Runtime