Back to Blog

What are Functions in Python? Explained in 3 Minutes (Common Questions #2)

Sandy LaneSandy Lane

Video: What are Functions in Python? Explained in 3 Minutes (Common Questions #2) by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

A function is a named, reusable block of code. Define once, call many times. The single most useful organising tool in any programming language.

If you've written any Python, you've already used functions. print(...). len(...). type(...). Each is a function someone else wrote that you call by its name. Today's episode is about defining your own — the def keyword, parameters, return values, and the practical patterns that come up immediately.

What functions are for

Three reasons to write a function:

  1. Naming. A block of code with a clear name communicates intent. is_even(n) is more readable than n % 2 == 0 every time you check.
  2. Reuse. Define once, call from many places. If the implementation changes, you change it in one place.
  3. Abstraction. A function is a small contract: given these inputs, I produce this output. Callers don't need to know how it works.

If you find yourself writing the same three lines twice, that's a function. If a piece of logic has a name in your head ("validate the email," "compute the discount"), that's a function.

Built-in functions

Python ships with dozens of built-ins. You've seen these:

print(len("Hello"))    # 5
print(type(42))        # <class 'int'>

len, type, print, int, str, range, enumerate, zip, sorted, min, max, sum — these are all functions. They take values, do something, and (usually) return a result.

The point: functions are not magic. Built-in functions look the same as ones you write yourself. The only difference is that built-ins are implemented in C inside the Python interpreter, so they're slightly faster and don't have a def line you can read.

Defining a function

def say_hello():
    print("Hello, World!")

Three pieces:

  • def keyword — tells Python "this defines a function."
  • say_hello — the function's name. Same naming rules as variables: lowercase, underscores, no spaces.
  • () — the parameter list. Empty here because say_hello takes no arguments.
  • : — colon, then an indented block.
  • The indented block — the function's body. Runs when the function is called.

Once defined, you call it by name with parentheses:

say_hello()    # Hello, World!
say_hello()    # Hello, World!

Calling twice runs the body twice. The function is a recipe; calling it is cooking the recipe.

Parameters and arguments

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

result = add(3, 5)    # 8

a and b are parameters — placeholders that get values when the function is called.

When you call add(3, 5), Python binds a = 3 and b = 5, then runs the body. The body returns a + b, which is 8. The caller stores that in result.

(See Episode 1 for the full story on the parameter vs argument distinction, args, *kwargs, and keyword arguments.)

return

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

return ends the function and sends a value back to the caller. Without return, the function ends when its last line runs and returns None implicitly.

def say_hello():
    print("Hello, World!")
    # no explicit return → returns None

result = say_hello()
print(result)    # None

print and return are not the same thing. print writes to standard output (the terminal). return gives a value to the caller. A function can do both, but they serve different purposes:

  • Print when the user (or someone reading the terminal) needs to see something.
  • Return when calling code needs the value to use in further computation.

In real programs, helper functions almost always return rather than print. The caller decides what to do with the value.

A practical helper

def is_even(n):
    return n % 2 == 0

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

Six lines that do useful work. is_even takes one argument, returns a bool. The name is a clear question; the body answers it.

This is the unit at which good code is written. A handful of small, well-named functions, each doing one job, composed together into bigger behaviour. The opposite — one giant function with hundreds of lines — is harder to read, harder to test, harder to change.

When to write a function

A few rules of thumb.

Three repetitions. If you've written the same logic three times, factor it out. Once might be a coincidence; twice might be deliberate; three times is a function waiting to happen.

A nameable concept. If you can describe what the code does in two or three words ("validate email," "format date," "compute total"), that's a function.

A clear input/output. If a block has identifiable inputs and a recognizable output, that's the signature waiting to be written.

More than ~20 lines. A function over a screen of code is hard to follow. Split into smaller helpers.

You want to test it. Logic inside a function is testable in isolation; logic in the middle of main is not.

Functions are values

A subtle but important fact: a function is a value, just like a string or a number.

greeting = say_hello    # bind the function to another name
greeting()              # Hello, World!

You can store functions in variables, pass them as arguments to other functions, return them from functions, put them in lists or dicts. This makes Python a higher-order language and unlocks a lot of patterns: callbacks, decorators, dispatch tables.

For now, just know it's possible. We'll return to it in a later episode.

Scope: local vs global

x = 10

def show_x():
    print(x)        # reads the global x

show_x()            # 10

Variables defined outside any function are global. Functions can read them.

def add_one():
    x = x + 1       # error!

But assigning to a global from inside a function tries to create a local with the same name, and the read of x on the right-hand side fails because the local doesn't exist yet. To modify a global, declare it:

def add_one():
    global x
    x = x + 1

In practice, modifying globals from inside a function is bad style. It makes code hard to reason about. Pass values in as parameters; return new values out. That keeps each function self-contained.

Common mistakes

Calling a function without parentheses. say_hello is the function itself; say_hello() is a call. The first prints the function object's repr; the second runs it.

Forgetting return. A function with no return returns None. If the caller does result = my_function() and expects a value, they'll get None and be confused.

Mixing print and return. A helper function should usually return its result, not print it. Let the caller decide what to do with the value.

Reusing parameter names as globals. A name parameter inside a function shadows any global name. Usually fine, but can be confusing.

Indentation errors. The body must be indented. Mixed tabs and spaces are a syntax error. Pick one (spaces, by convention) and stick to it.

Recap

def name(parameters): defines a function. Parameters are placeholders; arguments are the values you pass at call time. return sends a value back; print shows it on screen — they're different. Functions are values you can pass around. Use functions for naming, reuse, and abstraction. Write small functions; compose them.

Now you can read any Python codebase, because almost every Python codebase is a tree of functions. Open one and start at main; follow each call to its definition; expand outward.

Next episode: what is a class? The next layer up — how classes group related functions and data, and when to reach for them instead of plain functions.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.