Post

09. Python Functions - Basics

🚀 Master the fundamentals of Python functions! This guide breaks down defining, calling, and documenting functions, empowering you to write cleaner, more reusable code. 💡

09. Python Functions - Basics

What we will learn in this post?

  • 👉 Introduction to Functions
  • 👉 Defining and Calling Functions
  • 👉 Function Parameters and Arguments
  • 👉 Return Statement
  • 👉 Default Parameters
  • 👉 Keyword Arguments
  • 👉 Docstrings and Function Documentation
  • 👉 Conclusion!

Functions: Your Code’s Building Blocks 🧱

Functions are like mini-programs within your bigger program. They’re reusable blocks of code that perform a specific task. Think of them as recipes – you can use the same recipe (function) multiple times to make the same dish (result) without rewriting the instructions. By mastering functions, you’ll write more efficient, maintainable, and professional Python code that scales with your projects.

Why Use Functions? 🤔

  • Code Reusability: Write once, use many times! This saves time and effort.
  • Modularity: Breaks down a complex program into smaller, manageable parts.
  • Organization: Makes your code easier to read, understand, and maintain.

The DRY Principle 🌵

Functions help you follow the DRY (Don’t Repeat Yourself) principle. If you find yourself writing the same code multiple times, put it in a function! This makes your code cleaner, easier to update, and less prone to errors. The DRY principle is a cornerstone of professional software development, reducing bugs and maintenance time significantly.

graph LR
    A["Code Duplication"]:::duplicationStyle --> B{"Is there repeated code?"}:::checkStyle
    B -- "Yes" --> C["Create a Function"]:::functionStyle
    B -- "No" --> D["Continue Coding"]:::continueStyle
    C --> E["Call the Function where needed"]:::callStyle
    E --> D

    classDef duplicationStyle fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef checkStyle fill:#ffd700,stroke:#d99120,color:#222,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef functionStyle fill:#6b5bff,stroke:#4a3f6b,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef continueStyle fill:#00bfae,stroke:#005f99,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef callStyle fill:#ff9800,stroke:#f57c00,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;

    class A duplicationStyle;
    class B checkStyle;
    class C functionStyle;
    class D continueStyle;
    class E callStyle;

    linkStyle default stroke:#e67e22,stroke-width:3px;

Functions are essential for writing efficient and maintainable code! 💪

Defining and Calling Functions in Python 🐍

Here’s a simple guide to understanding functions in Python! Defining and calling functions is the foundation of modular programming, allowing you to break complex problems into manageable, testable pieces.

Defining Functions with def

We use the def keyword to define a function. Function names should be descriptive and use snake_case (lowercase words separated by underscores). A well-named function acts as self-documenting code, making your intent clear to other developers (and your future self!).

1
2
3
def greet(name):
  """This function greets the person passed in as a parameter."""
  print(f"Hello, {name}!")

Calling Functions

To use (or “call”) a function, type its name followed by parentheses (), and any necessary arguments inside.

1
greet("Alice") # Output: Hello, Alice!

Examples

  • Simple addition:
1
2
3
4
5
6
7
def add_numbers(x, y):
  """Adds two numbers and returns the result."""
  sum = x + y
  return sum

result = add_numbers(5, 3)
print(result) # Output: 8
  • Checking if a number is even:
1
2
3
4
5
6
7
8
9
def is_even(number):
  """Checks if a number is even and returns True or False."""
  if number % 2 == 0:
    return True
  else:
    return False

print(is_even(4)) # Output: True
print(is_even(7)) # Output: False

Real-World Example: Temperature Converter 🌡️

Here’s a practical function you might use in a weather app or scientific application:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def celsius_to_fahrenheit(celsius):
    """
    Converts temperature from Celsius to Fahrenheit.
    Useful for weather applications and international data processing.
    """
    fahrenheit = (celsius * 9/5) + 32
    return fahrenheit

# Example usage
temp_c = 25
temp_f = celsius_to_fahrenheit(temp_c)
print(f"{temp_c}°C is {temp_f}°F") # Output: 25°C is 77.0°F

# Converting freezing and boiling points
print(f"Freezing point: {celsius_to_fahrenheit(0)}°F")  # Output: 32.0°F
print(f"Boiling point: {celsius_to_fahrenheit(100)}°F") # Output: 212.0°F
🚀 Try this Live → Click to open interactive PYTHON playground

