Functions

Functions are a way of organizing our code into maneageable pieces. They let us:

  • give human-friendly names to blocks of code

  • hide the details of how something works

  • reuse pieces of code in multiple places

Defining a Function

  • Functions are started with the keyword def (short for define a function)

  • Functions are assigned a name in the def statement

  • A list-of-parameters is declared after the name

  • All of the statements within the function are indented (4 spaces)

  • A value can be returned from the function with the return keyword. The function stops running when a value is returned.

  • All (python) functions return a value, even if the value they return is None if the function reaches the end of the statements without a return, the return value will be None

def function_name():
    return "Hello Function"

print( function_name() )

Parameters for Reuse

Functions normally accept parameters that they use to control how they function.

def function_name( name ):
    return "Hello "+ name

print( function_name( username ))

Naming and Documentation

Choosing a friendly, easily-understood name for your functions is an art. Documenting what they do lets users understand what your function is supposed to do, what it will return, and when to use it.

Each programming language has a different way of documenting functions. In Python we use “docstrings”, which are just strings that occur on the first line of the function. We normally use triple-quoted strings so that we can write multiple lines of documentation.

def greeting( name ):
    """Creates a greeting for user with the given name

    returns formatted greeting as a string
    """
    return "Hello "+ name

Scoping

When we pass a parameter into a function, we make that parameter available as a name within the namespace of the function. The function actually operates with multiple namespaces active, if a name can’t be found in the local namespace then the next “higher” namespace is checked. For instance:

weight = 3
def apply_weight( measurement ):
    """Scales the measurement by the weight of the measurement"""
    # weight is *not* defined in this function
    # but we can still access the value from the "global" namespace (the module)
    return measurement * weight

Note

Module-level variables in Python are called globals. Most functions just use two scopes, locals and globals. You can, however, define functions inside functions that have more levels of scope. Normally values in globals are functions or constants which we normally name with ALL_CAPS_AND_UNDERSCORES to indicate that they are constant.

def outer_function(collection, external_parameter):
    def inner_function(item):
        return item % external_parameter
    result = []
    for item in collection:
        result.append(inner_function(item))
    return result

Fun fact, the def statement just creates a variable with the name of your function that points to the (compiled) code that implements that function. The name is then assigned to the context in which the def statement occurs. This can sometimes be used to return a function from a function.

>>> def multiplier(multiplicand):
...    def bound_multiplier(value):
...        return value * multiplicand
...    return bound_multiplier
...
>>> times_two = multiplier(2)
>>> times_two(4)
8
>>> times_three = multiplier(3)
>>> times_three(4)
12

Default Parameters

Sometimes we want our function to have a default value for a parameter, but allow the user to change that parameter if they need to:

def greeting( name='World' ):
    """Creates a greeting for user with the given name

    name -- the name to greet, defaults to "World"

    returns formatted greeting as a string
    """
    return 'Hello '+name

greeting()
greeting('Groot')

Note

Some style guides will suggest that you never use a global variable in a function, and you may find that in University you get your project failed if you do. Most real world programmers aren’t quite so absolute in their avoidance of globals.

If you wanted to avoid a global in your function, you could use default parameters to rewrite apply_weight above as:

DEFAULT_WEIGHT = 3
def apply_weight( measurement, weight=DEFAULT_WEIGHT ):
    """Scales the measurement by the weight of the measurement"""
    return measurement + weight

Type Declarations

With more recent versions of Python, you can add information about the expected type of each parameter and the return type of your function. While we won’t be using these much in this tutorial, this should give you a feel for what they look like.

DEFAULT_WEIGHT:float = 3.0
def apply_weight( measurement: float, weight:float=DEFAULT_WEIGHT) -> float:
    """Scales the measurement by the weight of the measurement"""
    return measurement * weight

Type declarations can be used by tools to catch errors in your code that would otherwise result in crashes at run-time.

Heart Click in Functions explores using functions to rework our Heart Click game into an easily maintainable form.