Tutorial 07: Functions

These notes are adapted from the Python tutorial available at: https://docs.python.org/3/tutorial/.

Here we see write re-usable code in the form of functions.

Defining Functions

In Python we can create objects called functions to automate the execution of a code block for a varying set of inputs. We have used several functions in Python, such as sin, re.sub, and requests.get. Now we will learn how to create them ourselves!

Here is an example of a function that multiplies the input value by ten and returns the result.

In [1]:
def multiply_by_ten(n):
    """Takes the input and multiplies by ten."""
    result = n * 10
    return result

Once we run that code, we can call it like any other function:

In [2]:
multiply_by_ten(3)
Out[2]:
30

The keyword def introduces a function definition. It must be followed by the function name and the parenthesized list of formal parameters. The statements that form the body of the function start at the next line, and must be indented. The output of the function can be saved as a variable, like any other function in python.

In [3]:
old_value = 4
new_value = multiply_by_ten(old_value)
print(new_value)
40

We can also make a function print out a value (or otherwise interact with the filesystem or other resources), such as this:

In [4]:
def multiply_by_ten(n):
    """Takes the input and multiplies by ten."""
    result = n * 10
    print("So excited to return a value to you!")
    
    return result

And when you run the function now, it returns a result but also prints a message.

In [5]:
multiply_by_ten(42)
So excited to return a value to you!
Out[5]:
420

Function arguments

We can define functions that take more than one input value. For example, we could create a custom add function that adds two numbers together:

In [6]:
def my_add(a, b):
    """Adds together the two inputs and returns the result"""
    return a + b
In [7]:
my_add(1, 4)
Out[7]:
5

It is also possible to add default values for some of the inputs. The only rule is that the inputs with default values must come after the inputs without default values:

In [8]:
def my_add_default(a, b=1):
    """Adds together the two inputs and returns the result"""
    return a + b
In [9]:
print(my_add_default(5, 1))  # adds 5 and 1 together
print(my_add_default(5))     # this sets b=1, the default value
6
6

Docstrings

The first statement of the function body can optionally be a string literal; this string literal is the function’s documentation string, or docstring. There are tools which use docstrings to automatically produce online or printed documentation, or to let the user interactively browse through code; it’s good practice to include docstrings in code that you write, so make a habit of it. You can read more about docstrings in PEP 257.

To see a function's docstring, use the help function in Python:

In [10]:
help(my_add_default)
Help on function my_add_default in module __main__:

my_add_default(a, b=1)
    Adds together the two inputs and returns the result

Coding styles for docstrings follow several different conventions. In this course we are going to use what's typically called the "Google Style". Here is an example of the full documentation for the function my_add_default:

In [11]:
def my_add_default(a, b=1):
    """Adds together the two inputs

    This function is not very useful, but created just for illustration
    purposes. Generally you should just use the `+` operator in Python.

    Args:
        a: The first number to add.
        b: The number to add to the first input; defaults to 1.

    Returns:
        The sum of the two inputs.
    """
    output = a + b
    
    return output

The first line is always a short phrase describing the general idea of the function. Following this, there is a longer description of what the function actually does. Then, there is a section on the input "Args" (arguments) and then a section describing the results. Everything is indendented by four or eight spaces and finished with a final triple quote.

Now look at the nice help page given by our function:

In [12]:
help(my_add_default)
Help on function my_add_default in module __main__:

my_add_default(a, b=1)
    Adds together the two inputs
    
    This function is not very useful, but created just for illustration
    purposes. Generally you should just use the `+` operator in Python.
    
    Args:
        a: The first number to add.
        b: The number to add to the first input; defaults to 1.
    
    Returns:
        The sum of the two inputs.

I will always expect you to add a single line docstring to any function you create in this course. I'll specify when a "full docstring" is required.


Practice

Write a function to_piglatin, with a complete docstring, that takes an input word and returns a version of the word in Pig Latin (as we did in the string tutorial). It should have a single input giving the word to convert.

