Skip to content

Defining functions

Goal: name a block of code so you can run it whenever you like, with different inputs each time, and get a result back.

Why this matters

Here is a program that welcomes three people with a banner:

copied_code.py
print("=" * 30)
print("Welcome, Ada!")
print("=" * 30)

print("=" * 30)
print("Welcome, Grace!")
print("=" * 30)

print("=" * 30)
print("Welcome, Linus!")
print("=" * 30)

It works, but the same three lines are copied three times. Now suppose you want the banner to use - instead of =. You have to change three places, and if you miss one, the program is inconsistent. With ten banners it gets worse.

Copying code is the problem. A function is the fix: you write the block once, give it a name, and run it by name as many times as you like.

Your first function

first_function.py
def print_banner():
    print("=" * 30)
    print("Welcome!")
    print("=" * 30)

print_banner()
print("Some other work happens here.")
print_banner()
==============================
Welcome!
==============================
Some other work happens here.
==============================
Welcome!
==============================

The first four lines define the function. def is short for define, then comes the name you chose, then empty parentheses, then a colon. The indented lines below are the function's body, exactly like the body of an if or a loop.

Defining a function does not run it. Python reads the definition, remembers it under the name print_banner, and moves on. The body only runs when you call the function by writing its name followed by parentheses: print_banner(). Each call runs the whole body from the top.

You have been calling functions since lesson one. print(), input(), len(), int(), and random.randint() are all functions that someone else defined. The only new thing here is that you are the one writing the definition.

Function names follow the same rules as variable names: lowercase, words joined with underscores. A good name says what the function does, so it usually starts with a verb: print_banner, calculate_total, ask_for_number.

Parameters: giving a function input

The banner above always says Welcome!. To make each call different, give the function a parameter, a variable name inside the parentheses that receives a value on every call:

parameters.py
def print_banner(name):
    print("=" * 30)
    print(f"Welcome, {name}!")
    print("=" * 30)

print_banner("Ada")
print_banner("Grace")
print_banner("Linus")
==============================
Welcome, Ada!
==============================
==============================
Welcome, Grace!
==============================
==============================
Welcome, Linus!
==============================

name is the parameter. When you call print_banner("Ada"), Python sets name = "Ada" and runs the body. The value you pass in, "Ada", is called an argument. Parameter is the name in the definition, argument is the value in the call. People mix the two words up constantly, and it rarely matters.

This is the same output as the copied version at the top of the lesson, from a third of the code. Changing the banner character is now a one-line edit.

A function can take several parameters, separated by commas. The arguments are matched up in order:

two_parameters.py
def print_banner(name, width):
    print("=" * width)
    print(f"Welcome, {name}!")
    print("=" * width)

print_banner("Ada", 20)
print_banner("Grace", 40)
====================
Welcome, Ada!
====================
========================================
Welcome, Grace!
========================================

print_banner("Ada", 20) sets name to "Ada" and width to 20. Swap the arguments to print_banner(20, "Ada") and width becomes "Ada", so the first line of the body tries "=" * "Ada" and crashes with a TypeError. Order matters. Keep the arguments in the order the definition lists them.

Default values

If most calls would pass the same value, give the parameter a default in the definition. Callers can then leave it out:

default_values.py
def print_banner(name, width=30):
    print("=" * width)
    print(f"Welcome, {name}!")
    print("=" * width)

print_banner("Ada")
print_banner("Grace", 10)
==============================
Welcome, Ada!
==============================
==========
Welcome, Grace!
==========

width=30 means use 30 unless the caller says otherwise. You have used this already: print() puts a newline at the end by default, and round(x) rounds to zero decimal places unless you pass a second argument. Parameters with defaults must come after the ones without.

Return: getting a result back

So far the banner function does something. Many functions instead work something out and hand the answer back with return:

return_value.py
def add_tax(price):
    return price * 1.2

total = add_tax(100)
print(total)
print(add_tax(50) + add_tax(25))
120.0
90.0

return price * 1.2 ends the function and sends that value back to wherever the call was made. The call add_tax(100) then becomes 120.0, and you can do anything with it that you could do with the number itself: store it in a variable, print it, or add it to another call. len() and int() work this way, which is why you write length = len(word).

When Python hits return, the function stops immediately. Any lines after it inside the body never run.

return is not print

This is the most important idea in the lesson, and the one beginners struggle with most:

return_vs_print.py
def double_print(n):
    print(n * 2)

def double_return(n):
    return n * 2

result = double_print(5)
print(result)

