How Do Functions Work in Python?

How do functions work in Python

Understanding how functions work in Python is key to building clean and reusable code in your programs. In addition, understanding how Python functions work will help when working with a framework like Django or Flask which are both written in the Python programming language. In fact, this is probably one of the key benefits, since you will be able to scan source files and look at various function definitions to understand what the software offers you. Recall that in general terms and regardless of the programming language, a function serves the purpose of reducing code duplication, breaking down larger problems into smaller ones, increasing the readability of software, boosting code reuse, and information hiding.

First Class Citizens

You may have heard of the term “first-class citizen” in programming circles before. Indeed the idea of functions as first-class citizens is popular in the JavaScript world. What we mean when we say the first-class citizens is that functions are on the same level so to speak as other objects in the language. As a result of this, you can assign functions to variables or even pass them as an argument to other functions. Just like in JavaScript, this helps to allow one to bend the language to their will. Just like PHP has many functions that ship as part of the language, so too does Python. These are the built-in functions and include functions such as abs(), dir(), bin(), len(), and many more. If you want to create your own function, you can do so by making use of the def keyword, and we will take a look at this next.

Creating A Python Function

The best way to learn is by doing. Therefore, let us try to create our very own function in Python and learn about what makes one, well, function. So let’s consider creating a function that adds two numbers together and returns the sum. Yes, consider it the “Hello World” of creating a function. On second thought, let’s actually start with the “Hello World” function since it doesn’t even need to accept any parameters and the adding function will.

def hello():
    print('Hello World')
    print('Its me')
    print('I was wondering if after all these years you\'d like to meet')
    print('To go over everything')
    print('They say that time\'s supposed to heal ya')
    print('But I ain\'t done much healing')


## function call output ##
# Hello World
# Its me
# I was wondering if after all these years you'd like to meet
# To go over everything
# They say that time's supposed to heal ya
# But I ain't done much healing

Whoa. We got carried away there – I guess Adele has quite the far-reaching influence these days. In any event, we can see just how we constructed this first function. Note that we start the function definition with the def keyword. If you even opened up a .py file from a popular Python software repository and noticed untold numbers of that keyword, now you know what it is for. It is for defining your functions. Right after the def keyword, we assign a name to identify the function followed by opening and closing parenthesis just like you might find in another programming language. After this, we find the colon : character. This indicates the beginning of the function code block which will house the logic that will get executed when a call is made to this function at a later time. The next few lines look simple, but they are important! In Python Whitespace means something! Before each call to print() there is exactly four blank whitespaces. If you get your whitespace wrong in Python, the compiler will throw an error. This is a bit of a controversial aspect of Python, but if you want to program in this language – you will need to get used to it. After we define the function, we make a call to it by simply typing hello() on its own line with no spaces. We can see from the output, that we may have a future in the music business.

Next up, let’s create that adding function.

def addEmUp(one, two):
    return one + two

result = addEmUp(5,7)


## function call output ##
# 12

This function works just as we expect it might. Notice that in the definition of the function, we make an allowance for two parameters. These represent the two numbers we will pass to our function that it will add together. Then, we simply return the result of the first variable plus the second variable, represented by return one + two. The program output shows us that it is working like a charm. Let’s modify our function to accept user input, as it will be more flexible this way.

def addEmUp():
    one = int(input('What is the first number? '))
    two = int(input('What is the second number? '))
    return one + two

result = addEmUp()


# What is the first number? 1234
# What is the second number? 2345
# 3579

In running this iteration of the adding function, we can see from the program output that the user was prompted for the numbers to add. Something to note is that the built-in input() function is typically going to read data in as a string. This is why we need to wrap the call to input() by an int() call. This converts or casts the input data into an integer. This way, the adding happens in the numeric sense and not the string sense. Let’s see what happens if we do not include this call to int().

def addEmUp():
    one = input('What is the first number? ')
    two = input('What is the second number? ')
    return one + two

result = addEmUp()


# What is the first number? 1234
# What is the second number? 2345
# 12342345

As we can see, that is probably not the result we are looking for!

Python Syntax

Now that we have a few working functions to look at, we can see what the general syntax looks like. It follows this outline as we have demonstrated.

def name-of-function(Parameter(s)):
    statements (function body)
    statements (function body)
    statements (function body)

The function can accept none, one, or many parameters. As we have seen the body of the function has no curly braces as delimiters. The body simply makes use of indentation as part of its definition. If there are required parameters, those are designated first. Optional parameters always come after the required parameters. Within the statements of the function, one can make use of the return keyword to send the result of the function back to the calling location. When you use a return statement, it terminates the function call. There may be times when you do not need to return a value, but simply stop the execution of the function. In this case, if you use return, it will simply return the None value. This equates to something like null in other languages.

Specifying Optional Parameters

Maybe you don’t like saying Hello to World. Perhaps you would like to choose who you are saying Hello to. We can do this with optional parameters.

def hello(world='World'):
    print('Hello ' + world)

# Hello World
# Hello Jackson
hello('J Lo')
# Hello J Lo
# Hello Rico

In this iteration of the hello() function, we make the second portion of the hello message optional. We know it is optional because, in the parameter definition itself, we assign the string of ‘World’ to the world parameter. What this says is, when you call the hello() function, if no argument is passed in, the world variable will be automatically assigned the value of ‘World’. If a value is passed in as an argument, it will overwrite this default value. We see this in action by saying hello to Jackson, J Lo, and Rico.

Adding a Docstring to a Python Function

