Ad – 728Γ—90
βš™οΈ Functions

Python Functions – The Complete Guide

Functions are the building blocks of organized, reusable code. Instead of copying the same logic in multiple places, you write it once as a function and call it wherever you need it. Python functions are defined with the def keyword and support features like default parameters, variable-length argument lists with *args and **kwargs, docstrings, and flexible return values. This lesson walks you through everything you need to write clean, professional Python functions.

⏱️ 30 min read 🎯 Beginner–Intermediate πŸ“… Updated 2026

Why Use Functions?

Imagine you need to greet 50 different users. Without functions you'd write the same greeting code 50 times. With a function you write it once and call it 50 times. Functions solve several real problems:

♻️

Reusability

Write logic once, call it anywhere in your program β€” or even from other programs by importing.

πŸ“¦

Organization

Break large programs into smaller, named chunks. Each function has one clear job.

πŸ›

Easier Debugging

When a bug exists in a function, you fix it in one place and every call to that function is fixed instantly.

πŸ“–

Readability

A well-named function like calculate_tax() communicates intent immediately without reading the implementation.

Defining a Function with def

Use the def keyword followed by the function name, parentheses, and a colon. The function body is indented.

Python
# Define a simple function
def greet():
    print("Hello, World!")

# Call (invoke) the function
greet()
greet()  # Can call it multiple times
greet()
β–Ά Output
Hello, World! Hello, World! Hello, World!
πŸ’‘
Define Before You Call

In Python, a function must be defined before it's called. If you call a function before its def appears in the file, you'll get a NameError. The common pattern is to define all functions at the top and call them at the bottom.

Parameters and Arguments

Parameters are the variable names listed in the function definition. Arguments are the actual values you pass when calling the function.

Python
# 'name' is a parameter
def greet(name):
    print(f"Hello, {name}!")

# "Alice" and "Bob" are arguments
greet("Alice")
greet("Bob")

# Function with multiple parameters
def describe_pet(name, species, age):
    print(f"{name} is a {age}-year-old {species}.")

describe_pet("Fluffy", "cat", 3)
describe_pet("Rex", "dog", 7)
β–Ά Output
Hello, Alice! Hello, Bob! Fluffy is a 3-year-old cat. Rex is a 7-year-old dog.

The return Statement

Functions can return a value back to the caller using return. Without a return statement (or with a bare return), Python returns None.

Python
def add(a, b):
    return a + b

result = add(10, 25)
print(result)       # 35
print(add(3, 7))    # Use return value directly

# Return multiple values (as a tuple)
def min_max(numbers):
    return min(numbers), max(numbers)

low, high = min_max([4, 1, 9, 2, 7])
print(f"Min: {low}, Max: {high}")

# Function with no return (returns None)
def say_hello():
    print("Hello!")

value = say_hello()
print(value)   # None
β–Ά Output
35 10 Min: 1, Max: 9 Hello! None
⚠️
return Exits the Function Immediately

As soon as Python hits a return statement, it exits the function. Any code after return in the same block is unreachable (dead code). Use early returns intentionally to handle edge cases at the top of a function.

Ad – 336Γ—280

Docstrings – Documenting Your Functions

A docstring is a string placed as the first statement inside a function body. It documents what the function does, its parameters, and what it returns. You access it via function.__doc__ or the built-in help().

Python
def calculate_area(length, width):
    """
    Calculate the area of a rectangle.

    Args:
        length (float): The length of the rectangle.
        width (float): The width of the rectangle.

    Returns:
        float: The area (length Γ— width).
    """
    return length * width

# Access the docstring
print(calculate_area.__doc__)

# Or use help()
help(calculate_area)
β–Ά Output
Calculate the area of a rectangle. Args: length (float): The length of the rectangle. width (float): The width of the rectangle. Returns: float: The area (length Γ— width).

Default Parameters

You can give parameters default values. If the caller doesn't provide that argument, the default is used. Parameters with defaults must come after parameters without defaults.

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

# Use all defaults
greet("Alice")                          # Hello, Alice!

# Override one default
greet("Bob", greeting="Hi")             # Hi, Bob!

