Function Arguments
Core Python Functions and Modules

Python Function Arguments

1Function Arguments: overview

A function argument is the value you pass in a call, and Python matches each argument to a parameter either by position or by name, following the order rules in the def line.

GoalBy the end you can call a function with positional and keyword arguments, design parameters with defaults, keyword-only and positional-only rules, avoid the mutable default trap, and predict what a function can change in the objects you pass.

Before this lesson: Python Functions

2Function Arguments: diagram

Python Function Argumentsship("Pune", 2.5, express=True)arguments in the callparameters in the def"Pune" (1st place)city2.5 (2nd place)weightexpress=True (by name)expressdef ship(city, weight, express=False)
Each argument in the call is matched to one parameter in the def line: positional arguments by their place, keyword arguments by their name. Every parameter must end up with exactly one value.

3Function Arguments: explained step by step

In the last lesson you passed values into a function and got a result back. This lesson looks at the part that causes most beginner bugs: how Python matches the values in a call to the names in the def line, which rules control the order of those names, and what really happens to the objects you hand over.

Parameters and arguments, one more time

A parameter is a name in the def line. An argument is the value you give that name in a call. When the call starts, Python binds each argument to a parameter, exactly like an assignment such as city = "Pune". The diagram above shows that matching for one call.

Positional and keyword arguments

You can supply an argument in two ways. A positional argument is matched by its place in the call: the first value goes to the first parameter, the second to the second. A keyword argument is matched by name, as in weight=2.5. You may mix the two, but every positional argument has to come before the first keyword argument.

Python
def ship(city, weight, express=False):
    speed = "express" if express else "standard"
    return f"{weight} kg to {city} ({speed})"

print(ship("Pune", 2.5))
print(ship("Pune", 2.5, True))
print(ship(weight=2.5, city="Pune", express=True))
print(ship("Pune", express=True, weight=2.5))

Keyword arguments can appear in any order, and they make a call readable: express=True says far more than a bare True. Python refuses to fill the same parameter twice.

The order of parameters in a def

A def line can hold four kinds of parameters. They always appear in this order:

def name(only_pos, /, normal, with_default=1, *, only_kw, kw_default=2):
    ...
  • Names before the / can only be given by position.
  • Names between / and * can be given either way. A normal parameter with no default may not come after one that has a default, otherwise Python raises SyntaxError: parameter without a default follows parameter with a default.
  • Names after the * can only be given by keyword. They are exempt from the default rule, so a required keyword-only parameter may follow an optional one.

You rarely need all four kinds at once. The two markers let a function author protect callers from mistakes.

Keyword-only parameters

Put a bare * in the parameter list and everything after it must be passed by name. This is the right choice for flags and options, where a bare True in a call would be impossible to read.

Python
def send_email(to, subject, *, urgent=False):
    tag = "[URGENT] " if urgent else ""
    return f"To {to}: {tag}{subject}"

print(send_email("asha@example.com", "Lunch?"))
print(send_email("asha@example.com", "Server down", urgent=True))

try:
    send_email("asha@example.com", "Server down", True)
except TypeError as error:
    print(error)

Positional-only parameters

A / in the list makes every parameter before it positional-only. Use it when the name of the parameter is not meaningful to the caller, as with the built-in len(x). It also lets you rename the parameter later without breaking anyone's code.

Python
def clamp(value, /, low=0, high=100):
    return max(low, min(value, high))

print(clamp(150))
print(clamp(-5, low=-10))

try:
    clamp(value=150)
except TypeError as error:
    print(error)

The mutable default trap

A default value is created once, when Python executes the def line, not on every call. For a number, string or None that makes no difference, because they cannot change. For a list or dictionary it is a trap: every call that uses the default receives the same object.

Python
def add_task(task, tasks=[]):
    tasks.append(task)
    return tasks

print(add_task("email"))
print(add_task("report"))
print(add_task.__defaults__)

The second call returns ['email', 'report'] even though you never passed the first task in. The last line shows why: the one list lives on the function itself. The standard fix is to use None as a signal and create the list inside the body:

Python
def add_task(task, tasks=None):
    if tasks is None:
        tasks = []
    tasks.append(task)
    return tasks