In [13]:
def to_piglatin(word):
    """Translates an input word into 'pig' Latin.
    
    Args:
        word: A python string containing a single word.
    Returns:
        A string representing the word as translated into pig Latin.
    
    """
    return word[1:] + word[0] + 'ay'

Check that it works for several inputs:

In [14]:
print(to_piglatin("pig") == "igpay")
print(to_piglatin("monkey") == "onkeymay")
print(to_piglatin("penguin") == "enguinpay")
True
True
True

Print out the docstring of the function and check that it matches what you wrote above with the help function:

In [15]:
help(to_piglatin)
Help on function to_piglatin in module __main__:

to_piglatin(word)
    Translates an input word into 'pig' Latin.
    
    Args:
        word: A python string containing a single word.
    Returns:
        A string representing the word as translated into pig Latin.

Now, write a function remove_spaces with two inputs and a full docstring. The first input is a string that the user wants to replace all of the spaces in. The second is the replacement string that spaces are converted to. Name the inputs string and repl, and set a default value for the replacement to be a dash: -. I'll import the re module here as you'll need it in the function.

In [16]:
import re
In [17]:
def remove_spaces(string, repl='-'):
    """Replace spaces with a fixed string.
    
    Args:
        string: The input string to operate on.
        repl: The fixed string to replace spaces with. Defaults
            to a single dash, '-'.
    Returns:
        The new string object.
    """
    return re.sub(' ', repl, string)

You can check that your code runs correctly with the following tests:

In [18]:
print(remove_spaces("Hello there!") == "Hello-there!")          # simple test with default value
print(remove_spaces("Hello my friend!") == "Hello-my-friend!")  # test with two replacements
print(remove_spaces("Hello there!", "_") == "Hello_there!")     # use underscore as replacement
print(remove_spaces("Hello there!", "") == "Hellothere!")       # use empty string as replacement
print(remove_spaces("H a", "SPACE") == "HSPACEa")               # use a longer replacement string
print(remove_spaces("") == "")                                  # check that it works with an empty string
True
True
True
True
True
True

Print out the docstring of the function with the help function and check that it matches what you wrote above:

In [19]:
help(remove_spaces)
Help on function remove_spaces in module __main__:

remove_spaces(string, repl='-')
    Replace spaces with a fixed string.
    
    Args:
        string: The input string to operate on.
        repl: The fixed string to replace spaces with. Defaults
            to a single dash, '-'.
    Returns:
        The new string object.


Extra Practice

Write a function below, with a full docstring, that takes the name of a Wikipedia page and returns the 25 most frequent words used on the page. This will involve grabbing code from Tutorial 06 and wrapping it up in a function call.

In [20]:
def top_wiki_words(url):
    """Return top 25 words from Wikipedia page.
    
    Args:
        url: full URL of the Wikipedia page
    Returns:
        list of the top words used in the body of the text.
    """
    import requests
    import collections
    
    r = requests.get(url)
    website = r.text
    website = website[website.find("<body"):website.find("<noscript>")]
    website = re.sub('<[^>]+>', '', website)
    website = website.lower()
    website = re.sub('[^a-z]', ' ', website)
    website = re.sub('[ ]+', ' ', website)
    words = re.split(' ', website)
    
    return collections.Counter(words).most_common(30)
In [21]:
top_wiki_words('https://en.wikipedia.org/wiki/Marxism')
Out[21]:
[('the', 648),
 ('of', 547),
 ('and', 317),
 ('in', 189),
 ('to', 173),
 ('a', 155),
 ('marx', 118),
 ('marxism', 101),
 ('that', 95),
 ('s', 93),
 ('is', 93),
 ('marxist', 92),
 ('as', 73),
 ('class', 69),
 ('social', 65),
 ('by', 62),
 ('production', 61),
 ('theory', 52),
 ('society', 47),
 ('economic', 44),
 ('on', 44),
 ('socialism', 44),
 ('from', 43),
 ('with', 41),
 ('political', 40),
 ('history', 40),
 ('karl', 40),
 ('engels', 38),
 ('for', 37),
 ('socialist', 34)]