Why this matters
A function is only as useful as its interface: the way callers hand it data. Python gives you a rich toolkit here: optional parameters with defaults, arguments passed by name, functions that accept any number of values, and markers that force callers to be explicit. You meet all of these constantly, for example print(*items, sep=", ") and sorted(data, key=len, reverse=True). Knowing how arguments are matched to parameters, and when default values are created, lets you read any library's documentation and design clear functions yourself.
Vocabulary
- A parameter is a name in the function definition:
def area(width, height). - An argument is a value supplied in a call:
area(3, 4).
When the call happens, Python binds each parameter name to an argument object in the new frame. No copying of the object happens; the parameter is just another name for the same object, exactly as in assignment. (Lesson 5 of the collections module shows why that matters for lists.)
Positional and keyword arguments
Arguments can be matched by position or by name:
def describe(name, age, city):
return name + " (" + str(age) + ") from " + city
print(describe("Ada", 36, "London")) # positional
print(describe(city="Paris", name="Marie", age=66)) # keyword: any order
print(describe("Alan", city="Wilmslow", age=41)) # mixed
Ada (36) from London
Marie (66) from Paris
Alan (41) from Wilmslow
Rules: positional arguments come first, keyword arguments after; each parameter may be filled only once. Keyword arguments make calls self-documenting: connect(host, timeout=5) is clearer than connect(host, 5).
Default values
A parameter can have a default, used when the caller omits it. Parameters with defaults must come after those without:
def power(base, exponent=2):
return base ** exponent
print(power(5)) # 25
print(power(2, 10)) # 1024
print(power(2, exponent=3)) # 8
When defaults are evaluated
Here is a crucial detail about what happens underneath: the default expression is evaluated once, when the def statement executes, and the resulting object is stored on the function object. Every call that omits the argument gets that same object.
For immutable values like numbers and strings that is harmless. For a mutable object such as a list it is a famous bug:
def add_item(item, basket=[]):
basket.append(item) # mutates the one shared default list
return basket
print(add_item("apple"))
print(add_item("pear"))
print(add_item.__defaults__)
['apple']
['apple', 'pear']
(['apple', 'pear'],)
__defaults__ shows the default list living on the function object and growing with every call. (A list is written [...]; .append(x) adds x to its end. The next module covers lists properly.)
The idiom that fixes it uses None as a "nothing passed" marker and makes a fresh list inside the function, on every call:
def add_item(item, basket=None):
if basket is None:
basket = []
basket.append(item)
return basket
Use is None rather than == None or if not basket: an empty list passed by the caller is falsy, and you do not want to throw it away.
Any number of arguments: *args
Prefixing a parameter with * collects all extra positional arguments into a tuple (an immutable sequence, written with parentheses):
def total(*amounts):
print("got", amounts)
result = 0
for a in amounts:
result += a
return result
print(total(1, 2, 3))
print(total())
got (1, 2, 3)
6
got ()
0
The name args is a convention, not a keyword; *amounts is clearer here. This is how print accepts any number of things to print.
The * also works in the other direction, at the call site: *sequence unpacks a sequence into separate positional arguments:
values = [4, 5, 6]
print(total(*values)) # same as total(4, 5, 6)
print(*values, sep="-")
got (4, 5, 6)
15
4-5-6
Any keyword arguments: **kwargs
** collects extra keyword arguments into a dictionary mapping names to values (dictionaries are covered in the next module; for now, think "named values"):
def show_options(**options):
for name in options:
print(name, "=", options[name])
show_options(color="red", size=3)
color = red
size = 3
Keyword arguments keep the order you wrote them in. **mapping at a call site unpacks a dictionary into keyword arguments. You will see **kwargs mostly in code that forwards arguments to another function, such as decorators (module 13).
Keyword-only and positional-only parameters
Parameters that come after *args, or after a bare *, can only be passed by keyword:
def resize(image, *, width, height):
...
resize("cat.png", width=100, height=50) # fine
resize("cat.png", 100, 50)
TypeError: resize() takes 1 positional argument but 3 were given
This is a design tool: for settings that are easily confused (which number is width?) force callers to name them.
Parameters before a / are positional-only: callers may not use their names. Many built-ins work this way; help(len) shows len(obj, /):
def distance(x, y, /):
return (x * x + y * y) ** 0.5
print(distance(3, 4)) # 5.0
distance(x=3, y=4)
TypeError: distance() got some positional-only arguments passed as keyword arguments: 'x, y'
Positional-only parameters let a library rename them later without breaking anyone's code.
The full order in a definition is:
def f(pos_only, /, normal, *args, kw_only, **kwargs):
You will rarely need all of them at once; recognizing each piece is the important skill.
How Python matches a call
Conceptually, for each call CPython:
- Fills parameters from the positional arguments, left to right; extras go into
*args(or it is aTypeErrorif there is no*args). - Fills parameters from the keyword arguments by name; unknown names go into
**kwargs(orTypeError: got an unexpected keyword argument). A name already filled positionally isTypeError: got multiple values for argument. - Uses defaults for anything still empty; anything still missing is
TypeError: missing ... required argument.
Common mistakes
- Mutable default values (
=[],={}): useNoneand create inside. - Positional after keyword in a call,
f(a=1, 2):SyntaxError: positional argument follows keyword argument. - Default before non-default in a def,
def f(a=1, b):SyntaxError: parameter without a default follows parameter with a default. - Passing a list where
*argsexpects items:total([1, 2, 3])makesamounts == ([1, 2, 3],), a tuple holding one list, and adding0 + [1, 2, 3]fails. Usetotal(*[1, 2, 3]). - Misspelled keyword:
power(2, exponet=3)givesTypeError: power() got an unexpected keyword argument 'exponet'. Helpful, if you read it.
Recap
- Arguments bind to parameters by position, then by name; each parameter exactly once.
- Defaults are evaluated once at
deftime and shared by all calls; never use a mutable default. *argscollects extra positional arguments into a tuple;**kwargscollects extra keyword arguments into a dict. At a call,*and**unpack.- Parameters after
*are keyword-only; parameters before/are positional-only.
Going further: read the "More on Defining Functions" section of the official Python tutorial (docs.python.org/3/tutorial/controlflow.html). Then run help(sorted) and help(print) and identify which parameters are positional-only and which are keyword-only.