In the functions lesson every function had a fixed list of parameters. But some of Python's own functions are more flexible. print() accepts one value or twenty. max(3, 9, 4) works with any number of arguments. dict(name="Ada", age=36) accepts whatever keywords you give it. How do they do it?
The answer is *args and **kwargs, two special parameters that collect "everything else" the caller passes in. The same star symbols also work the other way round, unpacking a list or dictionary into separate arguments.
In this lesson you'll write flexible functions with *args and **kwargs, unpack values into calls, force callers to name certain arguments, return several values at once, and understand scope: which variables a function can see and change. You'll also learn about a classic Python trap that catches experienced programmers too.
*args: any number of positional arguments
A parameter that starts with * collects all extra positional arguments into a tuple:
def total(*prices):
return sum(prices)
print(total(2, 3)) # 5
print(total(1.5, 2, 4, 10)) # 17.5
print(total()) # 0The name args is only a convention; *prices is clearer here. You can combine it with normal parameters, which come first: def order(customer, *items):.
**kwargs: any number of keyword arguments
A parameter that starts with ** collects extra keyword arguments into a dictionary:
def profile(name, **details):
print(name)
for key, value in details.items():
print(f" {key}: {value}")
profile("Ada", city="London", role="Engineer")The full order of parameters is: normal ones, *args, keyword-only ones, then **kwargs. **kwargs is useful for options, settings and functions that pass arguments on to another function.
Unpacking and keyword-only arguments
Stars also work when calling a function. * spreads a list or tuple into positional arguments, and ** spreads a dictionary into keyword arguments:
point = [3, 4]
settings = {"sep": " | ", "end": "!\n"}
print(*point, **settings) # 3 | 4!A bare * in a definition makes every parameter after it keyword-only. def book(room, *, nights, breakfast=False): must be called as book(12, nights=2), so nobody can mix up two numbers by accident.
Positional-only parameters
Python 3.8 added the opposite of keyword-only parameters. Parameters listed before a / in the definition are positional-only: callers can't pass them by name.
def power(base, exponent, /):
return base ** exponent
power(2, 10) # 1024
power(base=2, exponent=10) # TypeErrorMany built-in functions work like this, for example len(obj), which is why len(obj=items) fails. Library authors use / so they can rename a parameter later without breaking anyone's code. In your own scripts you will mostly read it rather than write it.
Scope: local and global
A variable created inside a function is local: it exists only while the function runs, and the rest of the program can't see it. A variable created at the top level of a file is global, and functions can read it.
To change a global variable inside a function you'd need the global keyword, but it's usually better to pass values in and return results. Code where functions secretly change global variables is hard to follow and test.
A function can return several values at once by separating them with commas. Python packs them into a tuple, and the caller can unpack them: low, high = min_max(scores).
How Python looks up a name
When a function uses a name, Python searches four places in order, often remembered as LEGB:
- Local: names created inside the current function.
- Enclosing: names in any function that contains this one.
- Global: names at the top level of the module (file).
- Built-in: Python's own names, such as
lenandprint.
The first match wins. This is why creating a variable called list or sum is a bad idea: your global name hides the built-in, and later calls such as sum(values) fail with a confusing error.
Assigning to a name inside a function makes it local for the whole function, which explains the UnboundLocalError in the mistake below. To assign to a variable from an enclosing function, declare it with nonlocal; to assign to a module-level variable, use global. Both should be rare in everyday code.
Python Function Arguments Syntax
Parameter order
def func(a, b=2, *args, key, option=None, **kwargs):
...
func(1, 2, 3, 4, key="k", extra=True)
func(*[1, 2], **{"key": "k"})
def min_max(values):
return min(values), max(values)
low, high = min_max([4, 9, 1])Python Function Arguments Summary
*argscollects extra positional arguments into a tuple.**kwargscollects extra keyword arguments into a dictionary.*and**in a call unpack a list or dictionary into arguments.- Parameters after a bare
*are keyword-only. - Variables inside a function are local; prefer returning values to using
global. Never use a list or dict as a default value.
Python Function Arguments Examples
Each example shows the code, the output it produces and what each part does.
A flexible average
def average(*numbers, decimals=1):
if not numbers:
return 0
return round(sum(numbers) / len(numbers), decimals)
print(average(4, 8, 9))
print(average(7, 8, decimals=2))
scores = [62, 75, 81, 90]
print(average(*scores))7.0
7.5
77.0Line-by-line explanation
- Line 1decimals comes after *numbers, so it can only be given by name.
- Lines 2 to 3An empty tuple is falsy, so calling average() with no numbers returns 0 instead of crashing.
- Line 9*scores unpacks the list into four separate arguments.
Building HTML attributes with **kwargs
def tag(name, text, **attributes):
attrs = "".join(f' {key}="{value}"' for key, value in attributes.items())
return f"<{name}{attrs}>{text}</{name}>"
print(tag("p", "Hello"))
print(tag("a", "Home", href="index.html", title="Go home"))<p>Hello</p>
<a href="index.html" title="Go home">Home</a>Line-by-line explanation
- Line 1Any keyword arguments end up in the attributes dictionary.
- Line 2Each key and value becomes an HTML attribute.
- Line 6href and title weren't listed as parameters, but **attributes accepts them.
Local, global and returning several values
tax_rate = 0.2
def price_with_tax(price):
tax = price * tax_rate
return price + tax, tax
total, tax = price_with_tax(50)
print(total, tax)
print(tax_rate)60.0 10.0
0.2Line-by-line explanation
- Line 4Reading the global tax_rate inside the function is fine.
- Line 5Returning two values gives back a tuple.
- Line 7Unpack the tuple into two variables. This tax is a new global variable; the function's own tax variable disappeared when it finished.
Common Python Function Arguments Mistakes
The mutable default argument
def add_item(item, basket=[]):
basket.append(item)
return basket
print(add_item("apple")) # ['apple']
print(add_item("bread")) # ['apple', 'bread'] surprise!def add_item(item, basket=None):
if basket is None:
basket = []
basket.append(item)
return basketChanging a global without saying so
count = 0
def increase():
count += 1 # UnboundLocalErrordef increase(count):
return count + 1
count = increase(count)Putting *args in the wrong place
def order(*items, customer):
...
order("tea", "cake", "Sam") # TypeError: missing customerdef order(customer, *items):
...
order("Sam", "tea", "cake")Python Function Arguments in Real Projects
Flexible arguments in libraries
Many popular libraries use these features. Web frameworks pass extra options through **kwargs, plotting functions accept dozens of optional keyword settings, and decorators rely on *args, **kwargs to wrap any function regardless of its parameters. Keyword-only arguments appear throughout Python's standard library, such as sorted(items, key=..., reverse=True).
Python Function Arguments FAQ
What do * and ** mean in a Python function definition?
In a definition, *args collects extra positional arguments into a tuple and **kwargs collects extra keyword arguments into a dictionary. In a function call, the same symbols do the reverse: * spreads a list or tuple into separate arguments and ** spreads a dictionary into keyword arguments.
Do I have to call them args and kwargs?
No. Only the stars matter; args and kwargs are conventions. A descriptive name such as *prices or **options is often clearer. Stick with the conventional names when writing general wrappers, such as decorators, where readers expect them.
When should I use the global keyword?
Rarely. global lets a function reassign a module-level variable, but it hides where changes come from and makes testing harder. Passing values in as arguments and returning new values is almost always clearer. Reading a global constant, such as TAX_RATE, needs no keyword at all.
What is the difference between global and nonlocal?
global refers to a variable at the top level of the module. nonlocal refers to a variable in the nearest enclosing function, and only works inside nested functions. Both are needed only when you assign to the variable; reading it works without either keyword.
Why does Python say an argument was given more than once?
This TypeError happens when you pass the same parameter both by position and by name, for example greet("Ada", name="Bob") for def greet(name). Python fills name from the first argument, then finds a second value for it. Pass each parameter only once.
Python Function Arguments Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the definition so the function accepts any number of names.
def greet_all(names):
for name in names:
print("Hi", name)
What does this print?
def count(*things):
return len(things)
print(count("a", "b", "c"))
What does this print?
def show(**info):
print(sorted(info))
show(b=2, a=1)
Given def book(room, *, nights):, which call works?
What does this print?
def f(x, items=[]):
items.append(x)
return len(items)
f(1)
print(f(2))
Python Function Arguments Quiz
5 questions to check what you learned.
What type is
argsinsidedef f(*args):?- list
- dict
- set
- tuple
What type is
kwargsinsidedef f(**kwargs):?- list
- tuple
- str
- dict
What does
print(*[1, 2, 3])print?- [1, 2, 3]
- 1 2 3
- 123
- (1, 2, 3)
What happens to a local variable when its function finishes?
- It disappears
- It becomes global
- It's saved for the next call
- It's printed
What's the safe default for a parameter that should start as an empty list?
- basket=[]
- basket=list
- basket=None, then create a list inside the function
- basket=()