print(add_task("email"))
print(add_task("report"))
today = ["standup"]
print(add_task("review", today))

Test with is None, not if not tasks: an empty list the caller passed in is falsy, and you would silently replace it.

Arguments are shared, not copied

Python does not copy the values you pass. The parameter becomes a second name for the same object. If the function changes that object (append, item assignment), the caller sees the change. If the function assigns a new value to the parameter name, only the local name moves.

Python
def add_bonus(scores):
    for i in range(len(scores)):
        scores[i] += 5

def raise_limit(limit):
    limit = limit + 100
    return limit

marks = [60, 70]
add_bonus(marks)
print(marks)

budget = 500
print(raise_limit(budget), budget)

Be careful with +=: on a list it extends the list in place, so names += ["Zed"] changes the caller's list, while names = names + ["Zed"] builds a new one. If a function must not touch its input, work on a copy with list(scores) or return a new list.

Returning several values

A function can only return one object, but you can return a tuple and unpack it at the call site. The parentheses are optional, so return low, high is a tuple.

Python
def min_max(numbers):
    return min(numbers), max(numbers)

result = min_max([4, 9, 2])
print(result, type(result).__name__)
low, high = min_max([4, 9, 2])
print(low, high)

If you return more than three values, or the order is easy to forget, return a dictionary so each value has a name.

Reading the TypeError messages

Calling a function with the wrong arguments raises a TypeError that names the function and the problem. Take def area(width, height):

  • area() missing 1 required positional argument: 'height' means you passed too few.
  • area() takes 2 positional arguments but 3 were given means you passed too many.
  • area() got an unexpected keyword argument 'depth' means the name does not exist.
  • area() got multiple values for argument 'width' means one parameter was filled twice, for example area(5, width=5).

Putting a positional argument after a keyword one, as in area(width=5, 3), is caught even earlier as a SyntaxError: positional argument follows keyword argument.

Quick reference: Function Arguments

4Function Arguments: syntax

def f(a, b, /, c, d=1, *, e, g=2):
    ...                         # a, b: positional-only (before /)
                                # c, d: positional or keyword
                                # e, g: keyword-only (after *)

f(1, 2, 3, e=4)                 # positional arguments first, then keywords
f(1, 2, c=3, d=5, e=4)          # c and d may be given by name

def add(item, bag=None):        # None-sentinel: safe default for a list
    if bag is None:
        bag = []
    bag.append(item)
    return bag

def low_high(values):
    return min(values), max(values)   # one tuple

low, high = low_high([3, 1, 2])       # unpack it

5Function Arguments: run the example

Press Run (or Ctrl + Enter). The output (step 6) appears directly below the code. Change anything and run it again.

Interactive example
def start_order(customer, items=None, *, gift=False):
    if items is None:
        items = []
    return {"customer": customer, "items": items, "gift": gift}

first = start_order("Asha")
second = start_order("Ben", ["tea"], gift=True)
first["items"].append("coffee")

print(first)
print(second)
print(start_order("Cara")["items"])
Expected output{'customer': 'Asha', 'items': ['coffee'], 'gift': False}
{'customer': 'Ben', 'items': ['tea'], 'gift': True}
[]

7Function Arguments: how the example works

Hover or tap a step to highlight its lines in the example above.

  1. Line 1customer is a normal parameter, items has a default, and the bare * makes gift keyword-only. Callers must write gift=True instead of a bare True.
  2. Line 2-3The default for items is None, not []. If the caller did not pass a list, a brand-new empty list is created for this call only. Using is None keeps a list the caller passed in, even when it is empty.
  3. Line 4The function returns one dictionary that bundles the three values.
  4. Line 6Only customer is given. items is None inside the call, so Asha receives her own fresh list.
  5. Line 7"Ben" goes to customer and ["tea"] to items by position. gift=True is matched by name.
  6. Line 8Asha's list is changed in place with append. Because it was created inside the call, nobody else holds a reference to it.
  7. Line 10-11Asha's order shows coffee, and Ben's order is untouched with his own list and gift set to True.
  8. Line 12A third order starts with an empty list. With a [] default, it would have started with ['coffee'].

8Function Arguments: common mistakes