Parameters vs. Arguments: A Simple Guide 🚀

Okay, let’s demystify parameters and arguments! Think of a parameter as a placeholder in a function’s definition, while an argument is the actual value you pass when you call the function. Understanding this distinction helps you communicate more precisely about your code and debug issues faster.

Positional Arguments 📍

Positional arguments are passed based on their order. The first argument you pass goes to the first parameter, and so on. This is the most common way to pass data to functions and is intuitive for functions with few parameters.

1
2
3
4
5
def greet(name, greeting): # name & greeting are parameters
  print(f"{greeting}, {name}!")

greet("Alice", "Hello") # "Alice" and "Hello" are arguments, passed positionally
# Output: Hello, Alice!

Passing Multiple Arguments ➕

You can pass as many arguments as there are parameters defined in the function. Here are examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Function with one parameter
def square(number):
  return number * number

print(square(5)) # Output: 25

# Function with two parameters
def add(x, y):
  return x + y

print(add(3, 7)) # Output: 10

# Function with three parameters
def describe_person(name, age, city):
  print(f"{name} is {age} years old and lives in {city}.")

describe_person("Bob", 30, "New York") # Output: Bob is 30 years old and lives in New York.

Real-World Example: Order Processing 📦

Here’s a practical example from e-commerce applications:

1
2
3
4
5
6
7
8
9
10
11
12
13
def calculate_order_total(item_price, quantity, tax_rate, shipping_cost):
    """
    Calculate the total cost of an order including tax and shipping.
    Common in e-commerce and point-of-sale systems.
    """
    subtotal = item_price * quantity
    tax = subtotal * tax_rate
    total = subtotal + tax + shipping_cost
    return total

# Example: Ordering 3 books at $15 each
order_total = calculate_order_total(15.00, 3, 0.08, 5.99)
print(f"Order total: ${order_total:.2f}") # Output: Order total: $54.59
🚀 Try this Live → Click to open interactive PYTHON playground
  • Parameters define what a function expects.
  • Arguments are the real data you provide.
  • Positional arguments rely on order.

Understanding the return Statement in Python ↩️

The return statement is Python’s way of sending results back from a function. Think of it like this: you ask a function to do something, and return is how it gives you the answer. Without return, your functions would just perform actions but never communicate results back to your program.

Sending Values Back 📦

  • When a function hits a return statement, it stops executing and sends the specified value back to where the function was called. This value can then be stored in a variable, used in calculations, or passed to other functions.
1
2
3
4
5
6
def add_numbers(x, y):
  sum = x + y
  return sum

result = add_numbers(5, 3)
print(result) # Output: 8

Returning Multiple Values Using Tuples 👯

  • You can return multiple values by packing them into a tuple.
1
2
3
4
5
6
7
8
9
10
11
def get_name_and_age():
  name = "Alice"
  age = 30
  return name, age  # Returns a tuple

person_info = get_name_and_age()
print(person_info) # Output: ('Alice', 30)

name, age = get_name_and_age() #You can also unpack it directly
print(name) #Output: Alice
print(age) #Output: 30

Functions Without return (Returning None) 👻

  • If a function doesn’t have a return statement, or if it has return without a value, it implicitly returns None. This means the function does something, but doesn’t give back a specific answer.
1
2
3
4
5
def greet(name):
  print(f"Hello, {name}!")

result = greet("Bob") #Output: Hello, Bob!
print(result) # Output: None

Real-World Example: User Authentication 🔐

Here’s a practical example of returning multiple values for authentication systems:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def validate_login(username, password):
    """
    Validates user credentials and returns status and message.
    Common in web applications and security systems.
    """
    # Simulated database check
    valid_users = {"alice": "pass123", "bob": "secret456"}

    if username in valid_users and valid_users[username] == password:
        return True, "Login successful", username
    elif username in valid_users:
        return False, "Incorrect password", None
    else:
        return False, "User not found", None

# Example usage
success, message, user = validate_login("alice", "pass123")
print(f"Success: {success}, Message: {message}")
# Output: Success: True, Message: Login successful

