Defining functions
Write Python functions with def, return values from them, and document them with docstrings.
What you will learn
- Define and call a function with def
- Return one or several values, and explain why a function without return gives back None
- Write a docstring and read it back
On this page
You have been calling functions since your first print. Now you will write your own, so that a piece of logic you need twice has one name and one place to fix. The functions lesson in the fundamentals track covers what functions are for, parameters versus arguments, and the call stack. This lesson covers Python’s way of writing them.
def and return
A function definition is a def statement: the keyword, a name, parenthesised parameters, a colon, and an indented body.
def greet(name): """Return a greeting for name.""" return "Hello, " + name + "!"
message = greet("Ada")print(message)print(len(message))Hello, Ada!11greet hands a string back with return, so the caller can store it, measure it or pass it on. A function that only prints is different:
def show(name): print("Hello, " + name + "!")
result = show("Grace")print(result)print(result is None)Hello, Grace!NoneTrueshow has no return, so calling it produces its side effect (the printed line) and then evaluates to None. That is the difference the fundamentals lesson calls returning versus printing, and in Python it is easy to see: result really is None.
def is an ordinary statement that runs when execution reaches it. It creates a function object and binds it to the name. That means a function must be defined before the line that calls it executes, though not necessarily before it appears in the file: a function body can mention a function defined further down, as long as that function exists by the time the call happens.
Returning values
A return ends the function at once, and several return statements are fine. Checking the unusual cases first and returning early keeps functions flat. This is the guard clause idea from the if lesson, and now return makes it complete.
def sign(n): if n < 0: return "negative" if n == 0: return "zero" return "positive"
print(sign(-4), sign(0), sign(9))negative zero positivePython functions return exactly one object. A function that appears to return two actually returns a tuple, and the caller unpacks it:
def min_max(numbers): return min(numbers), max(numbers)
low, high = min_max([3, 9, 1, 7])print(low, high)print(min_max([3, 9, 1, 7]))1 9(1, 9)return a, b builds a tuple, and low, high = ... unpacks it, exactly as in the tuples lesson.
If execution reaches the end of the body without hitting a return, Python quietly returns None. That is the classic trap:
def classify(n): if n > 0: return "positive"
print(classify(5))print(classify(-5))positiveNoneclassify(-5) printed None rather than an error, which can surface far from the cause. When a function returns a value on some paths, make sure every path returns something.
A placeholder body: pass
A function body cannot be empty. When you want to sketch a function and fill it in later, write pass as its body. It does nothing, and the function returns None.
def send_report(name): pass
print(send_report("Ada"))NoneDocstrings
A string literal as the very first statement of a function body is its docstring. Tools, editors and help() read it. Convention (PEP 257) is triple double quotes and a first line that is a short sentence in the imperative: “Return …”, “Convert …”.
def celsius_to_fahrenheit(celsius): """Convert degrees Celsius to degrees Fahrenheit.""" return celsius * 9 / 5 + 32
def fahrenheit_to_celsius(fahrenheit): """Convert degrees Fahrenheit to degrees Celsius.""" return (fahrenheit - 32) * 5 / 9
print(celsius_to_fahrenheit(100))print(round(fahrenheit_to_celsius(98.6), 1))print(round(fahrenheit_to_celsius(celsius_to_fahrenheit(21.5)), 6))212.037.021.5The last line is a handy habit: convert there and back and check you land where you started. Rounding hides the tiny float error discussed in the numbers lesson.
You can read a docstring back from the function itself:
def greet(name): """Return a greeting for name.""" return "Hello, " + name + "!"
print(greet.__doc__)print(greet.__name__)Return a greeting for name.greetType help(celsius_to_fahrenheit) in the REPL and the docstring appears. For longer functions, add a blank line after the summary and describe the parameters and the return value in plain sentences.
Common pitfalls
- Forgetting the parentheses.
greetis the function object;greet("Ada")calls it. Writingx = greetand expecting a greeting bindsxto the function itself. - Printing instead of returning. A function that only prints cannot be reused in
total = price("a") + price("b"). Return the value and let the caller decide whether to print it. - Falling off the end. A path with no
returngives backNone. ThenNone + 1fails with aTypeErrorsomewhere else entirely. - Code after
return. Anything below areturnon the same path never runs. - Calling before defining. A call that executes before the
defhas run raisesNameError. - Reusing a built-in’s name such as
maxorsumfor your own function. The scope lesson in this module explains why that bites.
Write celsius_to_fahrenheit and fahrenheit_to_celsius, each with a docstring, then print a small table for -40, 0, 37 and 100 degrees Celsius. Check that converting a value there and back returns the original.
Input: the four temperatures. Expected output:
-40 C = -40.0 F 0 C = 32.0 F 37 C = 98.6 F 100 C = 212.0 F21.5Show solution
def celsius_to_fahrenheit(celsius): """Convert degrees Celsius to degrees Fahrenheit.""" return celsius * 9 / 5 + 32
def fahrenheit_to_celsius(fahrenheit): """Convert degrees Fahrenheit to degrees Celsius.""" return (fahrenheit - 32) * 5 / 9
for c in (-40, 0, 37, 100): f = celsius_to_fahrenheit(c) print(f"{c:>4} C = {f:>5.1f} F")
print(round(fahrenheit_to_celsius(celsius_to_fahrenheit(21.5)), 6)) -40 C = -40.0 F 0 C = 32.0 F 37 C = 98.6 F 100 C = 212.0 F21.5Each function takes one number and returns one number, with no printing inside, so the loop and the round-trip check can both reuse them. Minus 40 is the point where the two scales agree, which makes it a good test value.
classify(n) in the lesson returns "positive" for positive numbers and None otherwise. Rewrite it so it returns "positive", "zero" or "negative" on every path, by reusing sign.
Input: classify(5), classify(0), classify(-5). Expected output: positive zero negative.
Show solution
def sign(n): if n < 0: return "negative" if n == 0: return "zero" return "positive"
def classify(n): return sign(n)
print(classify(5), classify(0), classify(-5))positive zero negativeThe fix is to cover every case, and the best version of classify is no longer needed at all because sign already does the job. Functions can call other functions, which is how larger programs are built.
Write square(n) that returns n * n, and describe(n) that returns the text "<n> squared is <result>" using square. Print describe(7). Then write a stub cube(n) whose body is only pass, and print cube(2) to see what it returns.
Input: describe(7) and cube(2). Expected output: 7 squared is 49 then None.
Show solution
def square(n): return n * n
def describe(n): return f"{n} squared is {square(n)}"
def cube(n): pass
print(describe(7))print(cube(2))7 squared is 49Nonedescribe builds text and returns it instead of printing, so the caller decides what to do with it. The stub returns None because it never reaches a return.
Summary
def name(parameters):defines a function;returnhands a value back and ends the call.- A function with no
return, or a path that skips it, returnsNone. Several values returned together form a tuple. - The first string in a function body is its docstring, available through
help()and__doc__.
Next: parameters in depth, including defaults, *args, **kwargs and keyword-only arguments.