A list as a default value

✗ Wrong

def add_tag(tag, tags=[]):
    tags.append(tag)
    return tags

print(add_tag("python"))
print(add_tag("django"))
No error, but the second line prints ['python', 'django'] instead of ['django']

✓ Right

def add_tag(tag, tags=None):
    if tags is None:
        tags = []
    tags.append(tag)
    return tags

print(add_tag("python"))
print(add_tag("django"))

The default list is created once and reused by every call. Use None as the default and create the list inside the function.

Passing too few arguments

✗ Wrong

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

print(area(5))
TypeError: area() missing 1 required positional argument: 'height'

✓ Right

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

print(area(5, 3))

Every parameter without a default needs a value. Pass the missing argument, or give the parameter a default if it is really optional.

Giving the same parameter two values

✗ Wrong

def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

print(greet("Asha", name="Asha"))
TypeError: greet() got multiple values for argument 'name'

✓ Right

def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

print(greet("Asha", greeting="Welcome"))

"Asha" already fills name by position, and name="Asha" fills it again. Supply each parameter once, either by position or by name.

Passing a keyword-only argument by position

✗ Wrong

def connect(host, *, timeout=5):
    return f"{host} (timeout {timeout}s)"

print(connect("db.local", 30))
TypeError: connect() takes 1 positional argument but 2 were given

✓ Right

def connect(host, *, timeout=5):
    return f"{host} (timeout {timeout}s)"

print(connect("db.local", timeout=30))

Everything after the bare * must be passed by name. Write timeout=30.

Changing the caller's list by accident

✗ Wrong

def top_scores(scores):
    scores.sort(reverse=True)
    return scores[:2]

marks = [70, 95, 82]
print(top_scores(marks))
print(marks)
No error, but the second line prints [95, 82, 70]: the original list was reordered

✓ Right

def top_scores(scores):
    return sorted(scores, reverse=True)[:2]

marks = [70, 95, 82]
print(top_scores(marks))
print(marks)

The parameter is the same list object as marks, so sort() changes it for the caller too. Use sorted(), which returns a new list and leaves the original alone.

9Function Arguments: exercises

Write your answer, then press Check. The checks run in your browser.

1. Return the lowest and highest

Write code Easy

Write a function min_max(numbers) that returns both the smallest and the largest number in the list as one tuple (smallest, largest).

Then call it on temperatures and unpack the result into two variables called low and high, and print them.

Your code
temperatures = [18, 25, 11, 30, 22]

def min_max(numbers):
    # return the smallest and the largest number
    pass

# unpack the result into low and high, then print both

2. Predict: change or rebind?

Predict the output Medium

The function changes its parameter in two different ways. Type exactly what the program prints, then press Check.

Read this code
def update(team, count):
    team.append("Zed")
    count = count + 1
    team = ["Reset"]
    return team, count

players = ["Asha", "Ben"]
number = 2
new_team, new_number = update(players, number)
print(players, number)
print(new_team, new_number)

2 more exercises → 6 more examples

Finished this lesson?
Progress is saved in this browser — create a free account to keep it across devices.

Questions learners ask

What is the difference between an argument and a parameter in Python?

A parameter is the name in the function definition (def area(width, height)). An argument is the actual value you pass when you call the function (area(5, 3)). At call time Python binds each argument to a parameter. People often mix the words up, and it rarely matters in conversation, but error messages use the exact terms.

Why does my function remember values from the previous call?

You almost certainly used a mutable object such as a list or dictionary as a default value. Python creates that object once, when the def line runs, and reuses it for every call that omits the argument. Use None as the default and create the list inside the function body.

What do the * and / mean in a function definition?

A bare * means every parameter after it must be passed by keyword. A / means every parameter before it must be passed by position. They do not take values themselves. They only mark where the rules change, which lets a function author stop callers from writing confusing calls.

Further reading

10Next lesson

Up nextPython *args and **kwargs*args collects any extra positional arguments into a tuple and **kwargs collects any extra keyword arguments into a dic…

Related:
Copying Lists and DictsPython FunctionsPython *args and **kwargsPython Variable Scope

← Python Functions · All Functions and Modules lessons · Practise in the IDE