success, message, user = validate_login("alice", "wrong")
print(f"Success: {success}, Message: {message}")
# Output: Success: False, Message: Incorrect password
🚀 Try this Live → Click to open interactive PYTHON playground

Resources:

graph LR
    CALLER["Function Caller"]:::callerStyle --> CALL["Call Function"]:::callStyle
    CALL --> EXEC["Execute Function Body"]:::execStyle
    EXEC --> RETURN{"return statement?"}:::checkStyle
    RETURN -- "Yes" --> SEND["Send Value Back"]:::returnStyle
    RETURN -- "No" --> NONE["Return None"]:::noneStyle
    SEND --> USE["Use Returned Value"]:::useStyle
    NONE --> USE

    classDef callerStyle fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef callStyle fill:#6b5bff,stroke:#4a3f6b,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef execStyle fill:#ffd700,stroke:#d99120,color:#222,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef checkStyle fill:#ff9800,stroke:#f57c00,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef returnStyle fill:#00bfae,stroke:#005f99,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef noneStyle fill:#9e9e9e,stroke:#616161,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef useStyle fill:#43e97b,stroke:#38f9d7,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;

    class CALLER callerStyle;
    class CALL callStyle;
    class EXEC execStyle;
    class RETURN checkStyle;
    class SEND returnStyle;
    class NONE noneStyle;
    class USE useStyle;

    linkStyle default stroke:#e67e22,stroke-width:3px;

Sequence diagram 📦

%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#ff4f81','primaryTextColor':'#fff','primaryBorderColor':'#c43e3e','lineColor':'#e67e22','secondaryColor':'#6b5bff','tertiaryColor':'#ffd700','noteBkgColor':'#00bfae','noteTextColor':'#fff'}}}%%
sequenceDiagram
    participant Caller as 🎯 Caller
    participant Function as ⚙️ Function

    Note over Caller,Function: Function Execution Flow

    Caller->>+Function: Call the function with arguments
    activate Function
    Note right of Function: Processing...
    Function->>Function: Execute function body

    alt Has return statement
        Function-->>-Caller: Return value
        Note left of Caller: Value received ✅
    else No return statement
        Function-->>-Caller: Return None
        Note left of Caller: None received 👻
    end

    Caller->>Caller: Use returned value
    Note over Caller: Continue execution

Default Parameter Values in Python ⚙️

👋 Hey there! Let’s make function parameters easier to understand. Default parameters are incredibly useful when building APIs, libraries, or any function that should work well with minimal configuration.

What are Default Parameters? 🤔

Default parameters are values that are automatically assigned to function parameters if no value is explicitly provided when the function is called. They make your functions more flexible and user-friendly by providing sensible defaults.

Syntax:

1
2
3
4
5
6
def greet(name="Guest", greeting="Hello"): # Default parameters
  print(f"{greeting}, {name}!")

greet() # Output: Hello, Guest!
greet("Alice") # Output: Hello, Alice!
greet("Bob", "Hi") # Output: Hi, Bob!

When to Use Them? 💡

Use default parameters when you want a parameter to have a common or sensible default value, making the function more flexible.

1
2
3
4
5
6
def calculate_tax(price, tax_rate=0.07): # Default tax_rate
  total = price * (1 + tax_rate)
  return total

print(calculate_tax(100)) # Output: 107.0
print(calculate_tax(100, 0.1)) # Output: 110.0

Order Matters! ⚠️

Non-default parameters must come before default parameters in the function definition. Otherwise, you’ll get a SyntaxError.

1
2
3
4
5
6
7
8
#This is wrong:
#def incorrect_func(default=1, non_default): # SyntaxError: non-default argument follows default argument

#This is correct:
def correct_func(non_default, default=1): # Correct order
  print(non_default, default)

correct_func(5) #output: 5 1

Real-World Example: API Request Function 🌐

Here’s a practical example common in web development and API interactions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def make_api_request(endpoint, method="GET", timeout=30, retries=3):
    """
    Makes an HTTP API request with configurable options.
    Default values handle the most common use cases.
    """
    print(f"Making {method} request to {endpoint}")
    print(f"Timeout: {timeout}s, Retries: {retries}")
    # Simulated API call logic here
    return f"Response from {endpoint}"

