Pretty much everything that happens in a computer program can be summed up with four key principles. You’ll read about these very soon in the first section of this Chapter. You’ll feel familiar with three of them based on what you’ve read in the first two chapters. Now it’s time to cover the last of the key fundamental topics in coding, which is defining functions.
But before you learn about defining functions you can find out about the four categories of tasks in a computer program.
Store | Repeat | Decide | Reuse
You can think of everything a computer program does to fall into one of four categories:
Let me start by saying that this is an oversimplification. Programming can get more complex than this. But it’s a reasonable simplification to think about as you learn.
Computer programs rely on data. Data could be anything from a single number or name to a large, complex data set. In the Angry Goblin game, the data you needed included:
- the name of the player
- the number of doors in the game
- the position of the goblin
- the player’s guess
- the flag to indicate whether the game is still running, which you use in the
Computer programs store information in variables. You’ve seen two ways of assigning data to a variable. The most basic method is by using
=, which is the assignment operator. You’ve also seen there’s an assignment of data embedded within the
for statement when you write a
It may be tempting to think that storing information is essential but not the most challenging thing in coding. When we talk about more data types in the future, you’ll see that storing data is not always the most straightforward task. It can be the area you’ll need to spend the most time thinking about and planning before you start coding.
One of the primary manifestations of the DRY principle is when you want to repeat some actions several times in a row. You don’t want to repeat things yourself, so you get the computer to repeat them instead. There will be many things that require the repetition of a block of code in computer programs you’ll write. You’ve learned about the
while loops which are the two ways of creating loops in Python.
There are other constructs in Python coding that could fall under the repeat category. However, the two loops are the main ones and the only ones you’ll need to be familiar with for the time being.
Another common action that a computer program does is decide what course of action it needs to take. These are the decisions that only the program can make because they depend on other things happening as the program runs. The
if statement is the Python construct that deals with computer programs making decisions.
However, you’ve used another method that relies on the program making a decision. The
while statement needs to decide whether to execute the code inside the loop or skip it and let the program move on.
Often, you’ll need to perform the same or similar actions in different parts of your computer program or different programs. You’ll know by now that copying and pasting the code wherever you need it is WET. If you decide to make a change to this bit of code or fix an error, you’d have to search for every place you’ve pasted this code to make the change everywhere.
The way to reuse code wherever you need it is to use functions. You’ve used several functions already. Whenever you need to display some information to the user, you call the
print() function, and whenever you want to get a random number, you can call the
randint() function from the
But what if you’ve written some code that performs a specific action that’s useful in your code, and you want to reuse that code whenever you need to? The solution is to define your own functions.
The Finding Names Project
In Chapter 1, you worked on a project through which you learned about several fundamental topics in coding. It’s time for another project to lead you into defining functions. You won’t be coding a game this time. Instead, you’ll start looking at how you can work with data and how to analyse, filter and manipulate data.
Project Description: You have a very long list of names. You need to write code that will help prospective parents choose a name for their child by filtering names based on some simple requirements.
Your first task will be to write code that filters the names based on the first letter in the name. For example, you’ll need to display all the names that start with the letter P.
First, Solve The Problem. Then, Write The Code
A well-known quote in programming is the following one:
First, solve the problem. Then, write the code.John Johnson
This statement may seem obvious, but it highlights two points that are essential when developing a program:
- The distinction between subject-specific knowledge and programming-specific knowledge
- The importance of planning your programming task before starting to write code
What do I mean by subject-specific and programming-specific knowledge? Consider a computer program that simulates the propagation of coherent light through a diffraction grating. I happen to be a physicist with a PhD in Optics, so the physics of how light travels and interacts with objects is a topic I know very well. But not many people will be that well-versed in the subject. I have the subject-specific knowledge required to solve this problem.
However, my knowledge of physics is not sufficient to write the program to create the simulation. Programming-specific knowledge is required to write the code. A physicist who can’t code will not be able to write the simulation, no matter how good their physics knowledge is. And not even the best computer programmer in the world will be able to complete the simulation unless they’re very familiar with the science.
Every programming task has a subject-specific component and a programming-specific component. Even the Angry Goblin game you coded in Chapter 1 required you to understand the game’s rules. Granted, they weren’t too difficult, which means you didn’t have to spend any time thinking about the subject-specific knowledge, but that knowledge was required nonetheless.
The subject-specific part may be very simple or complex, depending on the problem you are trying to solve. If you want to look through a list of numbers to find all those greater than 10, then simple arithmetic is the only subject-specific knowledge that’s required.
Often, to solve your subject-specific problem, you may want to ask yourself the following question: How would I perform this task by hand, using pen and paper, assuming time wasn’t an issue?
Solving the Finding Names problem
If you want to find all the names starting with the letter P from a very long list of names, how would you do it using pen and paper? Imagine you were transported to the 1940s, before the computer era. How would you perform the task? What are the steps you’d need to take?
The key here is to break this task into distinct steps, including the obvious ones. Steps which may seem obvious to us human beings are not obvious to a computer.
Before you read further, get a sheet of paper and a pen or pencil, and try to write down the steps you would need to take. Include as much detail as possible.
You can now read on. Here is a set of steps that can describe this task:
- Look at the first name in the list
- Look at the first letter of that name
- If the letter is the one you want, write the name down in a new list
- If the letter is not the desired one, do nothing
- Look at the second name in the list and then repeat steps 2 to 4
- Indeed, repeat steps 2 to 4 for all the names in the list
Once you have these steps, you’ve solved the subject-specific problem. In this case, the solution wasn’t too complicated, although you have to be careful not to skip any steps that humans might take for granted. Note that the steps listed are simply ideas, written in plain English. You don’t need to be thinking about Python or programming at this stage.
The next step is to translate the above ideas from English—or whatever language you may be thinking in—into Python—or whatever language you may be coding in. Every time you write a computer program, you’ll have to go through this process. Sometimes the task will be easy, and you can complete the planning stage in your head. Other times you’ll need to spend some time making sure you fully understand the subject-specific knowledge and finding a solution to the problem before you start coding.
Translating The Ideas From English To Python
Now that you’ve used your subject-specific knowledge to write the steps you need to solve this problem, you can start applying your programming-specific knowledge.
Your starting point for this project will be a
list containing names as
str. Later in this book you’ll learn how to read in data from external files, but for now this is a good starting point. Here is the list of names you’ll use:
list_of_names = ['Amelia', 'Olivia', 'Emily', 'Alexey', 'Poppy', 'Ava', 'Isabella', 'Jessica', 'Marcus', 'Lily', 'Sophie', 'Grace', 'Vsevolod', 'Sophia', 'Mia', 'Evie', 'Ruby', 'Celim', 'Sumir', 'Ella', 'Scarlett', 'Ruben', 'Isabelle', 'Chloe', 'Cherlin', 'Sienna', 'Masha', 'Freya', 'Phoebe', 'Charlotte', 'Daisy', 'Alice', 'Florence', 'Eva', 'Sofia', 'Millie', 'Lucy', 'Evelyn', 'Elsie', 'Rosie', 'Imogen', 'Lola', 'Matilda', 'Elizabeth', 'Layla', 'Alasdair','Holly', 'Lilly', 'Molly', 'Erin', 'Ellie', 'Maisie', 'Maya', 'Abigail', 'Eliza', 'Georgia', 'Jasmine', 'Esme', 'Willow', 'Leanne', 'Bella', 'Annabelle', 'Keemiya', 'Ivy', 'Amber', 'Emilia', 'Emma', 'Summer', 'Hannah', 'Eleanor', 'Harriet', 'Rose', 'Amelie', 'Lexi', 'Megan', 'Gracie', 'Zara', 'Nuha', 'John', 'Lacey', 'Martha', 'Anna', 'Violet', 'Darcey', 'Maria', 'Maryam', 'Brooke', 'Aisha', 'Katie', 'Leah', 'Heinrich', 'Nour', 'Thea', 'Darcie', 'Hollie', 'Amy', 'Alexandra', 'Stephen', 'Jonathan', 'Penny', 'Mollie', 'Heidi', 'Lottie', 'Bethany', 'Francesca', 'Faith', 'Harper', 'Nancy', 'Beatrice', 'Isabel', 'Juliette', 'Darcy', 'Lydia', 'Sarah', 'Sara', 'Julia', 'Victoria', 'Zoe', 'Robyn', 'Oliver', 'Jack', 'Harry', 'Jacob', 'Charlie', 'Thomas', 'Annabel', 'George', 'Oscar', 'James', 'Ian', 'William', 'Noah', 'Alfie', 'Joshua', 'Yuvraj', 'Muhammad', 'Leo', 'Archie', 'Ethan', 'Joseph', 'Arushi', 'Freddie', 'Samuel', 'Alexander', 'Logan', 'Daniel', 'Isaac', 'Max', 'Mohammed', 'Benjamin', 'Hugo', 'Mason', 'Lucas', 'Edward', 'Harrison', 'Jake', 'Neil', 'Dylan', 'Asher', 'Riley', 'Akash', 'Finley', 'Catherine', 'Theo', 'Muktarsi', 'Sebastian', 'Adam', 'Zachary', 'Arthur', 'Thomas', 'Alberto', 'Toby', 'Jayden', 'Luke', 'Harley', 'Lewis', 'Tyler', 'Harvey', 'Anusha', 'Matthew', 'David', 'Reuben', 'Alok', 'Michael', 'Elijah', 'Kian', 'Tom', 'Mohammad', 'Blake', 'Jean', 'Luca', 'Theodore', 'Stanley', 'Derin', 'Jenson', 'Nathan', 'Nicholas', 'Charles', 'Frankie', 'Constantin', 'Jude', 'Teddy', 'Eric', 'Viren', 'Louie', 'Louis', 'Ryan', 'Hugo', 'Bobby', 'Niamh', 'Anya', 'Elliott', 'Dexter', 'Khai', 'Hariesh', 'Henry', 'Ollie', 'Aron', 'Alex', 'Liam', 'Kai', 'Gabriel', 'Connor', 'Aaron', 'Afrah', 'Frederick', 'Callum', 'Lorcan', 'Elliot', 'Albert', 'Leon', 'Ronnie', 'Rory', 'Jamie', 'Austin', 'Seth', 'Ibrahim', 'Mei', 'Owen', 'Caleb', 'Yousuf', 'Ellis', 'Sonny', 'Devyn', 'Robert', 'Joey', 'Felix', 'Finlay', 'Rossa', 'Ekraj', 'Jackson', 'Jimi', 'Meera', 'Rafi', 'Salahdeen', 'Guido', 'Tanya', 'Karlis']
You’ll have to scroll sideways in the code block to view all the names, as there are quite a few. Although you could copy and paste this code into a new file in your IDE to proceed, there is also another option. As you work your way through this book, you’ll need access to some files. Rather than copy-pasting from here each time, you can download the repository of files you need.
Through the link above, you can download the folder you need directly to your computer. I would recommend this option which is the most straightforward. But if you prefer, you can also access the repository through Github.
NOTE: As the content of The Python Coding Book is currently being gradually released, this repository is not final, so you may need to download it again in the future when there are more files that you’ll need in later chapters.
Making files accessible to your project
The simplest way to make sure you can access a file from your Python project is to place it in the project folder—the same folder where your Python scripts are located. If you’re using an IDE such as PyCharm, you can drag a file from your computer into the Project sidebar to move the file.
Alternatively, you can locate the folder containing your Python scripts on your computer and simply move the files you need in that folder as you would move any other file on your computer.
Tip: In PyCharm, if the Project sidebar is open you can click on the project name (top line) and then show the contextual menu with a control-click (Mac) or right-click (Windows/Linux). One of the options will be Reveal in Finder or Show in Explorer depending on what operating system you’re using.
You’ll need the file
names.py from the file repository for this project. You’ll need to place this file in your project folder.
Your task is to find all names that start with the letter P. The first step is to look at the first name. In the previous chapter, you learned about indexing and how you can extract a single item from a list:
# list_of_names = [...] name = list_of_names print(name)
Note: In all the code blocks for this project, I will not show the first line in which the
list is assigned to
list_of_names. This will avoid the very long first line that affects how the code block is displayed. Instead, the line will be replaced by a comment—the line that starts with a
#. The assignment needs to be present in your code, though.
0 represents the list’s first item, which the program will store in the variable
name. The output from the code above displays the first name in the list:
You now want to retrieve the first letter of this name so that the program can decide whether this is the required letter. The variable
name is a string. When you learned about indexing to access items from a list, I mentioned that indexing works on other data types. Any data types which are sequences can be indexed. A string is a sequence of characters, and you can therefore use the same indexing techniques as you did with lists:
You’re extracting the first item from the string
It’s decision time now. Your program must decide what to do with this name. You’ll therefore need to use an
# list_of_names = [...] name = list_of_names if name == "P": print(name)
Since the first name is Amelia which does not start with the letter P, the program doesn’t output anything. However, you should check that your code does work. You can change the
"P" into an
"A" in the conditional statement to check that the name Amelia is displayed.
If you look back at the steps you wrote in the planning stage, you’ll find that you now need to repeat the same thing for the second name in the list, and then the third and so on. Your program now needs to repeat code. Since you want to repeat code for all the names in the list, whatever happens, you’ll need to use a
It’s possible to use the
for loop with the
range() function and then change the number you’re using when you index
list_of_names to get a different name from the list each time. However, there’s a more Pythonic way of writing this loop. You can iterate directly through the list:
# list_of_names = [...] for name in list_of_names: # name = list_of_names if name == "P": print(name)
name is now defined in the
for statement. Therefore each item in the list will be assigned to it, one at a time. The assignment
name = list_of_names is no longer needed. This action is now being taken directly as part of the
This assignment has been commented out in the code above by adding the hash symbol
# before it. Any code that follows the hash symbol
# is ignored by the computer program and will not be executed.
You can safely delete this line as it doesn’t have any effect on the code. The line is left in place but commented out in the code above to serve as a reminder of the steps you’ve taken to get to this point. The result when this code is run is the following:
Poppy Phoebe Penny
There are only three names that start with the letter P in the long list of names you began with. At the moment, these names are displayed as an output. If you wanted to store these names in the program to use them later on in the code, you could place them in a list.
However, you can’t do this after the
for loop finished iterating through the list. It will be too late by then as these names are not being stored. The program ‘forgets’ that these names are important as soon as the
for loop moves on to the next name.
Therefore, you have to store these names the moment the program identifies them as the names you want to keep. The way you can do this is shown below:
# list_of_names = [...] result =  for name in list_of_names: if name == "P": result.append(name) print(result)
You start by creating an empty list and assign it to the variable
result. This variable is not empty. It has a list that is empty. This difference may seem just a technicality, but it’s an important distinction. The program can now identify
result as data of type
list. Even though the list is empty, it’s still a list.
The other change in the code is the line that follows the
if statement. Instead of printing the name if it starts with a P, you’re adding it to the list:
append() as a function as it’s written in lowercase letters and it’s followed by parentheses. The contents of
name are added to the end of the list
result. The style of this line bears some resemblance to another expression you’ve used before,
random.randint(). However, there is a significant difference.
random refers to a module, a book the program fetches from the library when you use the
import keyword. The name
result refers to a variable which is a storage box where you’re storing a list.
You’ll read more about this structure in the next chapter when you’ll explore data types further. You’ll learn about the actions such as
append() that you can perform on different data types.
The lines of code you’ve written are helpful to filter the list of names and find the names that start with the letter P. You can also find the names that begin with the letter N or A or any letter you wish. This seems like code that you may want to reuse often. It would be convenient if Python had a function called, let’s say,
find_names_starting_with() that you can use whenever you want to filter a list of names based on the first letter of the names. Unfortunately, there is no such function in Python. Not yet, at least.
You can create your own function called
find_names_starting_with(). You can create a new function using the
def keyword, which stands for define:
# list_of_names = [...] def find_names_starting_with(): result =  for name in list_of_names: if name == "P": result.append(name)
In the first line, you’re ‘teaching’ your computer program a new word, and more specifically, a function. You can choose any name you wish for the new function. You should follow modern best practices and choose a name that clearly describes what the function does.
Functions perform an action. Therefore, a function name should start with a verb to make its name clear. By convention in Python, function names are written using lowercase letters and with an underscore separating words. Note the parentheses and the colon to finish this first line.
The indented block of code following the colon is the definition of the new word you’ve just created. These lines are the code that the program will run whenever you use this function.
The function definition teaches the computer program the new word you’ve just created. You’re letting your program know that whenever you type
find_names_starting_with() later on in the program, these are the lines of code you want it to execute.
If you want the function to be executed, you’ll need to call the function. Calling a function is when you write the function name followed by parentheses. The parentheses ask the program to execute the function:
# list_of_names = [...] def find_names_starting_with(): result =  for name in list_of_names: if name == "P": result.append(name) find_names_starting_with()
When you run the code above, the program will run in the following order:
- This line is executed first. It tells the program that you’d like to define a function called
find_names_starting_with(). The function definition is not executed at this time.
- This line is executed first. It tells the program that you’d like to define a function called
- The program skips the indented block in the definition and moves to the next line. The next line is the function call. The program recognises this name as a function since you’ve defined it earlier. In this case, you defined the function in the lines immediately preceding the call, but the definition could have happened much earlier in the code.
- indented block inside function definition
- The program now goes back to where the function was defined, and the code in the function definition is executed.
Try running this code. You’ll get no output from this program. Perhaps it’s because you’re not printing the list. Let’s see what happens when you try to print
# list_of_names = [...] def find_names_starting_with(): result =  for name in list_of_names: if name == "P": result.append(name) find_names_starting_with() print(result)
Running this code will give an error:
Traceback (most recent call last): File "<path>/<filename>.py", line 10, in <module> print(result) NameError: name 'result' is not defined
This error is one you’ve seen before. Python is saying that it has no reference of anything named
result. The computer program looks all over the White Room and cannot find the
result anywhere. But you’re sure you’ve created the variable earlier on in the code. Why can’t the program find it?
Local Variables and Scope
Functions are sometimes referred to as sub-routines. I prefer the term mini-program. A function is a self-contained, small program that the main program can call. Another function or another program can also call it.
Since a function is a self-contained unit of code, any variable created inside the function stays inside the function. These variables are not available outside of it. This fact is why you get the error message saying that
name 'result' is not defined. The variable
result does not exist in the main program area because it only exists inside the function.
This is called the scope. The scope of a variable refers to those parts of a program where the variable exists. A variable created in the main program exists everywhere in the program, including within any functions defined in the program. This is a global variable, and its scope is the whole of the program. But a variable created inside a function is a local variable. Its scope is limited to within the function.
Confused? This topic is not the most straightforward one. And that’s an understatement. We’ll discuss this further later in this Chapter.
Back to the Finding Names project code. You’ve defined a function that created the result you want. The problem is that the variable containing the list with the names required seems to be trapped inside the function. However, when a function finishes all the actions it needs to do, it can take some data back into the main program. You can send data from the function back to the main program using a
# list_of_names = [...] def find_names_starting_with(): result =  for name in list_of_names: if name == "P": result.append(name) return result find_names_starting_with() print(result)
Note the indentation level for the
return statement. It’s part of the
def block but not part of the
if blocks. Therefore it only needs one indent.
You’re not there yet—there’s still an error when you run this code. Let’s see why.
return statement in the function definition tells the function to take whatever data is stored in
result back into the main program where the function was called. Here’s a bit of pedantry: The function does not return the variable but the data contained within that variable. The storage box is not returned to the main program, but only the contents of the box are.
This distinction is the reason why you get the following error when you run the latest version of the code:
Traceback (most recent call last): File "<path>/<filename>.py", line 12, in <module> print(result) NameError: name 'result' is not defined
find_names_starting_with() function did not return the box labelled
result to the main program. Therefore the main program cannot find any reference to
result. The function returned the contents of the box, which is a list containing strings. This list needs to be stored in a new box if you want to keep this information. Otherwise, the information is lost. You need to assign the data that’s returned from the function to a variable:
# list_of_names = [...] def find_names_starting_with(): result =  for name in list_of_names: if name == "P": result.append(name) return result result = find_names_starting_with() print(result)
You have now created a box called
result in the main program, and you’ve used it to collect the data that’s returned from
find_names_starting_with(). This concept is the same as when you collected the data returned from
input() in a variable when you used
input() in the Angry Goblin game.
result within the function and the
result created in the main program to store the data returned from the function are different variables. They are two separate boxes, and there’s no reason they need to have the same label. This code will be clearer if you use a different label for the two boxes:
# list_of_names = [...] def find_names_starting_with(): result =  for name in list_of_names: if name == "P": result.append(name) return result names_p = find_names_starting_with() print(names_p)
You’ll now get an output from this code:
['Poppy', 'Phoebe', 'Penny']
Parameters And Arguments
You’ve converted the code that filters the names based on their first letter into a function. You can now reuse this code whenever you want by calling the function.
You can make this function more flexible. At the moment, the function can only filter names that start with the letter P. It’s not helpful if we’re looking for names beginning with A.
The first line in a function definition is called the function signature. You can rewrite the function signature as follows:
You’re still asking the program to learn a new function name with this line, but you’re now letting the program know that there will be some information in the parentheses when you call the function. This information is stored in a box labelled
letter within the function. The name
letter is called a parameter.
You’ll also need to modify the function call:
names_p = find_names_starting_with("P")
When you call a function, the data you place in the parentheses are called arguments. The distinction between parameters and arguments is subtle but important. The parameter name is the name used in the function definition to refer to data. The argument is the actual value passed into the function when it’s called. In this example:
- The parameter is
- The argument is
When a function is called, a box is created in the function labelled with the parameter name. The value passed as an argument is put inside the box. Sounds familiar? The parameter name becomes a variable within the function.
Note how choosing a good name for the function can make a big difference to how readable your code is. Reading out the function call, you get Find names starting with P, which clearly describes the action performed by the function.
There’s one more change you need to make to the function definition:
# list_of_names = [...] def find_names_starting_with(letter): result =  for name in list_of_names: if name == letter: result.append(name) return result names_p = find_names_starting_with("P") print(names_p)
You no longer need to check whether
name is equal to the string
"P". Instead, you can use the label of the box. This label is the parameter name. You can now call the function as often as you wish, using different input arguments:
# list_of_names = [...] def find_names_starting_with(letter): result =  for name in list_of_names: if name == letter: result.append(name) return result names_p = find_names_starting_with("P") print(names_p) names_a = find_names_starting_with("A") print(names_a) names_b = find_names_starting_with("B") print(names_b)
You’ve called the function three times with
"B" as input arguments, and you’ve assigned the lists returned by each function call to different variables. The output shows all three lists:
['Poppy', 'Phoebe', 'Penny'] ['Amelia', 'Alexey', 'Ava', 'Alice', 'Alasdair', 'Abigail', 'Annabelle', 'Amber', 'Amelie', 'Anna', 'Aisha', 'Amy', 'Alexandra', 'Annabel', 'Alfie', 'Archie', 'Arushi', 'Alexander', 'Asher', 'Akash', 'Adam', 'Arthur', 'Alberto', 'Anusha', 'Alok', 'Anya', 'Aron', 'Alex', 'Aaron', 'Afrah', 'Albert', 'Austin'] ['Bella', 'Brooke', 'Bethany', 'Beatrice', 'Benjamin', 'Blake', 'Bobby']
You can get more practice by writing another function,
find_names_of_length(), which filters the names in the list based on the number of letters in the name. You can try writing this function, but first, you’ll need to learn another built-in function,
>>> my_numbers = [4, 6, 12, 9, 6, 23] >>> len(my_numbers) 6 >>> name = "Stephen" >>> len(name) 7
len() returns the length of any data type that’s a sequence. When you use it on a list, it will return the number of items in the list, and when you use it on a string, it returns the number of characters.
Before you carry on reading, try to write the function
Have you finished your attempt? You can read on.
Here’s the code showing both functions you’ve written so far:
# list_of_names = [...] def find_names_starting_with(letter): result =  for name in list_of_names: if name == letter: result.append(name) return result def find_names_of_length(length): result =  for name in list_of_names: if len(name) == length: result.append(name) return result names_p = find_names_starting_with("P") print(names_p) names_9 = find_names_of_length(9) print(names_9)
This code displays the following output:
['Poppy', 'Phoebe', 'Penny'] ['Charlotte', 'Elizabeth', 'Annabelle', 'Alexandra', 'Francesca', 'Alexander', 'Catherine', 'Sebastian', 'Frederick', 'Salahdeen']
The output shows two lists. The first list has all names starting with P, and the second list has all names with
9 letters. What if you want to find all names that start with E and are six letters long? You could write another function that combines both features, which you may call
find_names_starting_with_and_of_length(letter, length). However, there’s a better way.
One of the principles you should remember when defining functions is that a function should perform only one action. When you describe what a function does, there should be no and in that description as the function should perform only one task. In the next section, you’ll extend the functions you’ve defined above to make them even more useable and flexible than they already are.
More Parameters, More Arguments
Earlier in this Chapter, you’ve seen that when you define a variable inside a function definition, the variable is only available within the function. It’s not accessible from the main program. In Python, the reverse is not true. You have used the variable
list_of_names inside the function definitions even though you created this variable in the main program. Although you can do this, there’s a better way.
You can define your function so that it doesn’t have to rely on a variable that exists in the main program. Instead, you can send all the information needed by a function when you call it.
You can modify your functions as follows:
# list_of_names = [...] def find_names_starting_with(letter, names): result =  for name in names: if name == letter: result.append(name) return result def find_names_of_length(length, names): result =  for name in names: if len(name) == length: result.append(name) return result
Each function has two parameters now. In the parentheses in the first line of both function definitions, you’ve added a parameter which you’ve called
names. When you call the function, you need to pass two separate bits of information now instead of one.
Since within the function definitions, the name of the list containing the names is no longer
list_of_names but the parameter
names, you’ll need to modify the
for statements as well.
When you call
find_names_starting_with(), you’ll have to pass a string with the letter you want to filter with and the list of names you want to use. With
find_names_of_length(), the two arguments are the length required and the list of names to use.
You can now call the functions and print the lists that each function returns:
# list_of_names = [...] def find_names_starting_with(letter, names): result =  for name in names: if name == letter: result.append(name) return result def find_names_of_length(length, names): result =  for name in names: if len(name) == length: result.append(name) return result names_p = find_names_starting_with("P", list_of_names) print(names_p) names_9 = find_names_of_length(9, list_of_names) print(names_9)
The output from this code is the same as for the version in the previous section. However, this latest version of the function definitions makes these functions a lot more flexible as you can now use them with any list the has names in it and not just the one created at the top of the program.
Let’s try and find all names that start with an E and that are six letters long. You can first call
find_names_starting_with() in the same way as you did earlier:
# list_of_names = [...] def find_names_starting_with(letter, names): result =  for name in names: if name == letter: result.append(name) return result def find_names_of_length(length, names): result =  for name in names: if len(name) == length: result.append(name) return result names_e = find_names_starting_with("E", list_of_names) print(names_e)
All the names starting with E are displayed:
['Emily', 'Evie', 'Ella', 'Eva', 'Evelyn', 'Elsie', 'Elizabeth', 'Erin', 'Ellie', 'Eliza', 'Esme', 'Emilia', 'Emma', 'Eleanor', 'Ethan', 'Edward', 'Elijah', 'Eric', 'Elliott', 'Elliot', 'Ellis', 'Ekraj']
You can now call the
find_names_of_length() function. However, instead of passing the full list as an argument, you can pass
names_e. You’re using the result of one function as an input for the next:
# list_of_names = [...] def find_names_starting_with(letter, names): result =  for name in names: if name == letter: result.append(name) return result def find_names_of_length(length, names): result =  for name in names: if len(name) == length: result.append(name) return result names_e = find_names_starting_with("E", list_of_names) names_e_and_6 = find_names_of_length(6, names_e) print(names_e_and_6)
The output shows a list with all the names starting with E that are six letters long:
['Evelyn', 'Emilia', 'Edward', 'Elijah', 'Elliot']
Functions are at their best when you can use them in a flexible way. They are a way of packaging code so that you can reuse it whenever needed. Functions are a key component for writing DRY code. Defining functions is probably one of the most powerful tools in modern programming.
Error Messages When Calling Functions
The signature of the
find_names_starting_with() function now looks like this:
def find_names_starting_with(letter, names):
This line tells your computer program that there’s a function called
find_names_starting_with and that when you call this function, you’ll supply two bits of information as arguments in the parentheses. These are required arguments. Let’s see what happens if you try to call the function without these required arguments:
names_e = find_names_starting_with()
This line gives the following error messages:
Traceback (most recent call last): File "<path>/<filename>.py", line 1, in <module> names_e = find_names_starting_with() TypeError: find_names_starting_with() missing 2 required positional arguments: 'letter' and 'names'
You’ll get a similar error if you pass only one of the two required arguments:
names_e = find_names_starting_with("E")
The error message now complains about the
1 missing argument:
Traceback (most recent call last): File "<path>/<filename>.py", line 1, in <module> names_e = find_names_starting_with("E") TypeError: find_names_starting_with() missing 1 required positional argument: 'names'
The two error messages mention missing
required positional arguments. You’ve seen why they’re referred to as required. You’ll learn about optional arguments later on.
The arguments are also described as positional because there are different ways you can pass information into the function. In the function call you’ve used so far, the data are assigned to a parameter based on the position of the arguments in the parentheses:
names_e = find_names_starting_with("E", list_of_names)
"E" is the first argument, and therefore it’s assigned to the parameter
letter. The variable
list_of_names is the second argument, and the contents of this box are assigned to the parameter
However, you can choose to name the arguments in a function call:
names_e = find_names_starting_with(letter="E", names=list_of_names)
These are called keyword arguments. You’re using the parameter name to identify the arguments. When you use keyword arguments, the order in which you include the arguments in a function call is not important:
names_e = find_names_starting_with(names=list_of_names, letter="E")
This function call works perfectly even though the order of the arguments is not the same as the order of the parameters in the function signature.
You can use a mixture of positional and keyword arguments. However, the positional arguments must come first. The following line is valid:
names_e = find_names_starting_with("E", names=list_of_names)
"E" is a positional argument, whereas
list_of_names is passed as a keyword argument. However, if you try to pass a keyword argument first followed by a positional argument, you’ll get an error:
names_e = find_names_starting_with(letter="E", list_of_names)
The error message is the following one:
File "<path>/<filename>.py", line 1 names_e = find_names_starting_with(letter="E", list_of_names) ^ SyntaxError: positional argument follows keyword argument
The error message shows that this is a
SyntaxError. You can think of syntax as the grammar of the coding language. The error clearly states that the problem here is that the
positional argument follows keyword argument.
You’ll get errors often when you’re writing code. Even proficient coders get errors. Learning to recognise common error messages and understanding them is an important skill to learn and master. Unfortunately, not all error messages are clear, but they will always give you a clue about the problem.
The White Room Revisited
The previous Chapter introduced you to The White Room analogy. The White Room represents the computer program. There’s a set of shelves that include:
- The ‘built-in’ booklet with all the functions and keywords that every program has access to automatically
- Books brought from the library using the
- Boxes with labels and some content inside the boxes. These boxes represent variables
When you type any word in your code, the computer program looks around the White Room to find a reference to that name. What about new functions you define? How do these fit in this analogy?
I introduced functions as mini-programs earlier in this Chapter. A function is a self-contained unit that performs a specific action, not unlike an entire computer program. For this reason, you can think of a function like another room with a specific purpose.
When you define a function within a program, you’re creating a new room adjacent to the White Room, with a door connecting the two. The name you give the function is the label you put on the door of the Function Room. When you type a function’s name, the computer program will look around the White Room and find a reference to this name as a label on a door leading to another room.
When you call a function:
- The program leaves the White Room and goes through the door leading to the Function Room
- It performs whatever actions are needed in the Function Room
- The program returns to the main White Room when it completes these tasks. It shuts the door to the Function Room on the way out
Let’s use one of the function definitions from the Finding Names project to develop this analogy further:
def find_names_starting_with(letter, names): result =  for name in names: if name == letter: result.append(name) return result
result is a local variable in the function. When you define a variable created inside a function definition, the box that’s created is placed on the shelves inside the Function Room. This is why this variable is not accessible from the main program. It’s not present in the White Room but only in the Function Room.
Parameters and Arguments
At the entrance of the Function Room, there are two empty boxes ready to be used. They’re labelled
names. The parameters are empty storage boxes ready to be filled in as soon as the program enters the Function Room.
When you call a function, the arguments are taken to the Function Room and put inside the parameter boxes. These boxes are then placed on the shelves in the Function Room. They’re ready to be used when needed in the function:
names_e = find_names_starting_with("E", list_of_names)
This function call leads to the following steps:
- The program finds a door labelled
find_names_starting_withthat leads to a Function Room
- It ‘picks up’ a
strcontaining the letter
- It locates the box
list_of_nameswhich it finds in the White Room. It doesn’t take the box but only its contents
- The program then goes through the door labelled
find_names_starting_withinto the Function Room, taking the information it collected along with it
- As soon as the program enters the Function Room, it will find the two empty parameter boxes waiting to be filled. The data the program takes into the Function Room are placed in these boxes
You’ve seen how variables created within the function are local to the function. The box only exists in the Function Room.
When the function completes its actions, the program is ready to leave the Function Room and return to the White Room. Before it leaves the Function Room, it takes some information along with it back to the White Room. The
return statement determines what these data are.
find_names_starting_with Function Room, there’s a box labelled
result. This variable is included in the
return statement. When the program finishes from the Function Room and is ready to return to the main White Room, it doesn’t take the
result box back with it, but only its contents. This subtle but important distinction is why the main program cannot use the variable
result. There’s still no box labelled
result in the White Room.
However, the function call in the main program has an assignment statement:
names_e = find_names_starting_with("E", list_of_names)
You’re now creating a box in the main White Room labelled
names_e and putting whatever the program brought back from the Function Room into this box. The data returned from the function is now available to be used in the main program.
When a function finishes all its actions:
- It collects the data from the boxes named in the
- It leaves the Function Room and takes the data along with it to the White Room
- Once in the White Room, the program creates a new box and stores the data it brought from the function in this box
Even functions that you didn’t write yourself are Function Rooms your program can access. When you type
print(), the program finds a reference to this function in the ‘built-in’ booklet. The booklet provides a map of where the
The map showing the program where to find the
randint() Function Room is in the book named
random, which you brought from the library with the
A typical computer program will start in the main White Room and will then move across several other rooms, going in and out, performing actions, and moving data from one room to the next.
In this Chapter, you’ve covered:
- How functionality in a program can be categorised as Store, Repeat, Decide, and Reuse
- How to define your own functions
- What’s the difference between local and global variables
- How to return data from a function
- How to include parameters when defining a function
- How to use positional and keyword arguments
Defining functions is a powerful tool in programming. It allows you to write code that you can reuse in a flexible way as and when you need to. Another key component of all computer programs is the data that’s used and created. In the next Chapter you’ll learn more about data and data types.
Sign-Up For Updates
This site was launched in May 2021. The main text of the book is now almost complete—-a couple of chapters are still being finalised. Blog posts are also published regularly.
Sign-up for updates and you’ll also join the Codetoday Forum where you can ask me questions as you go through this journey to learn Python coding.