result = double_return(5)
print(result)
10
None
10

double_print(5) shows 10 on the screen, but hands nothing back, so result becomes None, the same nothing value that .append() returns. double_return(5) shows nothing on its own, but hands 10 back, so result is 10 and the print outside the function shows it.

The rule of thumb: a function should usually return its answer and let the caller decide whether to print it. A function that prints is a dead end, because you cannot do anything else with what it printed. The banner function is fine printing, because showing a banner is its job. A function that calculates should return.

Variables inside a function are private

Variables created inside a function exist only while that function is running:

scope.py
def calculate_area(width, height):
    area = width * height
    return area

room = calculate_area(4, 5)
print(room)
print(area)
20
Traceback (most recent call last):
  File "scope.py", line 7, in <module>
    print(area)
          ^^^^
NameError: name 'area' is not defined

area was created inside calculate_area, so it disappears when the function returns. Outside, it does not exist. This is called scope, and it is a feature: functions cannot accidentally overwrite each other's variables, so you can reuse simple names like total or i in every function without worrying.

The way to get a value out of a function is return, as calculate_area does. room holds the returned value, and that works fine. Parameters are private too: width and height only exist inside the function.

Try it

Write a function describe(name, age) that returns a string such as Ada is 36 years old. Call it three times with different people and print each result. Then give age a default value and call it with just a name.

Common mistakes

The function does nothing

You defined it but never called it. A definition on its own runs nothing. Add my_function() below the definition.

TypeError: greet() missing 1 required positional argument: 'name'

The definition has a parameter but the call passed no argument. Write greet("Ada"), not greet().

TypeError: greet() takes 1 positional argument but 2 were given

The opposite: more arguments than parameters. Check the definition and the call match.

The result is None

The function prints instead of returning, or has no return at all, or the return is inside an if that was not taken. Make sure every path through the function ends in a return.

NameError: name 'total' is not defined

You are using a variable outside the function that was created inside it. Return the value instead, and store it when you call the function.

The code after return never runs

That is expected. return ends the function on the spot. Move the lines above the return if they need to run.

Exercises

  1. Greet. Write greet(name) that prints a friendly greeting. Call it with two fixed names and once with a name from input().
  2. Even check. Write is_even(number) that returns True for even numbers and False otherwise. Use it in a loop to print the even numbers from 1 to 10. A function that returns True or False usually gets a name starting with is_.
  3. Average. Write average(numbers) that takes a list and returns its average. Make it return 0 for an empty list instead of crashing. Test it with three different lists.
  4. Tip calculator, again. In Module 1 you wrote a tip calculator. Rewrite it around a function calculate_tip(bill, percent=15) that returns the tip rounded to two decimal places. Then use the same function to also show a 20% tip.
Solution 1
def greet(name):
    print(f"Hello, {name}! Nice to meet you.")

greet("Ada")
greet("Grace")
greet(input("What is your name? "))

The last call passes the result of input() straight in as the argument. Functions calling functions is normal.

Solution 2
def is_even(number):
    return number % 2 == 0

print(is_even(4))
print(is_even(7))

for n in range(1, 11):
    if is_even(n):
        print(f"{n} is even")

number % 2 == 0 is already True or False, so you return it directly. There is no need for if number % 2 == 0: return True else: return False, though that works too.

Solution 3
def average(numbers):
    if len(numbers) == 0:
        return 0
    return sum(numbers) / len(numbers)

print(average([82, 95, 77, 60]))
print(average([10, 20]))
print(average([]))

Two return statements, but only one runs on any given call. If the list is empty, the first return ends the function before the division can happen.

Solution 4
def calculate_tip(bill, percent=15):
    return round(bill * percent / 100, 2)

bill = float(input("Bill amount: "))
tip = calculate_tip(bill)
print(f"Tip: {tip}")
print(f"Total: {round(bill + tip, 2)}")

print(f"A generous 20% tip would be {calculate_tip(bill, 20)}")

The function returns rather than prints, which is what makes the second use possible. The default percent=15 keeps the common case short.

Summary

  • def name(parameters): defines a function. The body runs only when you call name(arguments).
  • Parameters are the names in the definition. Arguments are the values passed in a call, matched by position.
  • parameter=value in the definition gives a default, so callers can leave it out.
  • return value sends a result back and ends the function. Store or use the result at the call.
  • Printing inside a function shows something. Returning gives something back. Calculating functions should return.
  • Variables created inside a function are private to it. Use return to get values out.

Next: Writing good functions