Web Analytics

Function Arguments

Intermediate ~25 min read

Python offers incredible flexibility in how you pass data to functions. From simple positional arguments to powerful *args and **kwargs, understanding argument types lets you design functions that are both easy to use and incredibly versatile. Master these patterns and you'll write more Pythonic, flexible code!

Positional vs Keyword Arguments

When calling a function, you can pass arguments by position (where order matters) or by keyword (where you explicitly name the parameter). Keyword arguments make your code more readable and let you skip optional parameters.

Output
Click Run to execute your code
Parameters vs Arguments: A parameter is the variable in the function definition (def greet(name)). An argument is the value you pass when calling the function (greet("Alice")). Parameters define what a function expects; arguments are what you provide.

Default Parameter Values

Default values make parameters optional. If the caller doesn't provide an argument, the default is used. This pattern is essential for creating user-friendly APIs with sensible defaults that can be overridden when needed.

Output
Click Run to execute your code
Mutable Default Values Trap! Never use mutable objects (lists, dicts) as default values. They're created once when the function is defined, not each call. Use None instead: def func(items=None): if items is None: items = []

Arbitrary Arguments: *args and **kwargs

Sometimes you don't know in advance how many arguments a function will receive. *args collects extra positional arguments into a tuple, while **kwargs collects extra keyword arguments into a dictionary. These make your functions incredibly flexible.

Output
Click Run to execute your code
Parameter Order Rule: When combining parameter types, they must appear in this order: (1) regular positional, (2) *args, (3) keyword-only, (4) **kwargs. Example: def func(a, b, *args, option=True, **kwargs)

Practical Argument Patterns

Let's look at real-world patterns using these argument types - building flexible APIs, configuration functions, and wrapper functions that pass arguments through.

Output
Click Run to execute your code

Common Mistakes

1. Keyword arguments before positional

# Wrong - keyword args must come after positional
greet(name="Alice", "Smith")  # SyntaxError!

# Correct - positional first, then keyword
greet("Alice", last_name="Smith")

2. Using mutable default values

# Wrong - list is shared across calls!
def add_item(item, items=[]):
    items.append(item)
    return items

add_item("a")  # ['a']
add_item("b")  # ['a', 'b'] - unexpected!

# Correct - use None and create new list
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

3. Putting default parameters before required ones

# Wrong - default before non-default
def greet(greeting="Hello", name):  # SyntaxError!
    print(f"{greeting}, {name}")

# Correct - required parameters first
def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}")

4. Confusing *args unpacking

def show_args(*args):
    print(args)

# Wrong - passing a list creates tuple with one element
my_list = [1, 2, 3]
show_args(my_list)  # ([1, 2, 3],) - tuple containing the list

# Correct - unpack the list with *
show_args(*my_list)  # (1, 2, 3) - each element is separate

5. Forgetting that **kwargs is a dict copy

def modify_kwargs(**kwargs):
    kwargs['new_key'] = 'new_value'
    return kwargs

original = {'a': 1, 'b': 2}
result = modify_kwargs(**original)
print(result)   # {'a': 1, 'b': 2, 'new_key': 'new_value'}
print(original) # {'a': 1, 'b': 2} - original unchanged!

# **kwargs creates a new dict from the passed keyword arguments

Exercise: Flexible Print Function

Task: Create a flexible logging function.

Requirements:

  • Accept a message (required)
  • Accept a level with default "INFO"
  • Accept any additional context via **kwargs
  • Print formatted output like: [INFO] Message | key=value
Output
Click Run to execute your code
Show Solution
def log(message, level="INFO", **context):
    """Log a message with optional context."""
    output = f"[{level}] {message}"
    if context:
        extras = " | ".join(f"{k}={v}" for k, v in context.items())
        output += f" | {extras}"
    print(output)

# Test it
log("User logged in")
log("Payment failed", level="ERROR")
log("Order placed", user="alice", order_id=12345, amount=99.99)

Summary

  • Positional: func(a, b) - order matters
  • Keyword: func(a=1, b=2) - explicit names, order flexible
  • Default values: def func(a, b=10) - optional parameters
  • *args: def func(*args) - tuple of extra positional args
  • **kwargs: def func(**kwargs) - dict of extra keyword args
  • Unpacking: func(*list) or func(**dict)
  • Order: positional, *args, keyword-only, **kwargs
  • Never: Use mutable objects (list, dict) as defaults

What's Next?

Now that you know how to pass data into functions, let's learn about Return Values - how functions send data back to the caller, returning multiple values, and the difference between return and print!