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.
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) # 8def 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 8print() 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 nameA 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 9return 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 nowA 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?