Back in our last article, we learnt how to use files in Python. Now that our scripts start to make sense, they also start to grow bigger. Longer scripts often means more functionality, but more complexity as well. We need a way to handle this complexity, and we can do it with functions. With this Python Functions Tutorial, we will learn what are functions and how to use them. With them, you can create big script and have a clear understanding of what is the purpose of each piece of code.
Python Functions Tutorial: What is a function?
Literally every modern programming language has the concept of functions. This is because functions are fundamentals for programming, you simply can’t write good code without them. Even if you learn them in this python functions tutorial, you will apply these concepts to any language you might want to learn in the future.
We can start with a quick definition.
A function is a piece of code that you can recall multiple times in your script, without rewriting it.
This might not be the best textbook-style definition, but it gives you the point. As soon as you see an example, you will have a clear understanding of that.
An example
The problem…
If you know the problem, you will know the need for a solution. Thus, imagine you want to show to the user a message several times during your script. For example, this could be the code to show the message.
print("WARNING MESSAGE")
print("-----------------------------------------------------------")
print("Before continuing with the execution ensure that all")
print("files are closed. This script will try to read and write")
print("on the files specified, and if they are currently in use")
print("in another program the script may crash and corrupt the")
print("files that has tried to open")
print("-----------------------------------------------------------")
print("")
As you can see, we have 9 lines of code. If we need to use this message multiple times in a script, we need to write it every time. This makes the script longer, and of course harder to read. Furthermore, what if we decide to change the message at some point in the future? We need to search all occurrences in our script and make the changes here. You can see that this is not the way to go.
…and the solution
What if we can tell the script “Hey, play the message here!” instead of writing it every time? This is exactly what a function does. Take a look at the following code.
def show_warning_message():
print("WARNING MESSAGE")
print("-----------------------------------------------------------")
print("Before continuing with the execution ensure that all")
print("files are closed. This script will try to read and write")
print("on the files specified, and if they are currently in use")
print("in another program the script may crash and corrupt the")
print("files that has tried to open")
print("-----------------------------------------------------------")
print("")
You can see that we wrapped our message inside the construct def show_warning_message():
. This is the definition of the function, which takes the name of show_warning_message
. This is user-defined, you can use whatever you want. The keyword def is what tells Python that we are creating a function.
However, if you run the code above nothing will happen. This is because we have just defined the function, we told the script “Whenever I say show_warning_message, do this”. We haven’t said “Ok, now do show_warning_message” just yet.
To execute the code of a function, we simply write its name as below (anywhere in the script).
show_warning_message()
Here we don’t use the keyword def
, because we are using the function – not defining it. Pay also attention to the brackets at the end of the function, in a minute we will explain why they are here.
Python Functions Terminology
After this Python Functions Tutorial, you will be ready to work with functions and truly understand them. Thus, you need to understand the terminology of functions. This can apply to any programming language, not just python.
- You create a function by writing its definition, as we have seen in the example above.
- When you decide to execute the code of a function somewhere in the script, you are calling the function. We refer to the portion of the script that calls a function as the caller.
- The scope defines what are the variables accessible to the function (more on that later).
- Subroutines is another name for functions because they execute code at-will of the main script.
- A global variable is a variable defined in the main script, and not inside a
def
construct.
Ensure you have these five concepts clear. They are extremely important for understanding the most beautiful part of this Python Functions Tutorial.
The Scope of a function
Getting confident with the scope
As from the terminology, you know that a function can work with variables. In fact, you can define variables inside a function, and you can also use variables from outside the function. Try running the following code.
def show_message():
print("Hello, your name is " + name)
name = "Marcus"
show_message()
You will get the message Hello, your name is Marcus
. This is because the function can access the variable name
, which was defined outside the function. You can also define some variables inside the function itself. The following code will give you the same result as the one above.
def show_message():
salutation = "Hello"
print(salutation + ", your name is " + name)
name = "Marcus"
show_message()
So far so easy. Well, not quite. In fact, functions doesn’t have full access to global variables.
Accessing global variables
Now instead of showing a message, we want to use a function to modify the name. Thus, we create a script like the one below.
def change_name():
name = "Albert"
print("I changed the name to: " + name)
name = "Marcus"
print(name)
change_name()
print(name)
Now, we expect to see that the name changes. We print it the first time, and it is “Marcus”, then we change it. The second time we print it, it should be “Albert”, because we re-assigned the value inside the function. Not so easy: look at the output of this code.
C:\Users\aless\Desktop>python myscript.py
Marcus
I changed the name to: Albert
Marcus
We can see that inside the function the name changed. In fact, the line I changed the name to: Albert
is inside the function, and here the name was Albert. However, changes are not retained after function execution. As a result, the second time we print the name we get Marcus.
The truth about the Scope
Whenever you call a function, a local scope is created. It is somehow a protected environment containing the variables for that function. Here you will have the variables defined in the function itself, and a clone of all the global variables. This way, you can access them and read from them, and even modify them during the function, but changes won’t be retained in the original variable, only in the clone. Of course, the clone is destroyed when a function finishes its execution.
Inside a function, you can call another function. If you do that, Python will clone the variables from the scope of the outer function to the scope of the inner function. We can visualize this with an example.
def greet():
if name == "Marcus":
salutation = "Hello"
greet_friendly()
else:
print(salutation + " " + name + ".")
def greet_friendly():
print(salutation + " dear " + name + "!")
salutation = "Good morning"
name = "Marcus"
greet()
With this code, Marcus will get a friendly salutation. The greet()
function will decide to call the greet_friendly()
function after checking the name. We can look at the scope now.
In the picture, we removed all the if/else logic and just shown the scopes. When instantiating the script, we automatically create a global scope. Then, every time we call a function, Python will define a new scope dynamically. Even if we call the same function twice, two Python will define two separate scopes. Each scope inherits
the variables from its parent scopes, the outer boxes.
Parameters and Returns
Until now, we just used functions as placeholders. In fact, the way we used them is similar to the copy-and-paste of their code. This is because they can’t alter their outer scope, so we can just use them to display output.
Furthermore, we have a problem. A function should be independent from the script, self-contained. This means we can’t rely on global variables, because in case a global variable changes name the function will break. We should define everything inside the function. How can we use variables from the script, then? Parameters are here to help.
Function Parameters in Python
For parameters, we have a quite beautiful definition.
Parameters are a feature that allows the caller of the function to initialize some values inside the inner scope of that function.
Take a look at the following function.
def greet():
title = "Mr."
name = "John"
surname = "Doe"
if title != "Mr." and title != "Mrs.":
print("Hello " + name + "!")
else:
print("Good morning, " + title + surname + ".")
greet()
Wouldn’t be awesome if we could define the values for title
, name
and surname
outside of the function, but not as global variables? Parameters are just that. You define a tuple of parameters inside the brackets when defining a function. Don’t get confused about tuples, they are just a list of comma-separated values. We can rewrite the function as below.
def greet(title, name, surname):
if title != "Mr." and title != "Mrs.":
print("Hello " + name + "!")
else:
print("Good morning, " + title + surname + ".")
In this definition, we tell Python that it should expect three values when this function is called. Python will then create the variables title
, name
and surname
inside the scope of the function. At this point, we can initialize their values when calling the function.
greet("Mr.", "John", "Doe")
This will produce the same output as above. However, now we have control over the initialization of variables inside the scope from outside the function. Awesome.
Tricky parameters…
When first approaching programming, parameters can be tricky. This is why we gave you a good explanation of scopes so that you can understand better how parameters work.
When defining a function, you tell it how many values it should expect, and where to store them inside the scope. We don’t tell what is the name of an outside variable to use. For example, we could do this.
variable_1 = "John"
variable_2 = "Doe"
variable_3 = "Mr."
greet(variable_3, variable_1, variable_2)
Python will take the content of variable_3
and put it inside the title
variable in the function’s scope, and so on with the other variables.
Function Returns
With parameters, we can isolate a function from the outer scope. However, we are still limited to output functions that give the results directly to the users. With returns, we can get the results from an elaboration back in our main script. We do that with the return
instruction. When the script reaches that, it stops executing the function and destroy its scope, getting back to its parent scope. While doing that, if you specify a value in the return
, it will give that back to the outer scope.
def calculate_average(list_of_numbers):
if len(list_of_numbers) == 0:
return 0
sum = 0
for num in list_of_numbers:
sum = sum + num
return sum / len(list_of_numbers)
result = calculate_average([1, 4, 14, 18, 11, 3, 0, 1, 15])
print(result)
This is the perfect example. If no numbers are provided (the length of the list is zero), then we return zero straight away. The function will stop on that line and get back to the outer scope. If the user provided some numbers, instead, we go ahead. We initialize a sum
variable and calculate its value by adding al numbers in the list. At the end of the function, we return the result of the division: the average.
In the outside script, we take the result value from the function and store it in the result variable. Then, we print it.
Remember, if your function reaches its end and does not encounter any return
statement, it will implicitly return None
.
Named Parameters and Default Parameters
With that, we covered everything we need to know about functions. However, here we have a few tricks that will help you write code easily.
You can ask the caller of the function to provide all parameters, that’s the default behavior. However, you can also say that some parameters will get a default value if not specified. For example, you can define a function like that:
def greet(name, salutation="Good Morning", extra_message=None):
# code goes here...
In this case, we require only to specify one value, that will go into the name
variable. If the user does not provide the salutation
or the extra_message
, we will set them to their default values (“Good Morning” for the salutation and None for the message). We call the parameters with a default value optional parameters. You just need to follow one rule: you cannot have a mandatory parameter after an optional parameter, so specify all the mandatory parameters first.
What if we want to call this function and define the additional message but not the salutation? We can do that with named parameters. Instead of providing a list of parameters, and let the function put the first in the first variable and so on, we can tell what goes where from the caller’s perspective. We use a similar syntax.
# calling the function
greet("John", extra_message="Welcome home!")
Here we tell the value for the message, and we just omit salutation. Even there, remember that the named parameter goes to the right of traditional parameters.
Best practices like a PRO-grammer
In this Python functions tutorial, we covered the basics of Python functions. At this point you have the power of creating long and complex code, calling several subroutines. However, you can start from the very first moment to write better code following these best practices. As a rule, always remember to write code that anyone can read and understand. If you need to touch your code a few months later, you want to have some code that is easy to understand. Here we have a few tips.
- Function names should tell what they do, be all lowercase characters and underscores. However, they shouldn’t start with an underscore.
- A function must be self-contained. Never reference global variables or variables from outer scopes in general. Use parameters!
- Each function must have a clear purpose and do just one thing. Never have something like
average_and_sum
, have two separate functions instead. Create functions that you can actually use more than once. - Don’t hardcode constants in functions unless they truly are constants (like Pi). If you think they may change, even rarely, depending on the outer scope, use optional parameters.
- Comment your function, so that anyone can understand what is its purpose and the way it works.
Wrapping it up
This is it for our Python Functions Tutorial. Now you have the tools to write good code, that you can re-use and you can maintain. I recommend practicing a lot on functions, as they are a pillar of programming. You need to understand them in order to do implement more advanced items like classes or decorators.
What do you think about functions? How are you going to use them? Let me know your thoughts in the comments!