In one of our prior tutorials, we had a cool little alphabet sorting example. In this iteration, we will turn it into a function, which of course makes the code reusable, while also adding a Docstring, which is almost a form of documentation for explaining what a function actually does. It follows the format of function_name.__doc__ and we will see an example here now.

def alphabetsorter():
    '''Sorts the alphabet from a dictionary!'''
    alphabet = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7,
                'h': 8, 'i': 9, 'j': 10, 'k': 11, 'l': 12, 'm': 13, 'n': 14,
                'o': 15, 'p': 16, 'q': 17, 'r': 18, 's': 19, 't': 20, 'u': 21,
                'v': 22, 'w': 23, 'x': 24, 'y': 25, 'z': 26}
    for key, value in sorted(alphabet.items()):
        print(key, value)
    print('The docstring of the function alphabetsorter is : ' + alphabetsorter.__doc__)


# a 1
# b 2
# c 3
# d 4
# e 5
# f 6
# g 7
# h 8
# i 9
# j 10
# k 11
# l 12
# m 13
# n 14
# o 15
# p 16
# q 17
# r 18
# s 19
# t 20
# u 21
# v 22
# w 23
# x 24
# y 25
# z 26
# The docstring of the function alphabetsorter is : Sorts the alphabet from a dictionary!

How to return more than one value

We have seen that we can return a value from a function. There are times you want to return many values from a function, however. Although you can not explicitly return several different variables representing different values, you can return a list or dictionary which holds the multiple values you wish to return. We see this all the time in other languages by returning an array of values, or an object that has many values. In Python, if you had to return 5 different integer values, you could return a tuple or list which contains these values and access them that way. Let’s have a look at a program that uses a multi-value return now.

def multi_value_return(x, y):
    sum = x + y
    modulo = x % y
    product = x * y
    return (sum, modulo, product)

while True:
    x = int(input("What is the first number?: "))
    if x == 0:
        print('Thanks for playing!')
    y = int(input("What is the second number?: "))
    (sum, modulo, product) = multi_value_return(x, y)
    print("Adding these two numbers equals: " + str(sum))
    print("The modulo of these two numbers is: " + str(modulo))
    print("Multiplying these two numbers is: " + str(product))

# What is the first number?: 8
# What is the second number?: 7
# Adding these two numbers equals: 15
# The modulo of these two numbers is: 1
# Multiplying these two numbers is: 56
# What is the first number?: 0
# Thanks for playing!

This example is pretty cool! First, we define a function that takes two parameters which are integers and calculates the sum, modulo, and product of those two integers. Now, our goal is to return all of those resulting values out of the function. We can’t do something like return sum, return modulo, return the product all in a row – the program will not work. So how do we make this work? The tuple comes to the rescue! Instead of trying to return each value on its own, we return one tuple. Within that tuple, we have the result of each calculation. With this approach, we get to access all three values when we call the function in the while loop as shown above.

How Variable Scope Works in Python

In Python, variable names are local to the function by default. Global variables in Python are accessible both inside and outside of functions. Variables inside of functions are only accessible inside of those functions. Variables and parameters assigned in a function occupy the local scope. A scope is a container of sorts for variables. When variables are assigned outside of functions, they occupy the global scope. There is only one global scope in a program, and it is created upon program initialization. A local scope comes into play any time a function is called. Consider these points about variables in Python:

  • Code in the global scope cannot use any variables in a local scope.
  • Code in a local scope can access variables in the global scope.
  • A nested function’s code can access the local scope of the outer function.
  • You can use the same name for different variables if they are in different scopes.
language = 'JavaScript'

def variablescope():
    language = 'Python'

variablescope() #inside the function language == 'Python'
print(language) #outside the function language == 'JavaScript'
global_language = 'JavaScript'

def variablescope():
    private_language = 'Python'

variablescope()         #JavaScript
                        #the global_language variable can be accessed inside the function

print(private_language) #NameError: name 'private_language' is not defined
                        #an error is thrown, unable to access variable defined
                        #inside the function

We can see the code snippet just above throws a NameError since you can not access a function defined variable in the global scope. If we need to get access to that variable, we can do so simply by declaring that variable as global. This is how we can do that.

global_language = 'JavaScript'

def variablescope():
    global private_language
    private_language = 'Python'

variablescope()         #JavaScript
                        #the global_language variable can be accessed inside the function

print(private_language) #Python
                        #we can now access private_language variable outside of the
                        #function since we declared it as global

Closure in Python

As we have learned in JavaScript, when a nested function reaches outside of its own local scope to access a non-global variable that is part of an outer function, then we have closure. The outer function, however, does not have access to the variables of the inner function. Python makes use of this construct, and we can see an example of this in action right here.

def outer_function():
    outer_variable = 'I live in the outer function'

    def nested_function():
        inner_variable = 'I live in the inner function'
        print('The outer_variable is: ', outer_variable)
        print('The inner_variable is: ', inner_variable)

    print('The outer_variable is: ', outer_variable)
    print('The inner_variable is: ', inner_variable)  # NameError: name 'inner_variable' is not defined


# The outer_variable is:  I live in the outer function
# The inner_variable is:  I live in the inner function
# ************
# The outer_variable is:  I live in the outer function
# NameError: name 'inner_variable' is not defined

How Do Functions Work in Python Summary

Functions allow for the programmer to create short, focused snippets of code that can be reused throughout an entire program. In functions, variables exist in a local scope. Therefore, code in the local scope of one function can’t access variables in the local scope of another function. One exception to this is when a function is nested inside another function. In this case, the inner function does have access to the outer function’s local scope. Functions help organize code into more manageable subgroups of software. They operate in a black box type of manner by accepting particular inputs as parameters and outputs or returns values after computation.