# Override all defaults
greet("Carol", "Good morning", ".")     # Good morning, Carol.

# More practical example
def create_user(username, role="viewer", active=True):
    return {
        "username": username,
        "role": role,
        "active": active
    }

admin = create_user("admin", role="admin")
guest = create_user("guest123")
print(admin)
print(guest)
β–Ά Output
Hello, Alice! Hi, Bob! Good morning, Carol. {'username': 'admin', 'role': 'admin', 'active': True} {'username': 'guest123', 'role': 'viewer', 'active': True}
🚨
Mutable Default Arguments β€” Classic Python Trap!

Never use a mutable object (list, dict) as a default argument. The default is created once when the function is defined, not on each call. Use None as the default and create the mutable object inside the function instead:

# BAD - shares the same list across all calls!
def add_item(item, items=[]):
    items.append(item)
    return items

# GOOD - create a fresh list each call
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

*args – Variable Positional Arguments

When you prefix a parameter with *, it collects any extra positional arguments into a tuple. The name args is a convention β€” the * is what matters.

Python
def add_all(*args):
    """Add any number of numbers together."""
    print(f"Arguments received: {args}")  # it's a tuple
    return sum(args)

print(add_all(1, 2))          # 3
print(add_all(1, 2, 3, 4))    # 10
print(add_all(10, 20, 30, 40, 50))  # 150

# Mixing regular and *args
def introduce(greeting, *names):
    for name in names:
        print(f"{greeting}, {name}!")

introduce("Hello", "Alice", "Bob", "Carol")
β–Ά Output
Arguments received: (1, 2) Arguments received: (1, 2, 3, 4) Arguments received: (10, 20, 30, 40, 50) 3 10 150 Hello, Alice! Hello, Bob! Hello, Carol!

**kwargs – Variable Keyword Arguments

When you prefix a parameter with **, it collects any extra keyword arguments into a dictionary. The name kwargs is a convention.

Python
def print_info(**kwargs):
    """Print any key-value information."""
    print(f"Info received: {kwargs}")  # it's a dict
    for key, value in kwargs.items():
        print(f"  {key}: {value}")

print_info(name="Alice", age=30, city="London")
print()

# Practical use: building flexible configuration
def create_profile(username, **options):
    profile = {"username": username}
    profile.update(options)
    return profile

p1 = create_profile("alice", theme="dark", language="en")
p2 = create_profile("bob", premium=True, notifications=False, timezone="UTC")
print(p1)
print(p2)
β–Ά Output
Info received: {'name': 'Alice', 'age': 30, 'city': 'London'} name: Alice age: 30 city: London {'username': 'alice', 'theme': 'dark', 'language': 'en'} {'username': 'bob', 'premium': True, 'notifications': False, 'timezone': 'UTC'}

Combining All Parameter Types

You can combine all parameter types in one function. The order must be: regular parameters β†’ *args β†’ keyword-only parameters β†’ **kwargs.

Python
def full_example(required, default="ok", *args, keyword_only="yes", **kwargs):
    print(f"required:      {required}")
    print(f"default:       {default}")
    print(f"args:          {args}")
    print(f"keyword_only:  {keyword_only}")
    print(f"kwargs:        {kwargs}")

full_example(
    "must",
    "custom",
    "extra1", "extra2",
    keyword_only="changed",
    x=1, y=2
)
β–Ά Output
required: must default: custom args: ('extra1', 'extra2') keyword_only: changed kwargs: {'x': 1, 'y': 2}

Complete Practical Example

Python
def calculate_invoice(customer, *items, discount=0, tax_rate=0.08):
    """
    Calculate a customer invoice total.

    Args:
        customer (str): Customer name.
        *items: Variable number of (item_name, price) tuples.
        discount (float): Discount as a decimal (e.g. 0.1 for 10%).
        tax_rate (float): Tax rate as a decimal. Default 8%.

    Returns:
        dict: Invoice breakdown.
    """
    subtotal = sum(price for _, price in items)
    discount_amount = subtotal * discount
    taxable = subtotal - discount_amount
    tax = taxable * tax_rate
    total = taxable + tax

    return {
        "customer": customer,
        "items": list(items),
        "subtotal": round(subtotal, 2),
        "discount": round(discount_amount, 2),
        "tax": round(tax, 2),
        "total": round(total, 2)
    }