# Most common case - just specify the endpoint
response1 = make_api_request("/api/users")
# Output: Making GET request to /api/users
#         Timeout: 30s, Retries: 3

# Override specific parameters as needed
response2 = make_api_request("/api/data", method="POST", timeout=60)
# Output: Making POST request to /api/data
#         Timeout: 60s, Retries: 3

# Full customization when necessary
response3 = make_api_request("/api/upload", "PUT", 120, 5)
# Output: Making PUT request to /api/upload
#         Timeout: 120s, Retries: 5

Default parameters make your functions more robust and user-friendly.

Keyword Arguments in Python 🔑

Keyword arguments let you pass values to a function by explicitly naming the parameter. This unlocks a superpower: calling functions without rigidly sticking to the order defined in the function’s signature! This feature becomes essential when working with functions that have many parameters or when building self-documenting code.

What are Keyword Arguments?

Instead of relying solely on position, you specify parameter_name=value. It’s like writing name = "Alice" when calling the function. This makes function calls more readable and maintainable, especially in team environments.

1
2
3
4
5
6
7
8
def describe_person(name, age, city):
    print(f"Name: {name}, Age: {age}, City: {city}")

# Positional arguments (order matters)
describe_person("Bob", 30, "New York") # Output: Name: Bob, Age: 30, City: New York

# Keyword arguments (order doesn't matter)
describe_person(age=25, city="London", name="Eve") # Output: Name: Eve, Age: 25, City: London

Mixing Positional and Keyword Arguments 🤹

You can mix them, but positional arguments must come first.

1
2
describe_person("Charlie", age=40, city="Paris") # Output: Name: Charlie, Age: 40, City: Paris
# describe_person(age=40, "David", city="Berlin") # This would raise a SyntaxError!

Real-World Example: Email Configuration 📧

Here’s a practical example from email and notification systems:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def send_email(to, subject, body, cc=None, bcc=None, priority="normal",
               attach_files=False, html=False):
    """
    Send an email with various configuration options.
    Keyword arguments make it clear what each parameter does.
    """
    print(f"To: {to}")
    print(f"Subject: {subject}")
    print(f"Priority: {priority}")
    print(f"HTML Format: {html}")
    if cc:
        print(f"CC: {cc}")
    if attach_files:
        print("Attachments included")

# Simple email - only required parameters
send_email(
    to="user@example.com",
    subject="Welcome!",
    body="Thank you for signing up"
)

# Complex email with many options - keyword arguments make it clear
send_email(
    to="manager@company.com",
    subject="Q4 Report",
    body="Please find the attached report",
    cc="team@company.com",
    priority="high",
    attach_files=True,
    html=True
)

# Order doesn't matter with keyword arguments!
send_email(
    body="Meeting reminder",
    subject="Team Meeting Tomorrow",
    to="team@company.com",
    priority="normal"
)

Key benefits:

  • Readability: Makes code easier to understand at a glance.
  • 🔀 Flexibility: No need to remember the exact order of parameters.
  • 🧩 Default Values: Great when some parameters have default values (you only need to specify the ones you want to change).

Documenting Your Python Code with Docstrings 📝

Docstrings are like little notes you write for your functions (or classes/modules). They’re enclosed in triple quotes ("""Docstring goes here""" or '''Docstring goes here'''). They explain what your function does, what it expects as input, and what it returns. The help() function reads these docstrings and shows them to you! Well-written docstrings are essential for professional code, enabling auto-generated documentation and helping team members understand your code quickly.

How to Write a Good Docstring

  • First line: A concise summary of what the function does. Keep it short and sweet. This should be a complete sentence that describes the action performed.

  • Parameters: Describe each input parameter, including its type if necessary. This helps users understand what data to provide.
  • Returns: Explain what the function returns and its type. Clear return documentation prevents confusion and errors.
  • Raises: Mention any exceptions your function might raise. This helps users handle errors gracefully.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def add_numbers(x, y):
    """Add two numbers together.

    Args:
        x: The first number.
        y: The second number.

    Returns:
        The sum of x and y.
    """
    return x + y

help(add_numbers)
# Output:
# Help on function add_numbers in module __main__:
#
# add_numbers(x, y)
#     Add two numbers together.
#
#     Args:
#         x: The first number.
#         y: The second number.
#
#     Returns:
#         The sum of x and y.

