Python Functions: def, return and Returning Multiple Values
A Python def creates a function object and return hands back a value, or None. Returning several values as a tuple, the None trap and functions as values.
- Course: Python study plan
- Module: Functions
- Kind: Lesson
- Reading time: 12 min
- Runtime: CPython 3.11
How do you define a function in Python?
Define a Python function with def, a name, parameters in parentheses and an indented body, as in def area(width, height): return width * height. The def statement creates a function object and binds the name when it runs; the body runs only when the function is called. return hands a value back, and a function without one returns None.
Lesson
A function in Python is an object created by a def statement, bound to a name like any other value, and called with parentheses. That one sentence explains most of what surprises people later: a function can be passed to another function, stored in a list, returned from a call, given attributes, and redefined. This lesson covers the statement, what return does and does not do, the None that every function returns when it says nothing, docstrings, and the discipline — one job, no hidden state, inputs in and a result out — that makes a function worth writing.
The statement
def area(width, height):
"""Return the area of a width × height rectangle."""
return width * height
print(area(3, 4)) # 12
def runs when the interpreter reaches it: it compiles the body, creates a function object, and binds the name area to it. The body does not run until the function is called. That is why a function must be defined above the code that calls it at module level (a call at the top of a file to a function defined below it is a NameError), but functions can call each other in any order as long as every def has executed before the first call.
The parameters — width, height — are local names bound to the arguments when the function is called. Everything assigned inside the body is local too, created on entry and gone on return (Module 4 lesson 3).
return
return expr evaluates the expression, ends the call and hands the value back. A return with no expression, or falling off the end of the body, returns None. There is no way to return nothing at all — the caller always gets an object.
def find_index(xs, target):
for i, x in enumerate(xs):
if x == target:
return i # leaves the loop and the function at once
return -1 # the "not found" answer; without it the caller would get None
Several values are returned as one tuple, and unpacked at the call site:
def min_max(xs):
return min(xs), max(xs)
lo, hi = min_max([3, 1, 4])
return inside a loop is the cleanest early exit (Module 3). return inside a try still runs the finally block (Module 10). And code after a return in the same block never runs — an unreachable line is a bug, not a comment.
The None trap
A function that computes something but forgets to return it hands back None, and the caller's next line fails with AttributeError: 'NoneType' object has no attribute … or TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'. The same trap hides in methods that work in place: xs.sort(), xs.append(x), d.update(...), random.shuffle(xs) all return None by design, so xs = xs.sort() throws the list away. The rule the standard library follows — a method that mutates returns None; a method that builds a new object returns it — is worth adopting in your own code.
Functions are objects
def double(x):
return 2 * x
f = double # another name for the same function
print(f(21)) # 42
ops = [double, str, len] # functions in a list
print(double.__name__) # 'double'
print(type(double)) # <class 'function'>
Because functions are values, they can be arguments (sorted(words, key=len), map(double, xs)), return values (a function that builds and returns another — Module 4 lesson 3), and dictionary values (a dispatch table, Module 3). Calling is the only thing parentheses do: double is the function, double(3) is a call. Forgetting the parentheses passes the function where its result was wanted, and since a function object is truthy, if is_ready: is a silent bug.
Docstrings and help
The first statement of a body, if it is a string, is the docstring; help(area) and editors show it, and area.__doc__ holds it. Write it as what the function does for the caller, in the imperative: "Return the area…", not "This function computes…". A one-line docstring is enough for a small function; a longer one describes parameters, the return value and the exceptions raised (Module 15 covers the conventions). Type hints — def area(width: float, height: float) -> float: — are the other half of the contract and are covered in this module's last lesson.
What makes a function good
- One job. A function that reads input, computes and prints does three, and none of them can be tested or reused alone. Read in
main, compute in a function that takes values and returns values, print inmain. - No hidden inputs. A function that reads a global has an input its signature does not show; pass it as a parameter.
- No surprise outputs. A function that returns a value and also mutates its argument or prints is doing two things; pick one and say so in the name (
sortversussorted). - A name that says what it returns, for functions that return:
is_prime,parse_line,best_score. A verb for functions that act:save,print_report.
def main():
n = int(input())
scores = [int(input()) for _ in range(n)]
lo, hi = min_max(scores)
print(f"{lo} {hi}")
That shape — main does I/O, helpers are pure — is how every reference solution in this track is written, and it is what makes the helpers testable in Module 18.
Pitfalls
- Calling a function above its
defat module level. xs = xs.sort()and other in-place methods assigned.- A missing
returnon one path (the loop found nothing, theelsebranch was not written). - Passing
fwheref()was meant. - A helper that prints its result instead of returning it — the caller cannot use it.
- A docstring that repeats the name instead of describing the contract.
Key takeaways
defcreates a function object when executed and binds a name; the body runs only when called.returnends the call with a value; noreturnmeansNone; several values come back as a tuple.- In-place methods return
Noneby convention; never assign their result. - Functions are values: pass them, store them, return them; parentheses call.
- One job, explicit inputs, a returned result, a docstring stating the contract; I/O in
main.
Common questions
How do I return multiple values from a Python function?
Return them separated by commas, which builds one tuple: return min(xs), max(xs). The caller unpacks it into several names, as in lo, hi = min_max(scores). The function still returns a single object; the tuple is what carries the values together.
Why does my Python function return None?
A function that ends without a return statement, or with a bare return, returns None. The usual cause is computing a value and never returning it, or a path — the loop found nothing, the else was not written — that falls off the end. In-place methods such as xs.sort() and xs.append(x) also return None by design.
What is the difference between return and print in Python?
return hands a value back to the caller, which can store it, test it or pass it on; print writes text to standard output, and the function still returns None. A helper that prints its result instead of returning it cannot be reused or tested, so compute in functions that return and print in main.
Are functions objects in Python?
Yes. A Python function is a first-class object: it can be bound to another name, stored in a list or dict, passed as an argument, as in sorted(words, key=len), and returned from another function. Parentheses are what call it — double is the function, double(3) is a call.
Exercises
Statistics helpers
Write three pure functions and a main that does all the input and output. min_max(xs) returns the smallest and largest values as a tuple; mean(xs) returns the average; spread(xs) returns max - min. main reads the numbers, unpacks the tuple, and prints the report. For an empty input print no data.
Input: one line of integers (possibly empty). Output: min <a> max <b> mean <m> spread <s> with the mean to two decimals, or no data.
3 1 4 1 5
prints
min 1 max 5 mean 2.80 spread 4A pipeline of functions
Functions are values. The starter defines double, square, negate and increment. Read a line naming functions in order (possibly empty), build a list of function objects from a dictionary of names, then read a line of integers and pass each through the whole list in order.
Input: a line of function names, then a line of integers. Output: <x> -> <result> per integer.
double square
1 2 3
prints
1 -> 4
2 -> 16
3 -> 36In this module: Functions
- Defining functions — def, return and functions as values (this lesson)
- Parameters and arguments — positional, keyword, defaults, *args and **kwargs
- Scope and closures — LEGB, global, nonlocal and late binding
- Recursion — base cases, the call stack and memoisation
- Lambdas and higher-order functions
- Type hints and docstrings — the contract a function publishes
- Checkpoint — Functions
← Checkpoint — Control flow · Parameters and arguments — positional, keyword, defaults, *args and **kwargs →