Python

Functions — the basics

How to package code into a reusable, named unit — parameters, return values, and the difference between the two most beginners mix up first.

Beginner

4 min read

Why functions exist

print("Hello, Ada!")
print("Hello, Grace!")
print("Hello, Alan!")

vs.

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

Both print the same three lines. The function version defines the greeting logic exactly once, then reuses it — if the greeting ever needs to change (say, to "Hi, {name}!"), there's exactly one line to edit, not one per person. This is the entire point of a function: package a piece of logic under a name, so it can be triggered repeatedly without being retyped, and changed in exactly one place when it needs to change.

Defining and calling a function

def add(a, b):
    result = a + b
    return result
 
total = add(3, 5)
print(total)   # 8

def NAME(PARAMETERS): starts a function definition; the indented block underneath is its body, run each time the function is called (add(3, 5)). a and b are parameters — placeholders that receive whatever values are passed in when the function is called (3 and 5 here, called arguments). Defining a function doesn't run its body at all — nothing happens until it's actually called.

return vs. print — the mix-up almost everyone makes first

def add_v1(a, b):
    print(a + b)          # displays the result, gives it back to nothing
 
def add_v2(a, b):
    return a + b            # hands the result back to whoever called it
 
result1 = add_v1(3, 5)   # prints "8" to the screen; result1 is None
result2 = add_v2(3, 5)   # prints nothing; result2 is 8

print() displays something on the screen — it's for a human to look at, and it doesn't give the caller anything usable back (technically it returns None, Python's "nothing" value). return hands a value back to the code that called the function, so that value can be stored in a variable, passed to another function, or used in a calculation. A function with no return statement returns None automatically. This distinction is the single most common early confusion: printing a result is not the same as returning it, and a function that only prints can't have its result used anywhere else in the program.

Default parameter values

def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}!")
 
greet("Ada")                    # "Hello, Ada!"       — greeting uses its default
greet("Ada", "Hi")              # "Hi, Ada!"           — greeting overridden
greet("Ada", greeting="Hey")    # "Hey, Ada!"          — same, passed by name

A parameter can have a default value, used whenever the caller doesn't provide one — this makes that argument optional. Arguments can be passed positionally (matched by order) or by keyword (matched by name, greeting="Hey"), which is especially useful once a function has several parameters and it's not obvious from a bare value alone which one is which.

Multiple return values

def min_max(numbers):
    return min(numbers), max(numbers)
 
lowest, highest = min_max([4, 1, 9, 2])
print(lowest, highest)   # 1 9

return a, b actually returns one value — a tuple (a, b) — but Python lets you unpack it directly into two variable names on the receiving end in a single line, which is why this looks like "returning two things" even though it's really returning one paired value.

Variable scope: where a name is actually visible

def calculate():
    x = 10        # local to calculate — only exists inside this function
    return x * 2
 
calculate()
print(x)          # NameError: x only existed inside calculate(), it's gone now

A variable created inside a function only exists while that function is running, and disappears afterward — it's local to that function, invisible to code outside it. This is a good thing: it means two different functions can both use a variable named x internally without stepping on each other. (A more precise, complete version of these scoping rules — including how nested functions interact with variables from an enclosing scope — is covered once you're comfortable with functions themselves, in the closures and scope lesson.)

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. What does a function that only calls print() and has no return statement actually return when called?

2. Given `def greet(name, greeting="Hello"):`, what does `greet("Ada")` do?

3. What does `return a, b` inside a function actually return?

4. Why does a variable assigned inside a function disappear once that function returns?