Real-World Example: Data Processing Function 📊

Here’s a professional docstring example for a data processing function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def process_customer_data(data, normalize=True, remove_duplicates=True):
    """
    Process and clean customer data from various sources.

    This function performs standard data cleaning operations including
    normalization, duplicate removal, and validation. Commonly used in
    ETL pipelines and data analysis workflows.

    Args:
        data (list of dict): Raw customer data where each dict contains
            customer information with keys like 'name', 'email', 'phone'.
        normalize (bool, optional): Whether to normalize text fields
            (lowercase, trim whitespace). Defaults to True.
        remove_duplicates (bool, optional): Whether to remove duplicate
            entries based on email. Defaults to True.

    Returns:
        list of dict: Cleaned and processed customer data with the same
            structure as input but with applied transformations.

    Raises:
        ValueError: If data is empty or not in expected format.
        TypeError: If data is not a list of dictionaries.

    Example:
        >>> customers = [
        ...     {'name': 'John Doe', 'email': 'john@example.com'},
        ...     {'name': 'Jane Smith', 'email': 'jane@example.com'}
        ... ]
        >>> cleaned = process_customer_data(customers)
        >>> print(cleaned[0]['name'])
        'john doe'

    Note:
        This function modifies the input data structure. Pass a copy
        if you need to preserve the original data.
    """
    if not isinstance(data, list):
        raise TypeError("Data must be a list of dictionaries")
    if not data:
        raise ValueError("Data cannot be empty")

    processed = []
    seen_emails = set()

    for customer in data:
        # Skip duplicates if enabled
        if remove_duplicates and customer.get('email') in seen_emails:
            continue

        # Normalize if enabled
        if normalize:
            customer['name'] = customer['name'].lower().strip()
            customer['email'] = customer['email'].lower().strip()

        processed.append(customer)
        seen_emails.add(customer.get('email'))

    return processed

# Using help() to see the documentation
help(process_customer_data)
🚀 Try this Live → Click to open interactive PYTHON playground

Best Practices

  • Follow PEP 257. It provides official guidelines.
  • Be clear, concise, and accurate.
  • Use imperative mood (“Do this”, “Return that”).
  • Keep your docstrings up-to-date with your code.

Remember: Good documentation is like a friendly signpost, guiding others through your code!


🎯 Practice Project Assignment

💡 Project: Personal Finance Calculator (Click to expand)

Your Challenge:

Build a personal finance calculator that uses all function concepts (parameters, return values, default arguments, keyword arguments, and docstrings).

Requirements:

Part 1: Income Calculator

  • Create calculate_monthly_income(hourly_rate, hours_per_week)
  • Assume 4 weeks per month, return total monthly income

Part 2: Budget Tracker

  • Create calculate_remaining_budget(income, rent, utilities, groceries, transport, entertainment)
  • Calculate total expenses and return remaining budget

Part 3: Savings Goal

  • Create savings_plan(current_savings, monthly_contribution, goal_amount, interest_rate=0.02)
  • Return: months_needed, total_contributed, interest_earned, final_amount

Part 4: Tax Calculator

  • Create calculate_taxes(income, tax_rate=0.20, deductions=0, credits=0)
  • Return: gross_tax, net_tax, take_home_pay

Part 5: Comprehensive Report

  • Create generate_financial_report(**kwargs) with docstring
  • Use all previous functions to display formatted report

Bonus Challenges:

  • Add input validation (check for negative values)
  • Create a debt payoff calculator function
  • Add emergency fund recommendation
  • Calculate debt-to-income ratio

Example Output:

=== PERSONAL FINANCE REPORT ===
Monthly Income: $4,000.00
Total Expenses: $2,100.00
Remaining Budget: $1,900.00
Savings Goal: 19 months to reach $15,000
Take-Home Pay: $40,400.00

Share Your Solution! 💬

Completed the project? Post your code in the comments below! Show us your implementation! 🎨


Conclusion

So, there you have it! 🎉 I’m really curious to know what you think. Do you have any tips or tricks to share? Or maybe a different perspective? 🤔 Drop a comment in the section below! I’m excited to see what you have to say and learn from your experiences. 📚 Let’s connect! 😊

This post is licensed under CC BY 4.0 by the author.