This lesson is being piloted (Beta version)

Functions

Overview

Teaching: 20 min
Exercises: 20 min
Questions
  • What are functions?

  • How do I create my own functions in Python?

  • How do I use my own functions?

  • How do I document my functions?

Objectives
  • Explain the benefits of using functions.

  • Explain and identify the difference between function definition and function call.

  • Write a function that takes a small, fixed number of arguments and produces a single result.

  • Explain the difference between required and optional function arguments.

  • Document your functions with docstrings.

Don’t Repeat Yourself

One of the core principles of any programming language is, “Don’t Repeat Yourself”, often referred to as DRY. If you have an action that should occur many times, you can define that action once and then call that code whenever you need to carry out the action.

Functions mean less work for us as programmers, and effective use of functions results in code that is less error-prone.

When our function works, we don’t have to worry about that code anymore. Every time you repeat code in your program, you introduce an opportunity to make a mistake. Writing a function means there is one place to fix mistakes, and when those bugs are fixed, we can be confident that this function will continue to work correctly.

Break programs down into functions to make them easier to understand

We can only keep a few items or ideas in working memory at a time. To understand complicated ideas, we chunk information together and then think about the higher-level chunks. For example, when thinking about cars we may just think about engines, wheels, and entertainment systems. Each one is a complicated system, but we don’t need to consider every detail all the time.

Functions serve the same purpose in programs. They chunk some code together so we can treat it as a single thing elsewhere. For example, I can use a sort() function to order a list of strings without needing to think about the implementation details of sorting algorithms.

Functions allow us to:

Additionally, functions provide a convenient focal point for documentation. They are at the right level of detail for documenting your code, and Python has a number of good tools for generating formatted documentation from functions.

FIXME: Do I need the next 2 paragraphs?

Conceptually, functions can be treated as a box of code. When using a function it can be a black box. You need only consider the inputs and outputs: what data do you supply, and what should you get back. When writing functions they are a white box: you must pay attention to the internal details as well as the data flow (inputs and outputs).

Finally, when designing your functions, it can be helpful to only write functions that do a single task. One function, one job. Complicated functions that do many things are harder to write, harder to debug, and harder to use.

Define a function using def with a name, parameters, and a block of code

def print_greeting():
    print('Hello!')

Write a Happy Birthday function

Write a function called happy_birthday that prints the message “Happy birthday!”.

Solution

def happy_birthday():
    print("Happy birthday!")

Defining a function does not run it

print_greeting()
Hello!

A Common Error

Functions must be defined before you can call them. The following code would not work:

print_greeting("Bruce")

def print_greeting():
    print('Hello!')

Call your Happy Birthday function

Call your happy_birthday function.

Solution

happy_birthday()
Happy birthday!

Arguments in the function call are matched to the parameters in the function definition

def print_date(year, month, day):
    joined = str(year) + '/' + str(month) + '/' + str(day)
    print(joined)

print_date(1871, 3, 19)
1871/3/19

Or, we can name the arguments when we call the function, which allows us to specify them in any order:

print_date(month=3, day=19, year=1871)
1871/3/19

Add a name parameter to your Happy Birthday function

Add a parameter called name to your happy birthday function. The function should now use the supplied name in the message. Do not worry about the case where the name is not supplied.

Solution

def happy_birthday(name):
    print("Happy birthday to " + name + "!")

Optional arguments have a default value in the function definition

def print_greeting(who=""):
    if who:  # recall that empty strings are treated as False
        print("Hello " + who + "!")
    else:
        print("Hello!")
print_greeting()
Hello
print_greeting("Brian")
Hello Brian

Add a default value to name

Give the name parameter a default value of “you”.

Solution

def happy_birthday(name="you"):
    print("Happy birthday to " + name + "!")

Positional and Keyword Arguments

In the Python world, you will frequently see function arguments described as positional and keyword arguments. Positional arguments are those that are matched to parameters by position in the function call, while keyword arguments are matched by name. Historically, positional arguments were used for parameters without a default value, while keyword arguments were used for parameters with default values. However this need not be the case.

Functions return results with the return statement

Averaging numbers

Write a function that accepts a list of numbers and returns the average. If the argument is an empty list, then return None. All other error conditions can be ignored.

Solution

def average(values):
    if len(values) == 0:
        return None
    return sum(values) / len(values)

Use docstrings to document your functions in a standard way

Python functions can contain a special documentation string, known as the docstring. The docstring can contain information about the purpose and use of your function.

There are a number of docstring conventions. Some of the most useful are:

A one-line docstring example

For example (from PEP-257):

def kos_root():
    """Return the pathname of the KOS root directory."""
    global _kos_root
    if _kos_root: return _kos_root
    ...

A multi-line docstring example

def complex(real=0.0, imag=0.0):
    """Form a complex number.

    Keyword arguments:
    real -- the real part (default 0.0)
    imag -- the imaginary part (default 0.0)
    """
    if imag == 0.0 and real == 0.0:
        return complex_zero
    ...

Note the following features:

Add a docstring

What do you think this function does? Add a docstring to it.

def last_first(first_name, last_name, separator=", "):
    result = "{0}{1}{2}".format(last_name, separator, first_name)
    return result

Solution

The function takes a first name and a last name, returning a single string with the last name first, separated from the first name by the separator string. The separator value is optional, with a default value of “, “.

def last_first(first_name, last_name, separator=", "):
    """Return a single string in the form "{last_name}{separator}{first_name}".
    Arguments:
    first_name -- the first name
    last_name -- the last name
    separator -- the separator used between first and last names (default ", ")
    """
    result = "{0}{1}{2}".format(last_name, separator, first_name)
    return result

Key Points

  • Don’t repeat yourself. Keep your code DRY by using functions.

  • Break programs down into functions to make them easier to understand.

  • Define a function using def with a name, parameters, and a block of code.

  • Defining a function does not run it.

  • Arguments in the function call are matched to the parameters in the function definition.

  • Optional arguments have a default value in the function definition.

  • Functions return results with the return statement.

  • Use docstrings to document your functions in a standard way.