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.
Before this lesson: Python Functions
2Function Arguments: diagram
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.
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 raisesSyntaxError: 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.
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.
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.
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:
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.
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.
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 givenmeans 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 examplearea(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
- Python Functions Cheat Sheet: def, args, lambda, scope — Define a Python function with `def name(parameters):` and `return` a result; use default values, `*args` and …
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.
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.
- Line 1
customeris a normal parameter,itemshas a default, and the bare*makesgiftkeyword-only. Callers must writegift=Trueinstead of a bareTrue. - Line 2-3The default for
itemsisNone, not[]. If the caller did not pass a list, a brand-new empty list is created for this call only. Usingis Nonekeeps a list the caller passed in, even when it is empty. - Line 4The function returns one dictionary that bundles the three values.
- Line 6Only
customeris given.itemsisNoneinside the call, so Asha receives her own fresh list. - Line 7
"Ben"goes tocustomerand["tea"]toitemsby position.gift=Trueis matched by name. - Line 8Asha's list is changed in place with
append. Because it was created inside the call, nobody else holds a reference to it. - Line 10-11Asha's order shows
coffee, and Ben's order is untouched with his own list andgiftset toTrue. - 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"))✓ 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))✓ 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"))✓ 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))✓ 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)✓ 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 ✓ CompletedWrite 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.
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
low, high = ....temperatures = [18, 25, 11, 30, 22]
def min_max(numbers):
return min(numbers), max(numbers)
low, high = min_max(temperatures)
print(low, high)
2. Predict: change or rebind?
Predict the output Medium ✓ CompletedThe function changes its parameter in two different ways. Type exactly what the program prints, then press Check.
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)
append changes the object that both names point to. Assigning to team or count only moves the local name; the caller's names still point at the old objects.['Asha', 'Ben', 'Zed'] 2
['Reset'] 3
2 more exercises → 6 more examples
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