Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

Define and Call Functions

Learn how to define and call your own functions in Python with a step-by-step tutorial, hands-on exercise, and troubleshooting tips.

Focus: define and call your own functions

Sponsored

You've been writing the same three lines of code over and over just to greet a user, calculate a discount, or validate input. It's tedious, error-prone, and makes your scripts grow longer without becoming smarter. That's the problem — and the solution is simple: define and call your own functions to package logic once and reuse it everywhere.

The problem this lesson solves

Without functions, every script is a flat sequence of instructions. If you want to greet ten users, you either copy-paste the same print statements or write a loop that still forces you to write the greeting logic inline. This leads to bloated code, hidden bugs when you edit one copy but forget another, and a painful process when you need to change how a greeting works — you'd have to hunt down every occurrence.

Functions solve this: you write the logic once, give it a name, and call it by name whenever you need it. Change one place, and every callsite benefits from the fix or improvement.

Core concept / mental model

Think of a function as a mini-program inside your program. It takes optional inputs (parameters), does something with them, and optionally returns a result.

Pro tip: A function is like a recipe. You define the steps once in a cookbook, and anyone can call the recipe by name to produce the dish. The recipe doesn't care who asks for it — it just follows the instructions.

Python functions live in memory after you define them (but before you call them, they're just inert code). Calling a function executes its body, then control returns to the line after the call.

Anatomy of a function definition

def function_name(parameter1, parameter2):
    """Optional docstring explaining what the function does."""
    # function body
    result = parameter1 + parameter2
    return result
  • def — keyword that starts a function definition.
  • function_name — follows the same rules as variable names (letters, underscores, no spaces).
  • (parameter1, parameter2) — comma-separated list of input variables. Can be empty.
  • : — colon marks the end of the header.
  • Indented block (4 spaces) — the function body.
  • return — optional keyword that sends a value back to the caller. If omitted, the function returns None.

How it works step by step

Step 1: Define the function

Python reads the def statement and creates a function object, binding the name to it. Nothing executes yet.

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

Step 2: Call the function

Write the function name followed by parentheses (). If the function expects arguments, provide them inside the parentheses.

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

Step 3: Execution flow

When Python reaches greet("Alice"): 1. It suspends execution of the current scope. 2. It assigns "Alice" to the parameter name inside the function's local scope. 3. It runs the function body — here it calls print(). 4. After the body finishes (or hits return), control goes back to the line after the call.

Step 4: Return values

Use the return keyword to send data back. The caller can capture it in a variable.

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

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

Common gotcha: If you forget return, the function still runs but returns None.

Hands-on walkthrough

Let's build a small utility library step by step.

Example 1: A simple converter

def celsius_to_fahrenheit(celsius):
    return (celsius * 9/5) + 32

# Call the function
f = celsius_to_fahrenheit(25)
print(f"25°C = {f:.1f}°F")  # Output: 25°C = 77.0°F

Example 2: Function with multiple parameters and default values

def create_profile(name, age, city="Unknown"):
    return f"{name}, {age} years old, from {city}"

print(create_profile("Alice", 30))
# Output: Alice, 30 years old, from Unknown

print(create_profile("Bob", 25, "New York"))
# Output: Bob, 25 years old, from New York

Named arguments let you skip order:

print(create_profile(age=22, name="Charlie", city="London"))
# Output: Charlie, 22 years old, from London

Example 3: Returning multiple values

Use a tuple (you can unpack it on the caller side):

def analyze_numbers(a, b):
    total = a + b
    product = a * b
    return total, product  # returns a tuple

sum_result, prod_result = analyze_numbers(4, 7)
print(f"Sum: {sum_result}, Product: {prod_result}")
# Output: Sum: 11, Product: 28

Example 4: Documenting your function

def calculate_bmi(weight_kg, height_m):
    """
    Calculate Body Mass Index.

    Parameters:
    weight_kg (float): Weight in kilograms.
    height_m (float): Height in meters.

    Returns:
    float: BMI value rounded to 1 decimal.
    """
    bmi = weight_kg / (height_m ** 2)
    return round(bmi, 1)

Calling help(calculate_bmi) prints that docstring.

Compare options / when to choose what

Technique Best For Returns Caution
Function with return Computing and returning a value The value Don't forget return
Function without return Performing an action (side effect) None Can't use result in expressions
Function with default params Common values that rarely change As defined Mutable default args cause bugs
Named arguments Functions with many optional params As defined Keyword order is irrelevant
Returning multiple values Needing more than one result from one computation Tuple Unpack with matching variable count

Pro tip: Use a function with return when you need the output for further calculations. Use a void function (no return) when the function's job is to print, save to a file, or modify a mutable object.

Troubleshooting & edge cases

Mistake 1: Defining a function but never calling it

def add(x, y):
    return x + y

# No output — the function is just defined, never executed

Fix: Add print(add(2,3)) or result = add(2,3) somewhere.

Mistake 2: Forgetting the colon or indentation

def greet(name)  # SyntaxError: expected ':'
print("Hello!")  # IndentationError: unexpected indent

Fix: Always end the def line with :, and ensure the body is indented exactly 4 spaces (or one tab).

Mistake 3: Using mutable default arguments

def append_to_list(item, my_list=[]):  # Dangerous! Shared across calls
    my_list.append(item)
    return my_list

print(append_to_list(1))  # [1]
print(append_to_list(2))  # [1, 2]  — not [2]!

Fix: Use None and create a new list inside:

def append_to_list(item, my_list=None):
    if my_list is None:
        my_list = []
    my_list.append(item)
    return my_list

Mistake 4: Passing wrong argument types

def divide(a, b):
    return a / b

divide(10, "2")  # TypeError: unsupported operand type(s)

Fix: Validate input with isinstance() or rely on duck typing with clear documentation.

What you learned & what's next

You now know the core ideas behind define and call your own functions: how def works, parameters vs arguments, return values, default values, and common pitfalls. This is the foundation for writing modular, reusable code.

Next up: you'll learn how functions interact with data structures — specifically passing lists and dictionaries to functions, and how modifications inside a function affect the original object. That topic builds directly on today's lesson.

Practice recap

Write a function called factorial(n) that returns the factorial of a non-negative integer n (using a loop or recursion). Then write a second function combinations(n, k) that uses your factorial function to compute n! / (k! * (n-k)!). Test both with small values and verify against known results.

Common mistakes

  • Forgetting to call the function (writing my_function without parentheses prints the function object, never executes it).
  • Using a mutable default argument like def add_item(item, lst=[]) — the list object is shared across calls, causing unexpected accumulation.
  • Defining a function after you try to call it — Python executes top-down, so a function must be defined before its first use in the script.
  • Omitting the return statement and trying to use the function's result in an expression — the function returns None, which often leads to TypeError: unsupported operand type(s).
  • Mixing positional and keyword arguments incorrectly — positional arguments must come before keyword arguments in a function call.

Variations

  1. Lambda (anonymous functions) for short, single-expression functions: lambda x, y: x + y.
  2. Nested functions defined inside another function (closures) to capture enclosing scope.
  3. Using *args and **kwargs to accept variable numbers of positional and keyword arguments in a single function definition.

Real-world use cases

  • Encapsulate a discount calculation in an e‑commerce checkout system — call the function with purchase amount and discount code to get the final price.
  • Build a reusable validation function that checks user input (e.g., email format or password strength) and returns True/False with an error message.
  • Create a data-cleaning pipeline where each step is a separate function (remove nulls, normalize dates, encode categories) — compose them together in a master function.

Key takeaways

  • A function is defined with def keyword, a name, parentheses for parameters, a colon, and an indented body.
  • Call a function by writing its name followed by parentheses, with arguments inside matching the parameters' positions or names.
  • Use return to send a value back to the caller; without it, the function returns None.
  • Default parameter values are evaluated once at function definition time — avoid mutable defaults like [] or {}.
  • Keyword arguments let you pass arguments in any order and make function calls more readable.
  • Always document your function with a docstring ("""...""") to explain purpose, parameters, and return value.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.