🎯 Default Arguments

🚀 Open Notebook

Open In Colab Open In Kaggle

📺 Video Tutorial

Watch on YouTube

Python default arguments are awesome! 👍 (6:15)

What You’ll Learn

In this chapter, you’ll learn about default arguments - function parameters that have preset values. You’ll understand how to make functions more flexible by providing default values, when arguments can be omitted, and the correct order for different parameter types to create user-friendly, versatile functions.

Learning Objectives

  • Define functions with default parameter values

  • Understand when and why to use default arguments

  • Master the correct order of parameter types (positional, default, keyword, *args/**kwargs)

  • Create flexible functions that work with varying numbers of arguments

  • Override default values when needed

  • Apply best practices for choosing appropriate default values

Concept Explanation

What are Default Arguments?

Default arguments are parameters that have a pre-assigned value. When calling a function, if you don’t provide a value for that parameter, it uses the default instead.

def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}!")

greet("Alice")              # Uses default: "Hello, Alice!"
greet("Bob", "Hi")          # Overrides default: "Hi, Bob!"
greet("Charlie", "Hey")     # Overrides default: "Hey, Charlie!"

Benefits:

  • Makes functions more flexible

  • Reduces number of required arguments

  • Provides sensible defaults for common cases

  • Simplifies function calls

Syntax for Default Arguments

Default values are assigned in the function definition using =:

def function_name(required_param, optional_param=default_value):
    # function body
    pass

Example with multiple defaults:

def create_user(username, role="user", active=True, notifications=True):
    print(f"Username: {username}")
    print(f"Role: {role}")
    print(f"Active: {active}")
    print(f"Notifications: {notifications}")

# All defaults used
create_user("alice123")

# Some defaults overridden
create_user("admin_bob", role="admin")

# Multiple defaults overridden
create_user("charlie", role="moderator", notifications=False)

Parameter Order Rules (CRITICAL!)

Python enforces a specific order for different parameter types:

def function(
    positional,          # 1. Required positional (no default)
    default_param=value, # 2. Default arguments (optional)
    *args,               # 3. Variable positional arguments
    **kwargs             # 4. Variable keyword arguments
):
    pass

You CANNOT put a required parameter after a default parameter:

# WRONG - SyntaxError!
def bad_function(name="Anonymous", age):
    pass

# CORRECT
def good_function(age, name="Anonymous"):
    pass

When to Use Default Arguments

1. Common Default Values

When a parameter usually has the same value:

def connect_database(host, port=5432, timeout=30):
    # PostgreSQL default port is 5432
    # 30 seconds is reasonable timeout
    pass

# Most calls use defaults
connect_database("localhost")

# Occasionally override
connect_database("prod-server", port=3306)  # MySQL

2. Optional Configuration

For optional features or settings:

def send_email(to, subject, body, cc=None, bcc=None, priority="normal"):
    # cc and bcc are optional
    # priority has sensible default
    pass

send_email("user@example.com", "Hello", "Message body")
send_email("user@example.com", "Urgent", "Important!", priority="high")

3. Backward Compatibility

Adding new parameters without breaking existing code:

# Original function
def process_data(data):
    # process data
    pass

# Enhanced with new optional parameter
def process_data(data, format="json"):
    # Can now handle different formats
    # Old code still works: process_data(my_data)
    pass

Mutable Default Arguments (DANGER ZONE!)

Never use mutable objects (lists, dicts) as defaults:

# WRONG - This is a common bug!
def add_item(item, shopping_list=[]):
    shopping_list.append(item)
    return shopping_list

# Unexpected behavior!
list1 = add_item("apple")   # ['apple']
list2 = add_item("banana")  # ['apple', 'banana'] - WRONG!
# The same list is reused!

# CORRECT - Use None and create new list
def add_item(item, shopping_list=None):
    if shopping_list is None:
        shopping_list = []
    shopping_list.append(item)
    return shopping_list

# Now works correctly
list1 = add_item("apple")   # ['apple']
list2 = add_item("banana")  # ['banana'] - CORRECT!

Why? Default values are created once when the function is defined, not each time it’s called. Mutable defaults persist across calls.

Overriding Default Arguments

You can override defaults in two ways:

1. Positional (Order Matters)

def greet(name, title="Mr", greeting="Hello"):
    print(f"{greeting} {title} {name}")

# Override by position
greet("Smith", "Dr")  # Hello Dr Smith
greet("Smith", "Dr", "Good morning")  # Good morning Dr Smith

2. Keyword (Order Doesn’t Matter)

# Skip some defaults, specify others by name
greet("Smith", greeting="Hi")  # Hi Mr Smith
greet("Smith", greeting="Hey", title="Ms")  # Hey Ms Smith

Combining Default Arguments with Keyword Arguments

This combination creates very flexible functions:

def create_profile(username, email, bio="", location="Unknown", 
                   age=None, verified=False):
    profile = {
        "username": username,
        "email": email,
        "bio": bio,
        "location": location,
        "age": age,
        "verified": verified
    }
    return profile

# Minimal call - only required arguments
profile1 = create_profile("alice", "alice@example.com")

# Selective overrides using keyword arguments
profile2 = create_profile(
    "bob",
    "bob@example.com",
    bio="Python developer",
    verified=True
)

# Can skip middle parameters
profile3 = create_profile(
    "charlie",
    "charlie@example.com",
    location="New York",
    age=25
)

Examples

Example 1: Simple Default Argument

def power(base, exponent=2):
    """
    Raise base to the power of exponent.
    
    Parameters:
        base (float): The base number
        exponent (float): The exponent (default: 2 for square)
    
    Returns:
        float: base raised to exponent
    """
    return base ** exponent

# Use default (square)
print(power(5))      # 25 (5^2)
print(power(3))      # 9 (3^2)

# Override default
print(power(5, 3))   # 125 (5^3)
print(power(2, 10))  # 1024 (2^10)

Example 2: Multiple Default Arguments

def format_price(amount, currency="$", decimal_places=2, show_symbol=True):
    """
    Format a price with currency symbol.
    
    Parameters:
        amount (float): Price amount
        currency (str): Currency symbol (default: $)
        decimal_places (int): Number of decimals (default: 2)
        show_symbol (bool): Whether to show currency symbol (default: True)
    
    Returns:
        str: Formatted price string
    """
    formatted = f"{amount:.{decimal_places}f}"
    
    if show_symbol:
        return f"{currency}{formatted}"
    return formatted

# All defaults
print(format_price(19.99))  # $19.99

# Override currency
print(format_price(19.99, "€"))  # €19.99

# Override decimal places
print(format_price(19.99, decimal_places=0))  # $20

# Override multiple
print(format_price(19.99, "£", 3))  # £19.990

# Use keyword arguments to skip some
print(format_price(19.99, show_symbol=False))  # 19.99

Example 3: Price Calculator with Tax and Discount

def calculate_price(base_price, tax_rate=0.08, discount=0, shipping=0):
    """
    Calculate final price with tax, discount, and shipping.
    
    Parameters:
        base_price (float): Original price
        tax_rate (float): Tax rate as decimal (default: 0.08 = 8%)
        discount (float): Discount amount in dollars (default: 0)
        shipping (float): Shipping cost (default: 0)
    
    Returns:
        float: Final price
    """
    # Calculate: base + tax - discount + shipping
    subtotal = base_price - discount
    tax = subtotal * tax_rate
    final_price = subtotal + tax + shipping
    
    return final_price

# Just base price (8% tax, no discount, no shipping)
print(f"${calculate_price(100):.2f}")  # $108.00

# With discount
print(f"${calculate_price(100, discount=20):.2f}")  # $86.40

# Different tax rate
print(f"${calculate_price(100, tax_rate=0.10):.2f}")  # $110.00

# Full customization
print(f"${calculate_price(100, tax_rate=0.05, discount=15, shipping=10):.2f}")
# $99.25

Example 4: Greeting Generator

def create_greeting(name, title="", greeting="Hello", punctuation="!"):
    """
    Create a personalized greeting.
    
    Parameters:
        name (str): Person's name
        title (str): Title (Mr, Ms, Dr, etc.) - optional
        greeting (str): Greeting word (default: Hello)
        punctuation (str): End punctuation (default: !)
    
    Returns:
        str: Formatted greeting
    """
    if title:
        return f"{greeting} {title} {name}{punctuation}"
    return f"{greeting} {name}{punctuation}"

# Basic greeting
print(create_greeting("Alice"))  # Hello Alice!

# With title
print(create_greeting("Smith", title="Dr"))  # Hello Dr Smith!

# Different greeting
print(create_greeting("Bob", greeting="Hi"))  # Hi Bob!

# Everything custom
print(create_greeting("Johnson", "Prof", "Good morning", "."))
# Good morning Prof Johnson.

Example 5: Email Sender Function

def send_email(to, subject, body, from_address="noreply@company.com",
               cc=None, bcc=None, priority="normal", html=False):
    """
    Send an email (simulated).
    
    Parameters:
        to (str): Recipient email
        subject (str): Email subject
        body (str): Email body
        from_address (str): Sender email (default: noreply@company.com)
        cc (str): CC address (default: None)
        bcc (str): BCC address (default: None)
        priority (str): Email priority (default: normal)
        html (bool): Whether body is HTML (default: False)
    """
    print(f"From: {from_address}")
    print(f"To: {to}")
    if cc:
        print(f"CC: {cc}")
    if bcc:
        print(f"BCC: {bcc}")
    print(f"Subject: {subject}")
    print(f"Priority: {priority}")
    print(f"Format: {'HTML' if html else 'Plain Text'}")
    print(f"Body: {body}")
    print("-" * 50)

# Simple email
send_email("user@example.com", "Test", "This is a test")

# With CC and high priority
send_email(
    "user@example.com",
    "Urgent: Server Down",
    "The production server is down!",
    cc="team@example.com",
    priority="high"
)

# HTML email with custom sender
send_email(
    "customer@example.com",
    "Welcome!",
    "<h1>Welcome to our service!</h1>",
    from_address="welcome@company.com",
    html=True
)

Example 6: Database Connection Function

def connect_database(host, database, username="root", 
                     password="", port=3306, timeout=30):
    """
    Connect to a database (simulated).
    
    Parameters:
        host (str): Database host
        database (str): Database name
        username (str): Username (default: root)
        password (str): Password (default: empty)
        port (int): Port number (default: 3306 for MySQL)
        timeout (int): Connection timeout in seconds (default: 30)
    
    Returns:
        dict: Connection details
    """
    connection = {
        "host": host,
        "database": database,
        "username": username,
        "port": port,
        "timeout": timeout,
        "connected": True
    }
    
    print(f"Connected to {database} at {host}:{port}")
    print(f"User: {username}, Timeout: {timeout}s")
    
    return connection

# Minimal connection (use most defaults)
conn1 = connect_database("localhost", "myapp")

# Custom username and password
conn2 = connect_database(
    "prod-server.com",
    "production_db",
    username="admin",
    password="secure123"
)

# PostgreSQL (different port)
conn3 = connect_database(
    "localhost",
    "postgres_db",
    port=5432,
    timeout=60
)

Example 7: Shopping Cart with Default Tax

def add_to_cart(item_name, price, quantity=1, tax_rate=0.08, 
                gift_wrap=False, express_shipping=False):
    """
    Add item to shopping cart with calculated total.
    
    Parameters:
        item_name (str): Name of item
        price (float): Price per unit
        quantity (int): Number of items (default: 1)
        tax_rate (float): Tax rate (default: 0.08 = 8%)
        gift_wrap (bool): Add gift wrapping (default: False)
        express_shipping (bool): Use express shipping (default: False)
    
    Returns:
        dict: Cart item details
    """
    subtotal = price * quantity
    
    # Add-on fees
    gift_wrap_fee = 5.00 if gift_wrap else 0
    shipping_fee = 15.00 if express_shipping else 5.00
    
    # Calculate tax on subtotal + gift wrap
    taxable_amount = subtotal + gift_wrap_fee
    tax = taxable_amount * tax_rate
    
    total = subtotal + tax + gift_wrap_fee + shipping_fee
    
    cart_item = {
        "item": item_name,
        "unit_price": price,
        "quantity": quantity,
        "subtotal": subtotal,
        "tax": tax,
        "gift_wrap": gift_wrap_fee,
        "shipping": shipping_fee,
        "total": total
    }
    
    # Display
    print(f"\n{'='*40}")
    print(f"Item: {item_name}")
    print(f"Price: ${price:.2f} x {quantity} = ${subtotal:.2f}")
    print(f"Tax ({tax_rate*100}%): ${tax:.2f}")
    if gift_wrap:
        print(f"Gift Wrap: ${gift_wrap_fee:.2f}")
    print(f"Shipping: ${shipping_fee:.2f}")
    print(f"{'='*40}")
    print(f"TOTAL: ${total:.2f}")
    
    return cart_item

# Simple purchase
add_to_cart("Laptop", 999.99)

# Multiple quantities
add_to_cart("Mouse", 29.99, quantity=2)

# With gift wrap and express shipping
add_to_cart("Headphones", 149.99, gift_wrap=True, express_shipping=True)

# Different tax rate (e.g., different state)
add_to_cart("Keyboard", 79.99, tax_rate=0.10)

Practice Exercises

Beginner Level

  1. Simple Greeter: Create a function greet(name, greeting="Hello") that greets a person with a customizable greeting.

  2. Rectangle Area: Write calculate_area(length, width=1) that calculates rectangle area, defaulting to a square if width is omitted.

  3. Repeater: Create repeat_string(text, times=3) that repeats a string a specified number of times (default 3).

  4. Discount Calculator: Write apply_discount(price, discount_percent=10) that applies a discount with 10% as default.

  5. Coffee Order: Create order_coffee(size="medium", milk=True, sugar=True) that prints a coffee order with defaults.

Intermediate Level

  1. Email Validator: Write a function that validates an email, with optional strict mode parameter (default False).

  2. Date Formatter: Create a function that formats dates with customizable separators (default: “/”) and order (default: “MDY”).

  3. Password Generator: Write a function that generates passwords with customizable length (default: 12) and character types.

  4. Shipping Calculator: Create a function that calculates shipping cost with weight, distance, and optional express shipping (default: False).

  5. Report Generator: Write a function that creates reports with title, data, optional footer (default: “End of Report”), and format (default: “text”).

Advanced Level

  1. API Request Simulator: Create a function that simulates API requests with method (default: “GET”), headers (default: {}), timeout (default: 30), retry (default: 3).

  2. Logger Function: Write a comprehensive logging function with log level (default: “INFO”), timestamp (default: True), file output (default: None).

  3. Cache Manager: Create a function with TTL (time to live) default, max size default, and eviction policy default.

  4. Query Builder: Write a function that builds database queries with optional WHERE clauses, ORDER BY (default: None), and LIMIT (default: None).

  5. Configuration Merger: Create a function that merges configurations with defaults, handling nested dictionaries and overrides.

Common Mistakes to Avoid

Mistake 1: Mutable Default Arguments

Wrong:

def add_student(name, class_list=[]):
    class_list.append(name)
    return class_list

class1 = add_student("Alice")  # ['Alice']
class2 = add_student("Bob")    # ['Alice', 'Bob'] - WRONG!
# Same list is reused!

Correct:

def add_student(name, class_list=None):
    if class_list is None:
        class_list = []
    class_list.append(name)
    return class_list

class1 = add_student("Alice")  # ['Alice']
class2 = add_student("Bob")    # ['Bob'] - CORRECT!

Why: Mutable defaults are created once and shared across all function calls. Always use None as default and create the mutable object inside the function.

Mistake 2: Wrong Parameter Order

Wrong:

# SyntaxError: non-default argument follows default argument
def create_user(role="user", username, email):
    pass

Correct:

def create_user(username, email, role="user"):
    pass

Why: Required parameters (no default) must come before optional parameters (with default). Python enforces this rule.

Mistake 3: Confusing Default Values with Function Calls

Wrong:

import datetime

# This evaluates ONCE when function is defined!
def log_message(message, timestamp=datetime.datetime.now()):
    print(f"[{timestamp}] {message}")

log_message("First")   # 2024-01-10 10:00:00
time.sleep(5)
log_message("Second")  # 2024-01-10 10:00:00 - SAME TIME!

Correct:

def log_message(message, timestamp=None):
    if timestamp is None:
        timestamp = datetime.datetime.now()
    print(f"[{timestamp}] {message}")

log_message("First")   # 2024-01-10 10:00:00
time.sleep(5)
log_message("Second")  # 2024-01-10 10:00:05 - CORRECT!

Why: Default values are evaluated once at function definition time, not each time the function is called.

Mistake 4: Overriding Defaults Positionally When Order Changes

Wrong:

def send_notification(user, message, email=True, sms=False, push=False):
    pass

# Want to enable only push, but this is confusing:
send_notification("Alice", "Hello", False, False, True)
# Hard to read - what do these booleans mean?

Correct:

# Use keyword arguments for clarity
send_notification("Alice", "Hello", push=True)
# or
send_notification("Alice", "Hello", email=False, push=True)

Why: Keyword arguments are clearer and more maintainable, especially with boolean flags.

Real-World Applications

1. API Design

REST APIs use default parameters for pagination, filtering, and sorting:

def get_users(page=1, per_page=20, sort_by="created_at", 
              order="desc", active_only=True):
    # Fetch users from database with sensible defaults
    pass

2. Configuration Management

Applications use defaults for configuration settings:

def initialize_app(debug=False, port=5000, host="localhost",
                   database_url="sqlite:///app.db"):
    # Start app with production-safe defaults
    pass

3. Data Processing

Data pipelines have default parameters for common operations:

def clean_data(data, remove_nulls=True, normalize=True,
               encoding="utf-8", handle_duplicates="remove"):
    # Process data with standard cleaning options
    pass

4. Machine Learning

ML models use default hyperparameters:

def train_model(data, learning_rate=0.001, epochs=100,
                batch_size=32, optimizer="adam"):
    # Train with commonly used defaults
    pass

Challenge Projects

1. Flexible Calculator

Create a calculator with default operation.

Requirements:

  • calculate(a, b, operation="+") function

  • Support +, -, *, /, **, %

  • Optional rounding parameter (default: None)

  • Optional absolute value parameter (default: False)

  • Comprehensive error handling

2. Blog Post Generator

Create a function that generates blog posts with many defaults.

Requirements:

  • Required: title, content

  • Defaults: author=“Anonymous”, published=True, category=“General”

  • Optional: tags=None, featured_image=None, comments_enabled=True

  • Generate slug from title

  • Add timestamp

  • Return formatted post dictionary

3. Invoice Generator

Build an invoice generator with customizable defaults.

Requirements:

  • Required: items list, customer name

  • Defaults: tax_rate=0.08, discount=0, payment_terms=“Net 30”

  • Optional: notes=“”, due_date=None, invoice_number=auto-generated

  • Calculate subtotals, tax, total

  • Format and display professional invoice

4. File Backup System

Create a backup function with intelligent defaults.

Requirements:

  • Required: source_path

  • Defaults: destination=“./backups”, compress=True, overwrite=False

  • Optional: encryption=False, retention_days=30, include_hidden=False

  • Generate timestamp-based backup names

  • Handle different file types

  • Verification after backup

5. Smart Form Validator

Build a form validation system with default rules.

Requirements:

  • Validate multiple field types

  • Defaults: required=False, min_length=0, max_length=None

  • Email validator with default strict=False

  • Password validator with default complexity=“medium”

  • Custom error messages with defaults

  • Return validation results dictionary


🎓 Key Takeaways from Video

  1. Lists store multiple items in a single variable

  2. Define functions using the def keyword

  3. Import modules to use external code

  4. Use loops to repeat actions

💡 These points cover the main concepts from the video tutorial to help reinforce your learning.