Chapters
Python114 chapters

FunctionsChapter 49 of 114

Arguments

Positional and keyword arguments, and how Python matches them up.

Positional arguments

Matched by order:

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

describe("Ada", 36)

Output

Ada is 36

Get the order wrong and you get nonsense rather than an error:

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

describe(36, "Ada")

Output

36 is Ada

Keyword arguments

Naming them removes the ordering problem entirely:

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

describe(age=36, name="Ada")

Output

Ada is 36

Keyword arguments must come after positional ones:

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

describe("Ada", age=36)

Output

Ada is 36

Booleans are the worst offenders. save(data, True) tells the reader nothing; save(data, overwrite=True) tells them everything.

Forcing the style

A * in the parameter list means everything after it must be passed by keyword:

Python
def connect(host, *, timeout=10, retries=3):
    print(host, timeout, retries)

connect("example.com", timeout=5)

try:
    connect("example.com", 5)
except TypeError as problem:
    print("TypeError:", problem)

Output

example.com 5 3
TypeError: connect() takes 1 positional argument but 2 were given

A / does the opposite, marking parameters before it as positional-only. You will see it in the standard library more often than you will write it.

Arguments are passed by assignment

The parameter becomes another name for the same object. What that means depends on whether the object can be changed.

Rebinding inside the function does not affect the caller:

Python
def rename(name):
    name = "changed"

value = "original"
rename(value)
print(value)

Output

original

But changing a mutable object in place does:

Python
def add_item(items):
    items.append("new")

values = ["first"]
add_item(values)
print(values)

Output

['first', 'new']

Both are the same rule: the name inside is a new label on the same object. Assigning moves that label; mutating changes the object everyone can see.

The mutable default trap

This one catches nearly everyone once, and it is worth meeting on purpose:

Python
def add(item, target=[]):
    target.append(item)
    return target

print(add("a"))
print(add("b"))

Output

['a']
['a', 'b']

The default is created once, when the function is defined, not on each call. Every call that relies on the default shares the same list.

The fix is always the same:

Python
def add(item, target=None):
    if target is None:
        target = []
    target.append(item)
    return target

print(add("a"))
print(add("b"))

Output

['a']
['b']

Unpacking into arguments

* spreads a sequence into positional arguments and ** spreads a dictionary into keyword ones:

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

values = ["Ada", 36]
describe(*values)

details = {"name": "Grace", "age": 45}
describe(**details)

Output

Ada is 36
Grace is 45

Test yourself

2 questions

What is wrong with 'def add(item, target=[])'?

Show the answer

The list is created once and shared by every call — Use None as the default and build the real list inside the function.

What does the do in 'def connect(host, , timeout=10)'?

Show the answer

Forces everything after it to be passed by keyword — It is a good way to stop callers writing connect(host, 5) where 5 means nothing to a reader.

Next chapter

args and kwargs

Accept any number of positional or keyword arguments.