Intro to Python

17. Functions

A common theme you have seen throughout this section is avoiding rewriting the same code multiple times. Functions are the key to achieving this in programming. A function is a way of organizing code that enables a programmer to reuse it. They are useful because once the code to complete a task has been written once, you can reuse that code to complete the same task anywhere you want with a single line that “calls” (runs) the function.

*Note: Functions are self-contained, which means any code executed within the function won’t influence anything outside the function. We call this a black box because everything inside the function is obscured from the outside code. It simply sees input go in and output come out. This is especially useful when working in teams. If programmer A writes a function and programmer B needs to use it, programmer B does not need to understand how it works. They only need to understand what inputs to give the function and what outputs come out of it; none of the inner processes or code matters to programmer B.*

Functions are composed of 4 components:

  • The function itself: The syntax used to denote a function is dependent on the language. In certain languages, you may also need to include additional keywords that describe the function’s behavior, such as the scope of the function (where it can be called from) and the data type of the output.
  • The name of the function: This is used to call (run) the function when you need it.
  • The parameters: Although functions are self-contained, one pipeline to get outside code/variables inside the function is to “pass it into” the function through parameters. We go into more depth on how to create this pipeline later in the article.
  • The code block: This is the code that will run every time a function is called.

When a program reaches a function call, the program follows this order of control:

  1. The program exits the current body of code and enters the function.
  2. The code block runs from top to bottom.
  3. Upon finishing the code block or being forced out of the block with the keyword return, the program returns to the original body of code.
  4. Any data returned by the function is used in place of the original function call. All other data is removed from the computer’s memory.

Let’s define a basic function named hello_world that simply prints “Hello World!” and then let’s call the function.

def hello_world():
    print("Hello World!")

hello_world()
> Hello World!

Common Mistake: If you run your code and nothing is happening, you might have forgotten to call your function! Defining the function does not run the code. Even experienced programmers can forget to call their function sometimes.

No parameters were being “passed into” (given to) the above function. Let’s expand it to talk about our favorite foods by adding a parameter for our favorite food and then printing it.

def hello_world(favorite_food):
    print("Hello World!")
    print("My favorite food is " + favorite_food + ".")

hello_world("pizza")
> Hello World!
> My favorite food is pizza.

To create and use a parameter, we first needed to give the parameter a name inside the parentheses beside the function definition header. It was favorite_food in this case. We then used the parameter favorite_food as a variable in the function, adding it to “My favorite food is” like a string. Notice that the function doesn’t determine the value of favorite_food. Instead, it is the function call that determines the value.

To use the function and pass a value to the parameter, we called the function, and between the parentheses beside the function call, we specified the parameter’s value. This is when favorite_food is given its value.

Our code would throw an error if the value of favorite_food isn’t passed through a parameter inside the function call’s parentheses and is instead simply defined as a variable outside the function. This is shown below:

def hello_world(favorite_food):
    print("Hello World!")
    print("My favorite food is " + favorite_food + ".")

favorite_food = "pizza"
hello_world()
> TypeError: hello_world() missing 1 required positional argument: 'favorite_food'

However, it is possible to use a variable as the parameter instead of explicitly defining it as “pizza.”

def hello_world(favorite_food):
    print("Hello World!")
    print("My favorite food is " + favorite_food + ".")

favorite_food = "pizza"
hello_world(favorite_food)
> Hello World!
> My favorite food is pizza.

One last notable property of a function is using the “return” keyword that serves two purposes:

  1. Immediately terminate a function and return to the function call.
  2. Return the data following the return keyword.

When data is returned, it is substituted for that function call. For example, we could write a function that adds two numbers and returns the result (and ignores all code after the return keyword). Then, we can store this result in a variable and print it.

def add(a, b):
    return a+b

x = 2
y = 3
z = add(x,y)
print(z)
> 5

Here is one last example of a function accomplishing the more complex task of calculating the factorial of num:

def factorial(num):
    ans = 1;
    for i in range(1, num + 1):
        ans *= i
    return ans

print(factorial(8))
> 40320

Why use functions?

Functions are the key to abstraction (the idea that we hide unnecessary code) in computer programming. Breaking a program down into functions makes them simpler to manage, easier to test, and better to reuse. In the absence of functions, programs would contain a lot of unnecessary duplicate code, would lack separation of utility, and be a nightmare to manage, debug, and test.

Here are the primary benefits of using functions:

  1. Encapsulates and separates tasks - The programmer only needs to know what the code does, not how it does it. Good functions divide code into segments, which splits more extensive programs into smaller pieces.
  2. Increases reusability - Copy and pasting code in programs add unnecessary complexity, maintenance, and introduces more places for the code to accidentally go wrong. Functions not only allow you to reuse the code anywhere in your program, but it also allows other programmers to reuse your code.
  3. Makes testing and debugging much easier - Without functions, programs would be much more challenging to understand and debug. When programs are divided into functions, programmers can “test once, deploy everywhere,” substantially reducing testing time. In addition, functions increase readability, making them easier to maintain and follow.
  4. Divides data and logic - Functions help separate steps from actual data. Functions execute code independent of the data passed in. This can help keep you reuse short-term variables and prevent unwanted modifications.
  5. Saves time - All programming languages have libraries, which are built-in functions, that you can use to accomplish common tasks that would otherwise take hundreds of lines to achieve. Examples include print statements, basic arithmetic operators, and similar tasks we take for granted. Imagine how much time would be wasted if you had to write all of this from scratch.

Note: The difference between libraries and functions is that libraries only contain very commonly used pieces of code and are uneditable. In addition, libraries are guaranteed to be relatively high-quality and previously extensively tested, whereas if you write functions, you need to test and refine them yourself.

You can play with all the code we've used in this article on Trinket: