Chapters
Python114 chapters

FunctionsChapter 50 of 114

args and kwargs

Accept any number of positional or keyword arguments.

*args collects extra positional arguments

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

print(total(1, 2))
print(total(1, 2, 3, 4))
print(total())

Output

3
10
0

Inside the function, numbers is a tuple of whatever was passed. The name args is only a convention — the * is what does the work — but stick to it, because everyone recognises it.

Python
def show(*args):
    print(type(args), args)

show(1, "two")

Output

<class 'tuple'> (1, 'two')

**kwargs collects extra keyword arguments

Python
def show(**kwargs):
    print(type(kwargs), kwargs)
    for key, value in kwargs.items():
        print(f"{key} = {value}")

show(colour="red", size=10)

Output

<class 'dict'> {'colour': 'red', 'size': 10}
colour = red
size = 10

Here it is a dictionary, keyed by the argument names.

Combining them

Ordinary parameters come first, then *args, then keyword-only parameters, then **kwargs:

Python
def report(title, *rows, separator=" | ", **options):
    print(title)
    print(separator.join(rows))
    print(options)

report("Names", "Ada", "Grace", separator=", ", colour="red")

Output

Names
Ada, Grace
{'colour': 'red'}

Note that separator was picked out by name and did not land in options. Anything named that matches a real parameter goes there; only leftovers go into **kwargs.

Passing them on

The most common real use is a wrapper that accepts anything and hands it straight to something else:

Python
def log_call(fn, *args, **kwargs):
    print("calling", fn.__name__, "with", args, kwargs)
    return fn(*args, **kwargs)

def area(width, height=2):
    return width * height

print(log_call(area, 3, height=4))

Output

calling area with (3,) {'height': 4}
12

The * and ** do opposite jobs depending on where they appear: in a definition they collect, in a call they spread.

Unpacking in a call

Python
def describe(name, age):
    print(f"{name} is {age}")

values = ("Ada", 36)
details = {"name": "Grace", "age": 45}

describe(*values)
describe(**details)

Output

Ada is 36
Grace is 45

The dictionary's keys have to match the parameter names exactly, or you get a TypeError about an unexpected argument.

Do not reach for it too early

*args makes a function flexible and its signature useless. A reader can no longer see what it takes, and neither can their editor:

Python
def draw(*args):
    print(args)

draw(1, 2, 3, 4)

Output

(1, 2, 3, 4)

If the arguments have meanings, name them. Save *args for genuine pass-through wrappers and for functions that really do take an unbounded list of the same kind of thing, like sum or print.

Test yourself

2 questions

What type is args inside 'def total(*args)'?

Show the answer

tuple — kwargs is the dictionary. args is always a tuple.

What do * and ** do at a call site rather than in a definition?

Show the answer

Spread a sequence or dictionary into arguments — In a definition they collect, in a call they spread. Same symbols, opposite jobs.

Next chapter

Default Values

Make a parameter optional, and avoid the shared-default trap.