invoice = calculate_invoice(
    "Alice Corp",
    ("Website Design", 1200.00),
    ("Logo Design", 350.00),
    ("Hosting Setup", 150.00),
    discount=0.10,
    tax_rate=0.07
)

for key, value in invoice.items():
    print(f"  {key:<12}: {value}")
β–Ά Output
customer : Alice Corp items : [('Website Design', 1200.0), ('Logo Design', 350.0), ('Hosting Setup', 150.0)] subtotal : 1700.0 discount : 170.0 tax : 107.1 total : 1637.1

πŸ‹οΈ Practical Exercise

  1. Write a function celsius_to_fahrenheit(celsius) that converts temperature and returns the result.
  2. Write a function is_even(n) that returns True if n is even, False otherwise.
  3. Write a function repeat_string(text, times=2) that returns the string repeated times times. Default to 2.
  4. Write a function sum_all(*numbers) that accepts any number of arguments and returns their sum.
  5. Write a function build_tag(**attributes) that accepts keyword arguments and returns an HTML-like string, e.g. build_tag(class_="btn", id="submit") β†’ 'class_="btn" id="submit"'.

πŸ”₯ Challenge

Build a complete grade calculator program using functions. Create a function letter_grade(score) that returns "A", "B", "C", "D", or "F". Create a function class_report(*students) where each student is a tuple of (name, score). The function should print a formatted table with each student's name, score, and letter grade, plus overall class average. Use docstrings on both functions.

Interview Questions on Python Functions

  • What is the difference between a parameter and an argument?
  • What does a function return when there is no explicit return statement?
  • What is a docstring and how do you access it programmatically?
  • What is the danger of using a mutable default argument like a list? How do you fix it?
  • Explain the difference between *args and **kwargs. What types do they produce inside the function?
  • Can a function return multiple values? How does Python handle this?
  • What is the correct order of parameter types in a Python function signature?
  • What is the difference between a keyword argument and a positional argument when calling a function?

πŸ“‹ Summary

  • Define functions with def function_name(parameters):; the body is indented.
  • Call a function by writing its name followed by parentheses and any arguments.
  • Docstrings (triple-quoted strings as the first statement) document what a function does.
  • Default parameters (def f(x=10)) make arguments optional β€” put them after required parameters.
  • Never use mutable objects (lists, dicts) as default argument values β€” use None instead.
  • *args collects extra positional arguments into a tuple.
  • **kwargs collects extra keyword arguments into a dict.
  • The return statement exits the function and optionally passes a value back; without it, None is returned.
  • Parameter order: required β†’ defaults β†’ *args β†’ keyword-only β†’ **kwargs.

Frequently Asked Questions

Can Python functions be passed as arguments to other functions? +

Yes! Functions are first-class objects in Python, meaning they can be assigned to variables, stored in lists, and passed as arguments. This is the basis for higher-order functions like map(), filter(), and sorted(key=...).

What is the difference between print() and return? +

print() displays a value to the screen β€” it doesn't give the value to the caller. return passes the value back to whoever called the function, where it can be stored in a variable or used in an expression. A function that only prints is not reusable in calculations.

Can a function call itself? +

Yes β€” this is called recursion. A function that calls itself must have a base case (a condition where it stops calling itself) to avoid infinite recursion and a RecursionError. Recursion is covered in its own lesson.

How many values can a Python function return? +

Technically one β€” but that value can be a tuple containing multiple items. When you write return a, b, c, Python packs those into a tuple (a, b, c). The caller can unpack it with x, y, z = my_function().

What is a keyword-only argument? +

A keyword-only argument is a parameter that can only be passed by name, not by position. You define them after a bare * or after *args: def f(a, *, b):. Calling f(1, 2) raises a TypeError; you must write f(1, b=2). They make function calls more explicit and self-documenting.