Learn Without Walls
← Previous Lesson Lesson 3 of 4 Next Lesson →

Lesson 9.3: Default and Keyword Arguments

What You'll Learn

1. Default Parameter Values

You can give parameters a default value so they become optional when calling the function:

def greet(name, greeting="Hello"):
    """Greet someone with a customizable greeting."""
    print(f"{greeting}, {name}!")

# Using the default greeting
greet("Alice")

# Overriding the default
greet("Bob", "Hi")
greet("Carol", "Good morning")
Hello, Alice! Hi, Bob! Good morning, Carol!
Rule: Parameters with default values must come after parameters without defaults. def func(a=1, b) is a syntax error, but def func(a, b=1) is fine.
def power(base, exponent=2):
    """Raise base to exponent. Defaults to squaring."""
    return base ** exponent

print(power(5))       # 5^2 = 25
print(power(5, 3))    # 5^3 = 125
print(power(2, 10))   # 2^10 = 1024
25 125 1024

2. Keyword Arguments

Instead of relying on position, you can name the arguments when calling a function:

def describe_pet(animal, name, age):
    """Describe a pet."""
    print(f"{name} is a {age}-year-old {animal}.")

# Positional (order matters)
describe_pet("dog", "Rex", 5)

# Keyword (order doesn't matter)
describe_pet(name="Whiskers", age=3, animal="cat")

# Mix: positional first, then keyword
describe_pet("bird", name="Tweety", age=2)
Rex is a 5-year-old dog. Whiskers is a 3-year-old cat. Tweety is a 2-year-old bird.
Keyword Arguments: Arguments passed using the name=value syntax. They make function calls clearer and allow you to pass arguments in any order.
Rule: Positional arguments must come before keyword arguments in a function call. func(name="A", "dog") is an error.

3. Combining Defaults and Keywords

Default values and keyword arguments work together to create flexible functions:

def create_profile(name, age, city="Unknown", hobby="Unknown"):
    """Create a profile string."""
    return f"{name}, {age}, from {city}, enjoys {hobby}"

# All defaults
print(create_profile("Alice", 25))

# Override just one default
print(create_profile("Bob", 30, city="LA"))

# Override a later default without changing earlier ones
print(create_profile("Carol", 28, hobby="painting"))

# Override all defaults
print(create_profile("Dave", 35, "NYC", "chess"))
Alice, 25, from Unknown, enjoys Unknown Bob, 30, from LA, enjoys Unknown Carol, 28, from Unknown, enjoys painting Dave, 35, from NYC, enjoys chess

4. *args: Variable Positional Arguments

Sometimes you want a function that accepts any number of arguments. Use *args to collect extra positional arguments into a tuple:

def add_all(*numbers):
    """Add any number of values together."""
    print(f"Received: {numbers}")
    return sum(numbers)

print(add_all(1, 2))
print(add_all(1, 2, 3, 4, 5))
print(add_all(10))
Received: (1, 2) 3 Received: (1, 2, 3, 4, 5) 15 Received: (10,) 10
*args: A parameter prefixed with * that collects all extra positional arguments into a tuple. The name args is a convention; you could use any name like *numbers.
def print_scores(student, *scores):
    """Print a student's name and all their scores."""
    print(f"{student}'s scores:")
    for score in scores:
        print(f"  - {score}")

print_scores("Alice", 95, 87, 92)
print_scores("Bob", 78)
Alice's scores: - 95 - 87 - 92 Bob's scores: - 78

5. **kwargs: Variable Keyword Arguments

Similarly, **kwargs collects extra keyword arguments into a dictionary:

def build_profile(name, **details):
    """Build a user profile from keyword arguments."""
    profile = {"name": name}
    profile.update(details)
    return profile

result = build_profile(
    "Alice",
    age=25,
    city="LA",
    major="CS"
)
print(result)
{'name': 'Alice', 'age': 25, 'city': 'LA', 'major': 'CS'}
**kwargs: A parameter prefixed with ** that collects all extra keyword arguments into a dictionary. The name kwargs is a convention.

6. Putting It All Together

When combining different parameter types, they must appear in this order:

  1. Regular positional parameters
  2. Parameters with default values
  3. *args
  4. **kwargs
def example(required, optional="default", *args, **kwargs):
    print(f"required: {required}")
    print(f"optional: {optional}")
    print(f"args: {args}")
    print(f"kwargs: {kwargs}")

example("A", "B", "C", "D", x=1, y=2)
required: A optional: B args: ('C', 'D') kwargs: {'x': 1, 'y': 2}

Real-World Example: Flexible Print Function

def log_message(message, level="INFO", **metadata):
    """Log a message with optional metadata."""
    print(f"[{level}] {message}")
    for key, value in metadata.items():
        print(f"  {key}: {value}")

log_message("Server started")
log_message("Login failed", level="ERROR", user="alice", ip="192.168.1.1")
[INFO] Server started [ERROR] Login failed user: alice ip: 192.168.1.1

Check Your Understanding

Question: What does this code print?

def repeat(text, times=2):
    return text * times

print(repeat("ha"))
print(repeat("ho", times=3))

Answer:

haha — uses the default times=2

hohoho — overrides with times=3

Try It Yourself

  1. Write a function make_sandwich(bread, *fillings) that prints the bread type and all fillings
  2. Write a function calculate_price(amount, tax_rate=0.10, discount=0) that returns the final price
  3. Call calculate_price using keyword arguments to apply a 20% discount but keep the default tax rate

Key Takeaways

← Previous: Parameters & Return Next: Scope →