Skip to main content

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.

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.

Example 1
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.

Example 2
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.

Example 3
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.

Example 4
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.

Example 5
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.

Example 6
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.

Example 7
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.

Example 8
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.

Example 9
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.

Example 10
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.

Example 11
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.

Example 12
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.

Example 13
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.

Example 14
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.

Example 15
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.

Example 16
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.

Example 17
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.

Example 18
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:

  1. Positional parameters (required)
  2. *args (arbitrary positional)
  3. Keyword-only parameters (with or without defaults)
  4. **kwargs (arbitrary keyword)
Example 19
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:

Example 20
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]

See also