5 Types of Python Function Arguments
Python functions support five types of arguments: positional, keyword, default, arbitrary positional (*args), and arbitrary keyword (**kwargs). Understanding these types helps you write flexible and maintainable functions.
Contents
1. Positional arguments
Positional arguments are passed in the order they appear in the function definition. The first argument maps to the first parameter, the second to the second parameter, and so on.
def greet(name, age, city):
return f"{name}, {age}, from {city}"
print(greet("Alice", 25, "London"))
>>> Alice, 25, from London
The order matters with positional arguments. Changing the order changes the result.
def calculate_area(length, width):
return length * width
print(calculate_area(5, 3))
>>> 15
print(calculate_area(3, 5))
>>> 15
Positional arguments are the simplest and most common way to pass values to functions.
def add(a, b):
return a + b
print(add(10, 20))
>>> 30
2. Keyword arguments
Keyword arguments are passed by explicitly naming the parameter. This makes function calls more readable and allows you to pass arguments in any order.
def create_profile(name, age, city):
return f"{name}, {age}, from {city}"
print(create_profile(age=25, city="London", name="Alice"))
>>> Alice, 25, from London
You can mix positional and keyword arguments, but positional arguments must come before keyword arguments.
def calculate_total(price, tax_rate, discount):
return price * (1 + tax_rate) * (1 - discount)
print(calculate_total(100, tax_rate=0.20, discount=0.1))
>>> 108.0
print(calculate_total(100, 0.20, discount=0.1))
>>> 108.0
Keyword arguments improve code readability, especially with functions that have many parameters.
def format_date(day, month, year, separator="-"):
return f"{day}{separator}{month}{separator}{year}"
print(format_date(15, 3, 2024, separator="/"))
>>> 15/3/2024
3. Default arguments
Default arguments have predefined values that are used when the argument isn't provided. They make parameters optional.
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Alice"))
>>> Hello, Alice!
print(greet("Bob", "Hi"))
>>> Hi, Bob!
You can have multiple default arguments. Parameters with defaults must come after parameters without defaults.
def calculate_total(price, tax_rate=0.20, discount=0):
subtotal = price * (1 - discount)
return subtotal * (1 + tax_rate)
print(calculate_total(100))
>>> 120.0
print(calculate_total(100, tax_rate=0.15))
>>> 115.0
print(calculate_total(100, discount=0.1))
>>> 108.0
Default arguments are evaluated once when the function is defined, not each time it's called. Be careful with mutable default arguments.
def add_item(item, items=[]):
items.append(item)
return items
# This can lead to unexpected behaviour
print(add_item("apple"))
>>> ['apple']
print(add_item("banana"))
>>> ['apple', 'banana']
# Better approach: use None as default
def add_item_safe(item, items=None):
if items is None:
items = []
items.append(item)
return items
print(add_item_safe("apple"))
>>> ['apple']
print(add_item_safe("banana"))
>>> ['banana']
4. Arbitrary positional arguments (*args)
Arbitrary positional arguments allow a function to accept any number of positional arguments. The *args parameter collects them into a tuple.
def sum_all(*args):
return sum(args)
print(sum_all(1, 2, 3))
>>> 6
print(sum_all(10, 20, 30, 40, 50))
>>> 150
You can access individual arguments by indexing the tuple or iterate over them.
def print_numbers(*args):
for i, num in enumerate(args):
print(f"Number {i+1}: {num}")
print_numbers(10, 20, 30)
>>> Number 1: 10
>>> Number 2: 20
>>> Number 3: 30
The name args is a convention; you can use any name after the asterisk.
def multiply_all(*numbers):
result = 1
for num in numbers:
result *= num
return result
print(multiply_all(2, 3, 4))
>>> 24
You can combine *args with regular positional arguments. Regular arguments must come before *args.
def greet(greeting, *names):
for name in names:
print(f"{greeting}, {name}!")
greet("Hello", "Alice", "Bob", "Charlie")
>>> Hello, Alice!
>>> Hello, Bob!
>>> Hello, Charlie!
5. Arbitrary keyword arguments (**kwargs)
Arbitrary keyword arguments allow a function to accept any number of keyword arguments. The **kwargs parameter collects them into a dictionary.
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25, city="London")
>>> name: Alice
>>> age: 25
>>> city: London
You can access specific values by key or iterate over all key-value pairs.
def create_profile(**kwargs):
profile = {}
for key, value in kwargs.items():
profile[key] = value
return profile
user = create_profile(name="Alice", age=25, email="alice@example.com")
print(user)
>>> {'name': 'Alice', 'age': 25, 'email': 'alice@example.com'}
The name kwargs is a convention; you can use any name after the double asterisk.
def process_data(**options):
for key, value in options.items():
print(f"Setting {key} to {value}")
process_data(debug=True, verbose=False, timeout=30)
>>> Setting debug to True
>>> Setting verbose to False
>>> Setting timeout to 30
You can combine **kwargs with regular parameters. Regular parameters must come before **kwargs.
def create_user(username, **user_info):
user = {"username": username}
user.update(user_info)
return user
user = create_user("alice", email="alice@example.com", age=25)
print(user)
>>> {'username': 'alice', 'email': 'alice@example.com', 'age': 25}
Combining argument types
You can combine different argument types, but they must follow a specific order: positional arguments, *args, keyword arguments, default arguments, and **kwargs.
def complex_function(required, *args, keyword="default", **kwargs):
print(f"Required: {required}")
print(f"Args: {args}")
print(f"Keyword: {keyword}")
print(f"Kwargs: {kwargs}")
complex_function(1, 2, 3, keyword="custom", extra="value")
>>> Required: 1
>>> Args: (2, 3)
>>> Keyword: custom
>>> Kwargs: {'extra': 'value'}
The correct order for function parameters is:
- Positional parameters (required)
*args(arbitrary positional)- Keyword-only parameters (with or without defaults)
**kwargs(arbitrary keyword)
def flexible_function(pos1, pos2, *args, kw1="default1", kw2="default2", **kwargs):
result = {
"positional": [pos1, pos2],
"args": args,
"keywords": {"kw1": kw1, "kw2": kw2},
"extra": kwargs
}
return result
result = flexible_function(1, 2, 3, 4, kw1="custom", extra1="value1", extra2="value2")
print(result)
>>> {'positional': [1, 2], 'args': (3, 4), 'keywords': {'kw1': 'custom', 'kw2': 'default2'}, 'extra': {'extra1': 'value1', 'extra2': 'value2'}}
Here's a practical example combining multiple argument types:
def format_message(template, *values, prefix="", suffix="", **metadata):
message = template.format(*values)
if prefix:
message = f"{prefix} {message}"
if suffix:
message = f"{message} {suffix}"
if metadata:
metadata_str = ", ".join(f"{k}={v}" for k, v in metadata.items())
message = f"{message} [{metadata_str}]"
return message
result = format_message(
"Hello, {}!",
"Alice",
prefix="[INFO]",
suffix="(end)",
timestamp="2024-01-15",
level="info"
)
print(result)
>>> [INFO] Hello, Alice! (end) [timestamp=2024-01-15, level=info]