Introduction to Python
This textbook is used by CIS 115, CC 111, CC 711, and the Cyber Pipeline outreach program to teach basic Python programming to first-time programmers.
This textbook is used by CIS 115, CC 111, CC 711, and the Cyber Pipeline outreach program to teach basic Python programming to first-time programmers.
Learning to program involves learning not only the syntax and rules of a new language, but also a new way of thinking. Not only do we want to write code that is understandable by the computer, we must also understand what the computer will do when it tries to run that code.
A key part of that is learning to think like a computer thinks. We sometimes call this our “mental model” of a computer. If we have a good “mental model” of a computer, then we can learn to read code and mentally predict what it will do, even without ever running it on an actual computer. This ability is crucial for programmers to develop.
So, as we work through this lab, we’ll need to constantly pay attention to how a real computer works, and make sure that our “mental model” is accurate. If not, we’ll have to adapt our understanding of a computer to match the real world. This process of adaptation and accommodation is an important part of learning to program - we have to have a good understanding of what a computer actually does when it runs our code, or else we won’t be able to write code that will do what we want it to do.
We’ve chosen Python because it is very easy to learn, easy to use, and it is used in many different places, from scientific computing and data analysis to web servers and even artificial intelligence. It is a very useful language to learn, and it makes a great first programming language.
As we learn to write code in a real programming language, it helps to refer to the actual documentation from time to time. So, we recommend bookmarking the official Python Documentation as a great place to start. Throughout this course, we may also include links to additional resources, and those are also worth bookmarking. One of the best parts about programming is that nearly all of the documentation is online and easily accessible, and learning how to quickly search for a particular solution or reference is just as useful as knowing how to do it from memory. In fact, most programmers really only know the basics of the language and a few handy tricks, and the rest of it is just reading documentation and learning when to use it. So, don’t worry about remembering it all right from the start - instead, learn to read the documentation and use the tools that are available, and focus on understanding the basics of the language’s syntax and rules. Let’s get started!
First, let’s start with the basics of writing Python code in a file and running those files. This is the first major step toward actually writing a real program, but it can definitely be difficult the first time without prior experience to rely on. So, let’s go through it step by step and make sure we know how to run our programs in Python.
At this point, we should already have Python installed on our system. For students using Codio, this is taken care of already. For students using their own computers, refer to an earlier lab to find instructions for installing Python on your system, or contact the instructors for assistance.
To make sure that Python is installed and working, we’ll need to open a terminal in our operating system. In Codio, this can be found by clicking the Tools menu at the top, and then choosing Terminal. More information can be found in the Codio Documentation. There may also already be one open for you if you are reading this content from within Codio.
If you are working on your own computer, you’ll need to open a terminal on your operating system. This should have been covered in the previous lab when you installed Python. On Windows, look for Windows Terminal or Windows PowerShell (not the old Command Prompt, which requires different commands). On Mac or Linux, look for an application called Terminal. Throughout this course, we’ll call these windows the terminal, even though they may have slightly different names in each operating system.
Once we have the terminal open, we should see something like one of these examples:
At this point, we should see a place with a blinking cursor, where we can type our commands. This is called the command prompt in the terminal. The first thing we can do is check to make sure Python is properly installed, and we can also confirm that it is the correct version. To do this, we’ll enter the following command and press enter to execute it:
python3 --version
Hopefully, that command should produce some output that looks like this:
Here, we see that the currently installed Python version is 3.10.6. As long as your Python version number begins with a 3, you have correctly installed Python and are able to run it from the terminal. So, we can continue to the next part of this lab.
If you aren’t able to run Python or aren’t sure that you have the correct version, contact the instructor for assistance!
Notice that we have to use the command python3
, including the version number, instead of the simpler python
command here. This is because some systems may also have Python version 2, an outdated version of Python, installed alongside version 3. In that case, the simple python
command will be Python version 2, while python3
will be Python version 3. Unfortunately, most programs written in Python 3 will not run properly in Python 2, so it is important for us to make sure we are using the correct Python version when running our programs.
Thankfully, the command python3
should always work, and using that command is a good habit to learn. So, throughout this course, we will use the command python3
to run Python programs.
When we open a terminal, it will usually start in our user’s home folder. This may mean different locations for different operating systems. For example, if our current user’s name is <username>
, the terminal will usually start in this location for each operating system:
C:\Users\<username>
/home/<username>
/Users/<username>
/home/codio/workspace
The directory that is open in the terminal is known as the working directory. We can use the pwd
command to determine what our current directory is:
In this example, we are looking at the Codio terminal, so our working directory is /home/codio/workspace
.
Next, we can see the files and directories contained in that directory using the ls
command. Here’s the output of running this command in Codio:
In the output, we can see that there is a file named README.txt
and a directory named python
. In Codio, we’ll place all of our files in the python
directory, so we can open that using the cd python
command:
Notice how the python
directory is now included in the command prompt in the terminal. Most terminals will show the working directory in the command prompt in some way.
That’s the basics of navigating in the terminal, but there is much more to learn. If you’d like to know more, consider checking out some of the resources linked below!
Most of the content in this course will focus on using the commands that are present in the Linux terminal, since they are the most widely-used commands across all platforms. In general, these commands should work well on both Windows and Mac, with a few caveats:
Fully learning how to use these tools is outside of the scope of this class, but we need to know enough to navigate the filesystem and execute the python3
command. If you need assistance getting started with this step, your best bet is to contact the instructors. Each student’s computer is a bit different, so it is difficult to cover all possible cases here.
Before we learn to write our first program, let’s start by introducing some important vocabulary terms:
""
in our code and elsewhere.We’ll use these terms to help us understand different parts of the code in our programs. Now that we’ve covered them them, we can start to discuss the various statements in Python.
The first statement that we’ll cover in the Python programming language is the print(expression)
statement. This statement is used to display output to the user via the terminal. So, when Python runs this statement, it will evaluate the expression
to a single value, and then print that value to the terminal.
For example, the simplest Python code would be a simple Hello World program, where we use the print(expression)
statement to display the text "Hello World"
to the user:
print("Hello World")
Notice that the expression
part of the statement contains "Hello World"
in quotation marks? That is because "Hello World"
is text, so we should put it in quotes and make it into a string in our code. Also, since the string "Hello World"
can be treated like a value, we can also say it is an expression, and therefore we can use it in the expression
part of the statement. This may seem pretty straightforward now, but as our programs become more complex it is important to think about what pieces of code can be treated as values, expressions, and statements.
When we run that program in Python, we’ll see the following output:
Hello World
If you look at the output, you might notice something strange - the text on our user interface doesn’t include the quotation marks ""
that the expression "Hello World"
contained. When we display text to the user, Python will remove the quotation marks from the beginning and the end of the string, and just display the text inside. Pretty handy!
Let’s go through the full process of writing and running that program in Python!
The first step to create a program in Python is to create a text file to store the code. This file should have the file extension .py
to indicate that it is a Python program. So, we’ll need to create that file either on our computers or in Codio or another tool if we are using one. For example, in Codio we can create the file in the python
folder by right-clicking on it and selecting the New File option. We’ll name the file hello.py
:
Once we’ve created that file, we can then open it by clicking on it. In Codio and in other online tools, it will open in the built-in editor. On a computer, we’ll need to open it in a text editor specifically designed for programming. We recommend Visual Studio Code, which is available on all platforms. Tools like the built-in Notepad tool on Windows, or a word processor like Word or Pages do not work for this task.
In that file, we’ll simply place the code shown above, like this:
That’s all there is to it!
Once we’ve written the code, we can open the Terminal and navigate to where the code is stored in order to run the program. On Codio, we’ll just use the cd python
command to enter the python
directory. On a computer, we’ll need to navigate the file system to find the location where we placed our code. We highly recommend creating a folder directly within the home directory and placing all of our code there - that will make it easy to find!
Once we are in the correct directory, we can use the ls
command to see the contents of that directory. If we see our hello.py
file, we are in the correct location:
If we don’t see our file, we should make sure we’ve saved it and that our current working directory is the same location as where the file is stored.
Finally, we can execute the file in Python using the python3
command, followed by the name of the file:
python3 hello.py
If everything works correctly, we should see output similar to this:
There we go! We’ve just run our first program in Python! That’s a great first step to take.
Of course, there are lots of ways that this could go wrong. So, if you run into any issues getting this to work, please take the time to contact an instructor and ask for assistance. This process can be daunting the first time, since there are so many things to learn and so many intricacies we simply don’t have time to cover up front. Don’t be afraid to ask for help!
One important key to remember: by default, the print(expression)
statement in Python will add a newline to the end of the output by default. This means that multiple print(expression)
statements will print on multiple lines. Let’s look at some examples!
Throughout this course, we’ll show many different code examples and their output here in the lab. To test them out, feel free to copy the code examples to a Python file and run it yourself. You can even tweak them to do something new and see how Python interprets different pieces of code. In the end, the best way to learn programming is to explore, and running these examples on your own is a great way to get started!
First, let’s write a simple program that prints 4 letters separated by spaces:
print("a b c d")
Just like our “Hello World” program, when we run this program, we’ll see that string printed in the user interface:
a b c d
Ok, that makes sense based on what we’ve previously seen. The print(expression)
statement will simply display any string expression in our user interface.
Of course, programs can consist of multiple statements or lines of code. So, what if we write a program that contains multiple print(expression)
statements, like this one:
print("one")
print("two")
print("three")
print("four")
What do you think will happen when we try to execute this program on our “mental model?” Have we learned a rule that tells us what should happen yet? Recall on the previous page we learned that it will print the value on the user interface, but that’s it. So, when we execute this program, we’ll see the following output:
one
two
three
four
That’s a very interesting result! We can see that four separate print(expression)
statements will generate four lines of output by default. Each statement is printed on its own line. So, in Python we can print multiple lines of output simply by using multiple print(expression)
statements!
What if we want to print output on multiple lines using a single print(expression)
statement? How can we do that? In this case, we need to introduce a special symbol, the newline symbol. In most programming languages, the newline symbol is represented by a backslash followed by the letter “n”, like \n
, in a string. When our user interface sees a newline symbol, it will move to the next line before printing the rest of the string. The newline symbol itself won’t appear in our output.
For example, we can update our previous program to contain newline symbols between each letter:
print("a\nb\nc\nd")
This might be a bit difficult to read at first, but as we become more and more familiar with reading code, we’ll start to see special symbols like the newline symbol just like any other letter. For now, we’ll just have to read closely and make sure we are on the lookout for special symbols in our text.
When we run this program, we should see the following output on our user interface:
a
b
c
d
There we go! We’ve now figured out how to print text on multiple lines using a single print(expression)
statement.
What if we want to display multiple print(expression)
statements on the same line? To do that, we must add an additional option to the print(expression)
statement - the end
option.
For example, the following code will produce output all on the same line:
print("Hello ", end="")
print("World!")
In this example, we set end
to be an empty string ""
. When we run this program, we’ll get the following output:
Hello World!
In fact, in Python, the print(expression)
statement is an example of a function in Python. Functions are just repeatable procedures in our code - when we use them, we write the name of the function, followed by a set of parentheses and then inputs, or “arguments,” separated by commas within the parentheses. This is known as “calling” a function. So, in actuality, the expression
in the print(expression)
statement is just the first argument when we call the print
function.
Therefore, the end
option that we showed above is just a second argument that is optional - it simply lets us choose what to put at the end of the output. By default, the end
parameter is set to the newline symbol \n
, so if we don’t provide an argument for end
it will just add a newline at the end of the value.
We can set the value of end
to be any string. If we want to include a space at the end of the output, we can add end=" "
to the print
function call.
In this course, we won’t spend much time talking about optional parameters and default values in Python functions, but it is important to understand that statements like print
are actually just Python functions behind the scenes!
Let’s try some simple practice problems. These problems are not graded - they are just for you to practice before doing the real exercises in the lab itself. You can find the answers below each question by clicking the button below each question.
What is the output that is displayed to the user after running the following Python code:
print("Kansas State University")
print("Manhattan ", end="")
print("KS", end=" ")
print("66506")
print("KSU!\nGo Cats!")
We want to write a program that produces the following output:
*
* *
* * *
Rearrange the following Python statements to create a program that produces that output. You may not use all of these statements in your answer.
print("*")
print("*")
print("*")
print("*", end=" ")
print("*", end=" ")
print("*", end=" ")
Write a Python program that will display the last five letters of the alphabet, one per line.
Now that we’ve learned how to use the print(expression)
statement, let’s focus on the next major concept in Python, as well as any other programming language: variables.
The word variable is traditionally defined as a value that can change. We’ve seen variables like $ x $ used in Algebraic equations like $ x + 4 = 7 $ to represent unknown values that we can try to work out. In programming a variable is defined as a way to store a value in a computer’s memory so we can retrieve it later. One common way to think of variables is like a box in the real world. We can put something in the box, representing our value. Likewise, we can write a name on the side of the box, corresponding to our variable’s name. When we want to use the variable, we can get the value that it currently stores, and even change it to a different value. It’s a pretty handy mental metaphor to keep in mind!
In a later lab, we’ll learn how to use operators to manipulate the values stored in variables, but for right now we’re just going to focus on storing and retrieving data using variables.
To use a variable, we must first create one. In Python, we create a variable in a special type of statement called an assignment statement. The basic structure for an assignment statement is a = expression
. When our Python interpreter runs this statement, it will first evaluate expression
into a single value. Then, it will store that value in the variable named a
.
For example, let’s consider the Python statement:
x = "Hello World"
In that statement, we are storing the string value "Hello World"
in the variable named x
. Pretty handy!
Now, let’s cover some important rules related to assignment statements:
expression = x
in programming like we can in math. In mathematical terms, this means an assignment statement is not commutative.Once we’ve created a variable, we can use it in any expression to recall the current value stored in the variable. So, we can extend our previous example to store a value in a variable, and then use the print(expression)
statement to display it’s value. Here’s what that would look like in Python:
x = "Hello World"
print(x)
Notice that we don’t put quotation marks "
around the variable x
in the print(expression)
statement. This is because we want to display the value stored in the variable x
, not the string value "x"
. So, when we run this code, we should get this output:
Hello World
To confirm, feel free to try it yourself! Copy the code above into a Python file, then use the python3
command in the terminal to run the file and see what it does. Running these examples is a great way to learn how a computer actually executes code, and it helps to confirm that your “mental model” of a computer matches how a real computer operates.
Python also allows us to change the value stored in a variable using another assignment statement. For example, we can write some Python code that uses the same variable to print multiple outputs:
a = "Output 1"
print(a)
a = "Output 2"
print(a)
When we run this code, we’ll see the following output:
Output 1
Ouptut 2
So, just like we learned above, when we evaluate a variable in code, it will result in the value currently stored in that variable at the time it is evaluated. So, even though we are printing the same variable twice, each time it is storing a different value. Recall that this is why we call items like a
a variable - their value can change!
Finally, Python has a few simple rules that determine what names can be used for variables in our code. Let’s quickly review those rules, as well as some conventions that most Python developers follow when naming variables.
First, the rules that must be followed:
_
._
.Next, here are the conventions that most Python developers follow for variable names, which we will also follow in this course:
_
have a special meaning. So, we won’t create any variables beginning with an underscore right now, but later we’ll learn about what they mean and start using them._
, as in number_of_inputs
tmp
or temp
are temporary variables.i
, j
, and k
are iterator variables (we’ll learn about those later).x
, y
, and z
are coordinates in a coordinate plane.r
, g
, b
, a
are colors in an RGB color system.print
statement, so we should not name a variable print
in our language.These conventions are not strict requirements enforced by the Python language itself, but they are general rules to help us write code that is meaningful and easy to read.
Finally, don’t forget that some of the code examples in this course will NOT follow these conventions, mainly because long, descriptive variable names might give away the purpose of the code itself. We’ll still follow the rules that are required, but in many cases we’ll use simple variable names so that the focus is learning to read the structure of the code, not inferring what it does based solely on the names of the variables.
As we learn to write more complex programs in Python, it is important to make sure we can still mentally execute the code we are writing in our “mental model” of a computer before we actually run it on a computer. After all, if we don’t have at least an idea of what the code actually does before we write it, we really haven’t learned much about programming!
Thankfully, when working in a real programming language such as Python, there are many tools to help us visualize how the code works when we run it. This helps us continue to develop our “mental model” of a computer by looking behind the scenes a bit to see what is happening when we run our code.
One such tool is Python Tutor, a website that can quickly run short pieces of Python code to help us visualize what each line does and how it works. This tool is also integrated directly into Codio!
Let’s look at an example of how Python Tutor compares to the manual code traces we performed in a previous lab. For this example, we’re going to use the following code:
x = "Hello"
y = x
print(y, end=" ")
x = "World"
print(x, end=", ")
print(y)
This visualization can be found by clicking this Python Tutor Link to open Python Tutor on the web.
The initial setup for Python Tutor is shown in the image below:
This looks similar to the setup we used when performing code tracing with pseudocode. We have an arrow next to our code that is keeping track of the next line to be executed, and we have areas to the side to record variables and outputs. In Python Tutor, the variables are stored in the Frames section. We’ll learn why that is important later in this lab when we start looking at Python functions.
So, let’s click the Next > button once to execute the first line of code. After we do that, we should see the following setup in Python Tutor:
That line is an assignment statement, so Python Tutor added an entry in the Frames section for the variable x
, showing that it now contains the string value "Hello"
. It placed that variable in a frame it is calling the “Global frame,” which simply contains variables that are created outside of a function in Python.
When we click the Next > button again, we should see this:
Once again, the line that was just executed is an assignment statement, so Python Tutor will add a new variable entry for y
to the list of variables. It will also store the string value "Hello"
. Just like before, notice that the variable y
is storing the same value as x
, but it is a copy of that value. The variables are not connected in any other way.
We can click Next > again to execute the next line of code:
Once this line is executed, we’ll see that it prints the value of the variable y
to the output. Python Tutor will look up the value of y
in the Frames section and print it in the output, but it won’t evaluate the expression in the code like we did when we performed code tracing in pseudocode. It’s a subtle difference, but it is worth noting.
Once again, we can click Next > to execute the next assignment statement:
This statement will update the value stored in the variable x
to be the string value "World"
. After that, we can run the next statement:
That statement prints the value of x
to the output, followed by a comma ,
and a space as shown in the end
argument provided to the print
function. Finally, we can click Next > one more time to execute the last line of code:
This will print the value of y
to the output. Once the entire program has been executed, we should see the output Hello World, Hello
printed to the screen.
The full process is shown in the animation below:
Using tools like Python Tutor to step through small pieces of code and understand how the computer interprets them is a very helpful way to make sure our “mental model” of a computer accurately reflects what is going on behind the scenes when we run a piece of Python code on a real computer. So, as we continue to show and discuss examples in this course, feel free to use tools such as Python Tutor, as well as just running the code yourself, as a great way to make sure you understand what the code is actually doing.
Let’s try some simple practice problems. These problems are not graded - they are just for you to practice before doing the real exercises in the lab itself. You can find the answers below each question by clicking the button below each question.
Write the output that is displayed to the user after running the following Python code:
a = "two"
b = a
c = a
print(c, end=" ")
c = "one"
b = c
a = "three"
print(a, end=" ")
print(c)
We want to write a program that produces the following output:
and a 1
and a 2
and a 3
Rearrange the following Python statements to create a program that produces that output. You may not use all of these statements in your answer.
x = "1"
x = "2"
x = "3"
print(x)
print(x)
print(x)
print("and a", end=" ")
print("and a", end=" ")
print("and a", end=" ")
We wish to write a Python program that displays the following output:
abba banana bandana
We currently have the following code:
a = "abba"
b = "banana"
c = "bandana"
# more code goes here
print(c, end=" ")
print(b, end=" ")
print(a)
Complete the Python program by writing code where the # more code goes here
comment is found so that the desired output is produced. See if you can do it by only using assignment statements and variables, without using any new string values. (Hint: you’ll need to create a least one new variable!)
We introduced some basic statements and structures in the Python programming language. Let’s quickly review them!
The print(expression)
statement is used to print output on the terminal.
expression
to a value, then display it to the screen.end
parameter, such as print(expression, end="")
to remove the newline.The assignment statement, like a = expression
is used to create variables and store values in the variables.
So far, we’ve only worked with string values in Python. Strings are a very useful data type in programming languages such as Python, but they are very limited in their use. Recall that a data type simply defines how a particular value is stored in a computer. The str
data type is used to store string values in Python.
Python supports many different data types for handling various data that we’d like to store and manipulate in our programs. In this lab, we’re going to cover the two basic types used for storing numbers in Python, the int
or integer type, and the float
or floating-point type.
In mathematics, an integer is a whole number, such as
$ 3 $,
$ -5 $, or even
$ 0 $. Basically, any positive or negative number that doesn’t include a fractional or decimal portion is a whole number, and therefore it is an integer. In Python, those numbers can be stored in the int
data type.
In Python, we can store an integer value in a variable using an assignment statement:
x = 5
That statement will store the integer value
$ 5 $ in the variable x
. Notice that the value
$ 5 $ does not have quotation marks around it. This is because we want to store the integer value
$ 5 $ and not the string value "5"
in the variable. Also, as we learned earlier, this is why we cannot create variable names that begin with a number - since numerical values start with a number, this is how Python can tell the difference between a numerical value and a variable.
We can also store negative numbers in a variable by placing a negative symbol -
in front of the numerical value:
y = -8
We’ll need to be careful and make sure that there is a space after the equals sign =
, but no space between the negative symbol -
and the number after it. Otherwise, the negative symbol could be confused for the minus symbol, which is an operator that we’ll learn about later in this lab.
In Python, there is effectively no maximum size for an integer, so we can store any arbitrarily large whole number (either positive or negative) in an int
variable.
The other type of number we can store in Python is a floating-point number. We won’t go into too much detail about floating-point values here, since you’ll learn about them elsewhere in this class. For the purposes of programming, the only thing to know about floating-point numbers is that they are used to represent numbers that include a fractional or decimal portion. In Python, these values are stored in the float
data type.
To create a variable that stores a floating-point value in Python, we can use an assignment statement that includes a value with a decimal point, like this:
a = 5.8
We can also create negative values using the negative symbol -
:
b = -7.987
Finally, it is possible to store a whole number in a floating-point value by simply adding a decimal point and a 0
at the end of the value, as in this example:
c = 42.0
Later in this lab, we’ll see a couple of situations where that may be useful.
For now, we’re just going to assume that Python can easily handle any reasonable number we want it to store in a float
variable, but there are some limits to the size and accuracy of those numbers. To reach these limits, we usually have to be dealing with numbers that have
$ 100 $ or more digits, either before or after the decimal place. So, for the purposes of this class, those limits really won’t apply to what we’re doing. You’ll learn about these limits in detail in later programming classes.
One thing that is very useful to know how to do in Python is determining the type of data stored in a variable. Python is very flexible, and we can store any type of data in any variable. In fact, a variable’s data type can even change in Python, which is something that many other programming languages won’t allow. Technically speaking, we would say that Python uses strong typing, which means that each variable has a known data type that we can find, and dynamic typing, meaning that the type of the variable can change while the program is running.
To determine the type of a variable, we can use the type(expression)
function in Python. We can simply place any variable or expression in the expression
argument, and then it will tell us the type of the value that results from evaluating that expression. Then, we can simply use the print()
function to print it to the screen. We won’t use this in our programs themselves, but it can be helpful for debugging purposes or to just better understand what is going on with data types.
Here’s a quick example program showing the type()
function in Python:
x = "Hello"
y = 5
z = 6.7
print(type(x))
print(type(y))
print(type(z))
When we execute this code in Python, we should see the following output:
<class 'str'>
<class 'int'>
<class 'float'>
Based on that output, we can assume that the variable x
is the str
data type for strings, y
is the int
data type for whole numbers, and z
is the float
data type for decimal numbers. The type()
function is pretty handy!
We can also convert values between the various data types in Python. To do this, there are special functions that match the name of the data types themselves. So, to convert any value to a string, we can use the str()
function. Likewise, to convert anything to an integer, we can use the int()
function. And finally, to convert anything to a floating-point value, we can use the float()
function.
So, we can extend the previous example a bit by showing how we can convert values between different data types:
x = "5.7"
print(x)
print(type(x))
print()
y = float(x)
print(y)
print(type(y))
print()
z = int(y)
print(z)
print(type(z))
When we run this program, we’ll get this output:
5.7
<class 'str'>
5.7
<class 'float'>
5
<class 'int'>
In this program, we’re starting with the string value "5.7"
stored in variable x
. So, the first two print()
statements will print that string value, and show that x
is indeed storing a str
data type. Then, we’ll use the float()
function to convert the string value "5.7"
stored in x
to the floating-point value
$ 5.7 $ and store that in y
. The next two print statements will print that value, and show that y
is storing a float
data type. Notice that the value printed for both variables x
and y
looks identical, but the data type of each variable is different!
Finally, we can use the int()
function to convert the floating-point value
$ 5.7 $ to an integer. In math, when we are asked to convert the number
$ 5.7 $ to a whole number, our first instinct is probably to just round up to
$ 6 $, since that is the closest value. However, in Python, as in most other programming languages, this function will simply truncate the value instead. Truncating a value simply means we take off the end of the value, so to convert
$ 5.7 $ to an integer we just remove the decimal portion, and we’re left with the value
$ 5 $. So, in the output above, we see that z
stores the integer value
$ 5 $, and it is the int
data type.
Notice that we are careful to say that the int()
function will truncate the value, and not that it will round down. This is due to how Python handles negative numbers like
$ -5.7 $. When converting that to an integer, it will also truncate it to
$ -5 $ instead of rounding down to
$ -6 $. So, we use the word truncate as the best way to describe the int()
function.
Since we are running our Python programs on a real computer, we have to be a bit careful about how we use these functions. Specifically, if we try to convert a value to a different data type and Python can’t figure out how to do that, we’ll cause an exception to occur. An exception in programming is any error that happens when the computer tries to run our code.
For example, what if we try to convert the string value "5.7"
directly to an int
data type, as in this example:
a = "5.7"
print(a)
print(type(a))
print()
b = int(a)
print(b)
print(type(b))
When we try to run this code in a file, such as the tutor.py
file shown here, we’ll see this output printed on the terminal:
5.7
<class 'str'>
Traceback (most recent call last):
File "tutor.py", line 5, in <module>
b = int(a)
ValueError: invalid literal for int() with base 10: '5.7'
Uh oh! That’s not good. In the output, we can see that we’ve caused a ValueError
, which is an exception that happens when we try to use a value in an incorrect way. So, we’ll need to carefully look at our code to see if we can find and fix the error.
Thankfully, in the output, it will tell us that the error occurred on line 5 of the file tutor.py
, so we can open that file and scroll to that line of code:
b = int(a)
This is where the error occurred. There are several ways we can fix it. The easiest would be to simply convert a
to a floating-point value using the float()
function instead.
Learning how to find and fix these exceptions is a key part of learning how to program. We’ll inevitably run into a few exceptions as we start to build larger and more complex programs. In this course, most exceptions can be easily handled simply by working carefully through the code, but every once in a while we may run into an exception that is truly difficult to solve. That’s one of the important things to remember when learning how to program - it is sometimes much easier to cause an exception than it is to figure out how to fix it, and sometimes you may need to reach out for help to get past a particularly tricky exception. So, don’t be afraid to ask the instructors or TAs for help if you get stuck on an exception. Many times, it’s a great chance for you to learn some new programming skills.
Now that we have the ability to store numerical data in variables in Python, we should also learn how to manipulate that data into something new. To do that, let’s learn about operators. An operator in programming is a special symbol that can be used in an expression to manipulate the data in some way.
Most operators are binary operators, which means they perform an operation that uses two values as input and produces a single value as output. In fact, in some programming languages, the operators themselves are implemented as functions in the language! Finally, we have to be careful not to confuse these with bitwise operators, which are operators perform operations on the binary value stored in a variable. We won’t cover those operators in this class, but the terminology is a bit confusing.
An expression containing a binary operator typically follows this format:
<expression> <operator> <expression>
As before, the <expression>
parts can be any valid expression in the language that can be reduced to a single value, and the <operator>
part is typically a single symbol, but it can also be a short keyword as well.
Thankfully, these operators should all be very familiar to us from mathematics already, so this is just a quick discussion of how they can be used in programming.
For starters, we can use the plus +
and minus -
symbols as operators to perform addition and subtraction in Python, just like in math. For example, we can add two variables together to create a third variable as shown in this example:
a = 5
b = 7
c = a + b
print(c)
When we run this code in the Python interpreter, we should get this result:
12
Likewise, we can subtract two variables using the minus symbol -
as shown here:
x = 24
y = 10
z = x - y
print(z)
This code should produce this output:
14
In Python, we use the asterisk *
, sometimes referred to as the star symbol, to multiply two values together. For example, we can find the product of two values as shown in this Python block:
a = 6
b = 7
c = a * b
print(c)
When we run this code, we should see the following result displayed to the user:
42
Division is performed using the slash /
symbol. A great way to think of division in programming is just like a fraction, since it uses the same symbol between the two numbers. For example, if we execute this code:
x = 27
y = 3
z = x / y
print(z)
we would see this output:
9.0
What if the division would result in a remainder? In that case, we’ll simply use decimal values in Python so that the result is exactly correct. For example, if we try to divide $ 19 $ by $ 5 $, as in this example:
a = 19
b = 5
c = a / b
print(c)
When we run this code, the Python interpreter will produce the following output:
3.8
So, as we can see, all of these operators in Python work exactly like their counterparts in math. So, we can easily use them in a way that should be very familiar to us.
The Python programming language also includes a few math operators that are not commonly used outside of programming.
The first one is the modulo operator. The modulo operator is used to find the remainder of a division operation. If we think back to math again, we’ve probably learned how to perform long division when dividing two values. At the end, we might be left with a remainder, or a portion of the first number that is left over after the operation is complete. In many computer programs, that value is very useful, so we have a special operator we can use to find that value. In Python, we use the percent symbol %
as the modulo operator.
For example, if we want to find the remainder after dividing $ 19 $ by $ 5 $, we would use the following code:
x = 19
y = 5
z = x % y
print(z)
When we run this code, we would get the following output:
4
This is because the value $ 5 $ will fit into the value $ 19 $ only $ 3 $ times, and then we’ll have the value $ 4 $ left over. Mathematically, we are saying that $ 19 / 5 = (3 * 5) + 4 $. Since $ 4 $ is the leftover portion, it is the resulting value when we use the modulo operator.
In this course, we’ll only worry about how the modulo operator works when applied to positive whole numbers. In practice, it can be applied to any numerical value, including decimal values and negative numbers, but those values are not really useful in most cases. So, to keep things simple, we’ll only use positive whole numbers with this operator.
The next operator introduced by Python is the exponentiation operator. The double star **
operator is used to represent exponentiation, sometimes referred to the power operator. In Python, the expression 2 ** 3
would be written mathematically as
$ 2^3 $, which is the operation of taking
$ 2 $ to the power of
$ 3 $, or multiplying
$ 2 $ by itself
$ 3 $ times.
Here’s a quick example of using the **
operator in code:
x = 5 ** 3
print(x)
print(type(x))
When this code is run, we see the following output:
125
<class 'int'>
||| growthhack
^
!Many other programming languages and tools use the carat ^
character to represent the exponentiation operation. However, in Python, the carat ^
character is used to represent the bitwise XOR operation. So, we must be careful not to accidentally use the ^
operator when we actually mean to use the **
operator for exponentiation.
|||
Finally, Python also includes the integer division operator, represented by two forward slashes //
, which is used to perform division that truncates the result to an integer. However, as we’ll see on the next page, if either of the values in the operation is a float
value, it will return a value that is a float
data type, even though the result is an integer value.
Let’s look at an example with that operator in code:
a = 17.5 // 4.5
print(a)
print(type(a))
As we expect, when we run this program, we’ll get the following output:
3.0
<class 'float'>
So, we see that this operator will return a float
value, even though it is truncating the result to an integer, simply because the input values contained a float
.
Since Python has multiple numeric data types, there are some rules that govern which data type is produced as a result of various math operations. Thankfully, the rules themselves are pretty straightforward once they are explained.
The basic rule to remember, if a mathematical operator is applied to two variables of the same data type, the result will also be that data type.
Let’s see what that means in practice. Here’s a quick example in Python using the multiplication operator on two integer values:
x = 5
y = 10
z = x * y
print(z)
print(type(z))
When we run this code, we should see the following output:
50
<class 'int'>
Since both x
and y
are the int
data type, the result of x * y
will also be an int
value, so the variable z
will have that data type, as shown in this example.
However, there is one exception to this rule, which is the division operator /
. In Python, the division operator will always return a float
value, even if it is a whole number. Here’s an example that demonstrates that:
a = 9
b = 3
c = 4
x = a / b
print(x)
print(type(x))
print()
y = a / c
print(y)
print(type(y))
When we run this program, we’ll see the following output:
3.0
<class 'float'>
2.25
<class 'float'>
So, as we can see, even though we are dividing two int
values, we’ll get a float
value as a result each time we use the division operator.
Following the rule above, if we perform a mathematical operation between two float
values, the resulting value will always be a float
as well:
a = 2.5
b = 4.5
c = a + b
print(c)
print(type(c))
Running this code will produce this output:
7.0
<class 'float'>
So, even though the result is a whole number, the value that is stored is the float
data type.
The other rule to remember is anytime an operation involves a float
value and an int
value, the result will be a float
. So, if there are mixed types, Python will default to the float
data type.
This can be seen in the following example:
a = 5
b = 2.0
c = a - b
print(c)
print(type(c))
When this code is executed, the output should look like this:
3.0
<class 'float'>
Once again, even though the result is a whole number, because the variable b
is a float
value, the entire result will also be a float
value.
Learning the data types that are returned by a mathematical operator can be tricky, but most programmers slowly develop an intuition of how each operator works and what to expect. So, don’t worry too much if this is confusing right now, since it will become much clearer with practice! Also, don’t forget that we can always create a simple test program like the examples shown above to confirm the result for any operation.
Finally, just like in mathematics, we must also be aware of the order that these operators are applied, especially if they are combined into a single expression. Thankfully, the same rules we learned in mathematics apply in programming as well. Specifically, operators in Python are applied in this order:
**
is resolved second, moving from left to right*
, /
, //
and %
are resolved third, moving from left to right.+
and -
are resolved fourth, moving from left to right.You might recall the “PEMDAS” acronym for remembering the order of operations in math, and thankfully it still applies here. Of course, this means that there are now 4 operators that all fit in the “multiplication and division” portion, so we have to carefully make sure they are all taken care of in the correct way.
Also, as you’ve probably already learned in math, it is always best to add extra parentheses to any expression to make the intent very clear instead of relying on the order of operations. So, when in doubt, use extra parentheses wherever needed!
Let’s work through a quick example just to see the order of operations in practice. Here’s a complex expression in Python that we can try to evaluate:
x = 8 / 4 + 5 * (3 + 1) - 7 % 4
Looking at our order of operations, the first step is to handle any expressions inside of parentheses. So, we’ll first start with the expression (3 + 1)
and evaluate it to 4
.
x = 8 / 4 + 5 * 4 - 7 % 4
Then, we’ll go left to right and perform any multiplication, division, and modulo operations. This means we’ll evaluate 8 / 4
, 5 * 4
and 7 % 4
and replace them with the resulting values:
x = 2.0 + 20 - 3
Notice that 8 / 4
was reduced to 2.0
instead of just 2
. This is because the division operator is the one exception to the rule where an operator applied to two integers will result in an integer. The division operator always produces a floating-point value.
Finally, we’ll perform addition and subtraction from left to right. So, we’ll evaluate 2.0 + 20
first, and then subtract 3
from the result of that operation. At the end, we’ll have this statement:
x = 19.0
So, we were able to use our knowledge of the order of operations to evaluate that complex expression to a single value,
$ 19.0 $, which will be stored in the variable x
.
Let’s try some simple practice problems. These problems are not graded - they are just for you to practice before doing the real exercises in the lab itself. You can find the answers below each question by clicking the button below each question.
Write the output that is displayed to the user after running the following Python code:
x = 13
z = "5"
y = int(z)
var = (x + y) % y * y - x
var = var / 2
print(var)
Pay special attention to data types! Make sure the answer is presented as the correct type.
Write the output that is displayed to the user after running the following Python code:
x = 9
y = 3.0
z = 5
ans = (x - z) % 2 // y + z ** (y - 1)
print(ans)
Pay special attention to data types! Make sure the answer is presented as the correct type.
You are teaching a class and would like to put your students into a number of groups. You know how many students are in the class and the number of groups to create, but you aren’t sure how many students should be in each group.
We’ll assume that these values are stored in the students
and groups
variables, respectively.
Write a Python program to compute the ideal group size for each group in the class. When divided, the groups in the class should have no fewer than size
people, and no more than size+1
people, and there should be exactly groups
groups total. For example, if there are
$ 15 $ people and the desired number of groups is
$ 4 $, then code should start with the following two variable assignments.
students = 15
groups = 4
# more code goes here
Your code should produce the following output for these values:
groups of 3 or 4
This is because the ideal group size for $ 4 $ groups out of $ 15 $ people is $ 3 $, and all groups should have either $ 3 $ or $ 4 $ members (in this case, one group of $ 3 $ and the rest groups of $ 4 $).
Write the rest of this Python program. Try different values for the students
and groups
variables to make sure that your answers are correct!
In this lab, we covered several major important topics. Let’s quickly review them.
str
data type stores text (strings). Use str(expression)
to convert an expression to a string if possible.int
data type stores whole numbers (integers). Use int(expression)
to convert an expression to an integer if possible.float
data type stored decimal numbers (floating-point). Use float(expression)
to convert an expression to a floating-point value, if possible.+
Addition-
Subtraction*
Multiplication**
Exponentiation (Power)/
Division//
Integer Division%
Modulo (the remainder of division)The Python programs we’ve written up to this point are very static - each time we run the program, it will perform the same exact operations. Since we’re running these programs on a real computer, it might be helpful to build programs that can read and respond to input from the user, making them much more useful overall. Python includes many different ways to handle user input, but in this lab we’ll just focus on the simple input()
function.
The input()
function is used to display a prompt the user and then record input. Let’s look at a quick example:
name = input("Enter your name: ")
print("Hello ", end="")
print(name)
Here, we see that the input()
function actually accepts a message as an argument, which will be displayed to the user. After the message is printed, the user will be given a cursor to enter text immediately after it. Once the user presses the ENTER key, the input()
function will read the input that was entered and store it as a string value or str
data type in the variable called name
.
For example, if the user inputs Willie Wildcat
at the prompt, this program’s output will look like this:
Enter your name: Willie Wildcat
Hello Willie Wildcat
We can see this even more clearly in the terminal. When we first run the program, we’ll see the prompt "Enter your name:"
printed, followed by a cursor prompting the user to enter something:
Once the user types the input and presses ENTER, the rest of the program will run:
Now we see the cursor is at the next command prompt, ready to run another program.
Of course, we can also read numerical input in Python using the input()
function. To do this, we must simply use either the int()
or float()
function to convert the input received as a string to the correct data type.
Here’s a quick example program that requires two inputs for the price and quantity of an item being purchased:
text_one = input("Enter the price of one item: ")
price = float(text_one)
text_two = input("Enter the quantity of items: ")
quantity = int(text_two)
cost = price * quantity
print("The total cost is $", end="")
print(cost)
If the user wishes to purchase $ 3 $ items at the price of $ 2.75 $ per item, then the program’s output would look like this:
Enter the price of one item: 2.75
Enter the quantity of items: 3
The total cost is $8.25
In the program, we simply read each input from the user as a string value, then use the appropriate conversion function to convert it to the correct data type. For numbers including a decimal point, we use float()
, but for whole numbers we use the int()
function.
This works well if the user enters values that make sense. But, what if the user wishes to purchase a fraction of an item? Will this program work? Unfortunately, if the user enters a value with a decimal point for the second input, it can no longer be converted to an integer and we’ll get an error:
Enter the price of one item: 2.75
Enter the quantity of items: 3.5
Traceback (most recent call last):
File "tutor.py", line 4, in <module>
quantity = int(text_two)
ValueError: invalid literal for int() with base 10: '3.5'
For right now, there’s not much we can do about that error other than write programs that clearly tell the user what type of data is expected, but later we’ll learn how to handle errors like this and prompt the user for input again.
Python includes an operator that can be used to concatenate, or combine, two strings together, one after another. We can use the plus symbol +
between two strings to concatenate them together into a single string, making it much simpler to build more complex strings and outputs.
A simple example is shown below:
first = "Hello"
second = "World"
print(first + second)
When executed, this code will display this output:
HelloWorld
As we can see, using the +
operator in Python to concatenate two strings together is a quick and easy way to simplify our print statements.
Python also requires both sides of the +
operator to be strings in order for concatenation to work. So, if we want to concatenate a string with a numeric value, we’ll have to convert it to a string using the special str()
function. Here’s an example:
text = "Your total is $"
value = 2.75
print(text + str(value))
When we run this program, we’ll receive the following output:
Your total is $2.75
However, if we forget to convert the value
variable to a string, as in this example:
text = "Your total is $"
value = 2.75
print(text + value)
we’ll receive an error instead:
Traceback (most recent call last):
File "tutor.py", line 3, in <module>
print(text + value)
TypeError: must be str, not float
So, we’ll have to be careful and make sure that we convert all of our numbers to strings before trying to concatenate them together. Of course, if both sides of the +
operator are numbers, then it will perform addition instead of concatenation! There are better ways to concatenate strings though, but we will cover this in another section. For now, the +
operator is a quick an easy way to get the job done!
We won’t always need to explicitly use the str()
function to convert another datatype into a string. If you are printing something other than a string directly using print
, for example, python will implicitly use str
for you.
x = 10
print(x)
# python will implicitly do the following instead
# print(str(x))
Python also includes an operator that allows us to quickly repeat strings. We can use the asterisk *
operator to effectively “multiply” a string. Consider this example:
word = "repeat"
print(word * 2)
print(word * 4)
When we run this program, we’ll see the following output:
repeatrepeat
repeatrepeatrepeatrepeat
This allows us to build outputs that consist of repeated strings. We can also use this to quickly format our output into various shapes and structures!
Finally, we can also use the escape operator \
to handle some certain special characters in Python strings. We’ve already seen this used in the newline character, which is simply the escape operator \
followed by the letter n
, as in \n
.
However, we can also use it to include quotation marks in our string itself. Consider the following example:
sentence = "Hello from "the little apple!""
print(sentence)
When we try to run this code, we’ll get the following error:
File test.py, line 1
sentence = "Hello from "the little apple!""
^
SyntaxError: invalid syntax
This is because the double quotation mark "
is used to show which part of the code is a string value, but the Python interpreter gets confused when we try to include quotation marks inside of our strings. So, we can use the escape operator \
to tell it to treat those quotation marks as part of the string value itself, and not part of the Python code:
sentence = "Hello from \"the little apple!\""
print(sentence)
This program will execute properly and produce the following output:
Hello from "the little apple!"
Notice that the quotation marks now appear correctly in the output, but the escape operators are not shown. There are many other special ways to use the escape operator in Python, all of which can be found in the Python Documentation.
Python also allows us to mix single and double quotes in a string, as long as we use them consistently, So, we can change the previous example to use single quotes around the string itself, and remove the escape operator from the double quotes, which will result in this code:
sentence = 'Hello from "the little apple!"'
print(sentence)
This code will also produce the desired output. However, in this class we won’t use single quotes around strings, just for consistency. Many other programming languages don’t allow this, so we encourage you to learn how to use the escape operator to handle these situations.
Python also includes another method of building strings, which are known as “F-strings”. F-strings allow us to put placeholders in strings that are later replaced with values from variables, effectively creating a way to build “templates” that can be used throughout our program.
The easiest way to see how this works is looking at a few examples. Let’s start with a simple one:
name = input("Enter your name: ")
print(f"Hello {name}")
If the user inputs "Willie Wildcat"
when prompted, then this code will produce this output:
Enter your name: Willie Wildcat
Hello Willie Wildcat
There are many important parts to this example, so let’s look at each one individually. First, in the print()
statement, we see the string "Hello {name}"
. This is an example of a template string, which includes a set of curly braces {}
as placeholders for data to be inserted. Each template string can have unlimited sets of curly braces. Inside of each set of curly braces, we can place the variable or expression that will be substituted at that location as a string.
Also, we notice that in front of the string, we see the character f
. Preceding a string with f
outside of the quotation marks will denote the string as an f-string (hence the name), which allows the values inside to be interpolated. Interpolation is the term used when formatting marks in a string, such as the curly braces in an f-string, are interpreted and replaced with the correct values they represent.
Python f-strings can do many powerful things, such as handle more complex formatting and multiple lines. For right now, we’ll just use simple variable placeholders in our f-strings, but over time we’ll introduce additional ways to use f-strings to achieve the desired output.
The most powerful use of f-strings is to insert numerical values directly into strings without having to convert each value directly to the str
data type - the interpolation process handles this for us.
For example, we can update our previous program to use f-strings to display the output in a single print()
statement, and we can also add additional information with ease:
text_one = input("Enter the price of one item: ")
price = float(text_one)
text_two = input("Enter the quantity of items: ")
quantity = int(text_two)
cost = price * quantity
print(f"{quantity} items at ${price} each is ${cost} total")
When we execute this program, we’ll see output that looks like this:
Enter the price of one item: 2.75
Enter the quantity of items: 3
3 items at $2.75 each is $8.25 total
This example shows how easy it is to build complex output strings using f-strings. We can expand the template syntax to even round numbers. f"{.2f}"
allows us to format a number to two decimal places. The 2
represents the number of places you want to round to and the ‘f’ inside the curly brackets indicates that you are trying to format the number as a floating point number.
text_one = input("Enter Numerator:")
numerator = float(text_one)
text_one = input("Enter Denominator:")
denominator = float(text_one)
result = numerator / denominator
print(f"{numerator} divided by {denominator}, rounded to two decimal places is {result:.2f}")
Enter Numerator: 2
Enter Numerator: 3
2.0 divided by 3.0, rounded to two decimal places is 0.67
If we add a comma into the template string f"{,.2f}"
, we can automatically format in commas for thousands, millions, etc. For example:
text_one = input("Enter the price of one item: ")
price = float(text_one)
text_two = input("Enter the quantity of items: ")
quantity = int(text_two)
cost = price * quantity
print(f"{quantity} items at ${price:,.2f} each is ${cost:,.2f} total")
300 items at $2,500.45 each is $750,135.00 total
Prior to the introduction of f-strings, it was common to use the format()
method to place values inside of a template string. The last line of the previous example would look like this using the format method:
print("{} items at ${} each is ${} total".format(quantity, price, cost))
As we can see, the format()
method receives each value as an argument, and it will replace the curly brace placeholders {}
in the template string with each value, working from left to right. The output produced will be identical to the f-string in the example above.
We won’t use the format()
method in this class, but you may see it in many online tutorials and documentation since f-strings were introduced relatively recently.
Finally, let’s look at how we can rewrite some of our previous Python programs by combining expressions into more complex statements. Python allows us to perform multiple actions on a single line of code, provided they can all be combined in some way to create a single statement.
Let’s consider the example on the previous page, shown here:
text_one = input("Enter the first number: ")
one = int(text_one)
text_two = input("Enter the second number: ")
two = int(text_two)
one = one * one
two = two * two
total = one + two
print(f"The sum of squares of {one} and {two} is {total}")
There are many ways we can write this program to perform the same work. For example, the process of computing the sum of squares itself can actually be reduced to a single line of code as seen below:
total = (one * one) + (two * two)
We can perform multiple mathematical operations in a single expression, and as long as we either use parentheses or pay attention to the order of operations, we’ll get the expected answer. We don’t have to store the intermediate values in variables, since Python will do that for us when it evaluates the expression.
Likewise, we can put the input()
function inside of the int()
function, allowing us to read input as a string and convert it to an integer in a single line:
one = int(input("Enter the first number: "))
two = int(input("Enter the second number: "))
total = (one * one) + (two * two)
print(f"The sum of squares of {one} and {two} is {total}")
Finally, we can also move the computation of the sum of squares directly into the f-string. When Python tries to print the f-string, the interpolation process must compute that value before it can print the output.
one = int(input("Enter the first number: "))
two = int(input("Enter the second number: "))
print(f"The sum of squares of {one} and {two} is {(one * one) + (two * two)}")
Functionally, this code will create the exact same output as the previous code, but it will do so using fewer variables and statements. Each statement is simply more complex, consisting of multiple expressions.
The real question is: which of these two examples is best? That is, which one is the preferred coding style to learn? The answer is that it really depends - both have their merits, and functionally they will work nearly identically on most modern computers and programming languages.
It really is a question of style - is it better to have more lines of code and variables, clearly spelling out what each step of the process is, or is it better to have shorter programs with more complex lines of code but maybe fewer variables? For beginning programmers, it is usually recommended to follow the first style, using as many variables as needed and focusing on short, concise lines of code over large, complex statements.
However, as we become more accustomed to programming, we’ll find that many times it is easier to read and understand complex statements, and our code can be written in a way that better reflects what it is actually doing.
This is very similar to learning how to read, write, and speak in a new language. We must start with short, concise sentences, and slowly build up our knowledge of more complex statements and grammar rules until we become a fluent speaker.
Overall, the best advice to follow is to make your code readable to both yourself and anyone else who may have to read and maintain the code. As long as it is clear what the code is doing based on what it says, it is probably a good style to follow. As we continue to learn more, we’ll slowly refine our coding style to be one that is easy to follow and understand for everyone.
Let’s try some simple practice problems. These problems are not graded - they are just for you to practice before doing the real exercises in the lab itself. You can find the answers below each question by clicking the button below each question.
Write the output that is displayed to the user after running the following Python code:
x = input("Enter a number: ")
y = x + "00"
z = int(y)
a = int(z + (z / 100))
print(f"The result is {a}")
Assume the user inputs the string "4"
when prompted.
Write the output that is displayed to the user after running the following Python code:
x = int(input("Enter a number: "))
x = x // 100 * 100 + x % 10
print(f"The answer twice is {x * 2}")
Assume the user inputs the string "121"
when prompted.
Write a Python program that prompts the user to input a verb and a noun that will be stored in separate variables. Then, use those two words to create a simple compliment for the user and print it to the terminal. The compliment should be in the form of:
You "<verb>" my "<noun>"? That's awesome!
For example, if the user inputs "love"
and "food"
, then the string printed by the program should be:
You "love" my "food"? That's awesome!
Hint: Notice that the output includes quotation marks around the two words provided by the user, and an apostrophe (single quote) as well. You’ll need to account for this in your code!
You can expand on this program to prompt the user for additional inputs, and use f-strings to create a Mad Libs style game!
input(expression)
will display the expression
to the user as a prompt, then return what was typed by the user as a string value.input(expression)
is terminated by the user pressing ENTER, so next output starts on a new line.+
Concatenation (join strings together). May only be applied to two strings.*
Repeat (duplicate strings)\
Escape Operator for special characters{}
and inside the placeholder is any variable or expression to be printed in that location in the template string.Expressions can be combined in many ways within a statement. Expressions can be used as arguments to functions, and more!
In this lab, we’re going to introduce another data type in Python, the Boolean data type. Working with Boolean values in programming is a very important skill to learn.
Elsewhere in this course, we’ve already learned about Boolean values and how they work, so we won’t cover that information again here. Feel free to refer to other areas in this course for more background on Booleans, Boolean Logic, Boolean Algebra, and other related topics.
In this lab, we’ll learn how to create Boolean values in Python, as well as the various Boolean operators and comparators that can we can use in our code. Then, we’ll explore how to use conditional statements to affect the control flow of our programs. This allows us to build programs that will perform different tasks based on the input we receive from the user, which is a very key concept in programming. Let’s get to it!
In Python, Boolean values are stored in the bool
data type. Variables of the bool
data type can only store one of two values, True
or False
. So, a Boolean value is really the simplest data value we can imagine - it is a single binary bit representing a value of True
or False
.
To create a Boolean variable in Python, we can simply assign those values to a variable in an assignment statement:
a = True
b = False
print(a)
print(type(b))
When we execute that code, we’ll see the following output:
True
<class 'bool'>
In Python, we use the keywords True
and False
to represent Boolean values. In Python, it is very important to make sure these values are capitalized, otherwise the program will not work properly. Also, since these are keywords and not strings, we don’t need to put them in quotation marks.
Python includes the special bool()
function, which can be used to convert any data type into a Boolean value. The bool()
function follows these rules to determine what to return:
False
, the value 0
, the value None
, or anything with 0 length, including the empty string, it will return False
.True
.Let’s look at a couple of quick examples. First, let’s try to convert the strings "True"
and "False"
to their Boolean equivalents:
print(bool("True"))
print(bool("False"))
When this code is executed, we’ll see this output:
True
True
This seems a bit strange, since the string "False"
ended up creating the Boolean value True
. However, if we look at the rules above, since the string "False"
has a length greater than 1, and it is not any of the special values listed above, it should always result in the Boolean value True
.
In this example, we can check a couple of special values using the bool()
function:
print(bool(0))
print(bool(1))
print(bool(""))
In this case, we’ll see the following output:
False
True
False
Here, we see that the value 0
, as well as the empty string ""
, both result in a value of False
. However, the value 1 is True
, since it is a non-zero value.
In practice, we won’t use the bool()
function directly very often. Instead, if we want to determine if a user inputs a True
or False
value, we can just use one of the Boolean comparators that we’ll see later in this lab.
Python also includes several operators that can be applied to one or two Boolean values. These operators make up the basis of Boolean logic, and allow us to construct complex expressions of Boolean values. Let’s quickly review the three basic Boolean operators present in Python.
In Python, we use the keyword and
to perform the Boolean and
operation. This operator will return True
if both input values are also True
, otherwise it will return False
.
This corresponds to the following truth table:
Variable 1 | Variable 2 | Result |
---|---|---|
F | F | F |
F | T | F |
T | F | F |
T | T | T |
Here’s a quick example of the and
operator in Python:
x = True
y = False
print(x and y)
When we run this Python code, we should see this output:
False
Since the variable y
is False
, the resulting value is also False
.
Likewise, the keyword or
is used in Python for the Boolean or
operation. This operator will return True
if either of the input values is True
, but it will return False
if neither of them are True
.
This corresponds to the following truth table:
Variable 1 | Variable 2 | Result |
---|---|---|
F | F | F |
F | T | T |
T | F | T |
T | T | T |
Let’s look at an example:
a = False
b = True
print(a or b)
When we execute this code, we’ll get this output:
True
Since b
is True
, we know that at least one input is True
and the result of a or b
is also True
.
Finally, Python uses the keyword not
to represent the Boolean not
operation, which simply inverts a Boolean value from True
to False
and vice-versa.
This corresponds to the following truth table:
Variable 1 | Result |
---|---|
F | T |
T | F |
Here’s an example:
x = True
print(not x)
print(not not x)
When we run this code, we’ll see this printed to the terminal:
False
True
Since x
is True
, we know that not x
is False
. We can then perform the not
operation again, on that result, as shown in not not x
, which will result in the original value of x
, which is True
.
Python also uses various comparators to allow us to compare values of many different data types to produce a Boolean value. We can compare numbers, strings, and many other data types in Python using these comparators.
The basic comparators in Python are:
==
equal!=
not equal<
less than<=
less than or equal to>
greater than>=
greater than or equal toNotice that the equal comparator in Python now uses two equals signs ==
instead of a single one. This is because the single equals sign =
is used in the assignment statement, and we don’t want to confuse a Boolean comparator for an assignment. So, in Python, as in most other programming languages, we use two equals signs ==
when comparing values, and one equals sign =
when we are storing a value in a variable.
We usually only want to compare values of the same data type using comparators. It really only makes sense to compare strings to strings, and Booleans to Booleans. However, for the numeric data types int
and float
, we can easily compare values of either data type together in Python.
Consider the following example:
x = 5
y = 5.0
print(x == y)
When we execute this code, we’ll get this output:
True
Even though x
is the int
data type and y
is the float
data type, they both store the same numerical value, and our comparators will work just like we expect. So, we can easily compare both integers and floating-point values in our code.
Behind the scenes, when we compare an int
and a float
value in Python, Python will convert the values to a common data type, usually the float
data type, using the appropriate conversion function. This is known as coercion in programming - the program is essentially forcing a value of one data type into another data type because of an operation. However, it won’t change the data type of the variable involved, just the value it is evaluating.
Strings in Python can also be compared using the various comparator operators. When two strings are compared using any type of “less than” or “greater than” comparator, they will be compared according to their lexicographic order. In general, this means that each letter will be ordered based on its value in the ASCII encoding standard.
Let’s look at a quick example:
a = "First"
b = "fir"
print(a < b)
When we run this code, we’ll see this output:
True
This may seem surprising, since we’d expect the word "fir"
to come before the word "First"
in a dictionary, since it has fewer letters. However, in Python, the letters "f"
and "F"
are not treated identically. According to the ASCII encoding standard, capital letters have a lower value than lower-case letters, so all words starting with a capital "F"
will come before any words starting with a lower-case "f"
.
This can be a bit confusing, so it is always important to remember that we can always write a quick sample program in Python to test how a particular operator will work for a given set of values. If nothing else, we can always count on our computer to produce the same result for the same operation, no matter if it is part of a larger program or a small test program.
Finally, Python’s order of operations can be updated to include these Boolean comparators and operators. So, when we see an expression that combines mathematical operators with Boolean operators and comparators, we’ll follow this order:
not
operatorand
operatoror
operatorAs always, it is considered good practice to include parentheses in any complex expressions to make sure that the intent is clear, regardless of the order of operations.
Let’s try some simple practice problems. These problems are not graded - they are just for you to practice before doing the real exercises in the lab itself. You can find the answers below each question by clicking the button below each question.
What is printed to the terminal when the following Python program is run?
a = 5
b = 10
c = 15
x = b > a and c < b or (not (a + b > c))
y = b + a >= c or c / a > b / a
z = c - b != a and not c % a == 0
print(x)
print(y)
print(z)
Try to work out the solution yourself first before running the code.
What is printed to the terminal when the following Python program is run?
x = 7
y = 42
z = 6
a = x >= z and y <= x * z or not y % z == 0
b = not (y // x <= z or x - z > 0 or y % x != 1)
c = y // (z + x) < z / 2 and not bool(z)
print(a)
print(b)
print(c)
Try to work out the solution yourself first before running the code.
Write a Python program that performs the following operations:
True
. If not, print False
.Your program may not use any conditional statements (if statements).
Hint: what comparisons must be made, and what comparisons do not need to be made, to answer these questions definitively?
Write a complete program in Python that performs the following operations:
true
. If not, print false
.Your program may not use any conditional statements (if statements).
Hint: you can use the modulo %
operator to determine if one number is evenly divisible by another number. Can this also tell you which number is a multiple of another number?
Now that we understand how to use Boolean values in our programs, it’s time to put those values to use. One way to think of the result of a Boolean expression is that it helps us make a decision in our programs. For example, if we want to do something special in our program when the user inputs the value
$ 42 $ into the variable x
, then we can write the Boolean expression x == 42
to help us decide if the user input the correct value.
Once we have that decision made, we need some way to tell our program to run a different piece of code based on the outcome of that decision. In Python, as well as most other programming languages, we can use a special construct called an if statement to do this. If statements are one example of a conditional statement in programming.
These conditional statements allow us to affect the control flow of our programs. Effectively, we can use a Boolean expression to determine whether a particular piece of code should be executed or not. In an if statement, we include a Boolean expression and a block of statements. If the Boolean expression evaluates to True
, then we execute the code in the block of statements. If it is False
, then we skip the block and continue with the rest of the program.
The structure of an if statement in Python is shown below:
if <boolean expression>:
<block of statements>
In this structure, we have a <boolean expression>
that is evaluated. After the Boolean expression is a colon :
.
Then, the <block of statements>
is included below the if statement’s first line, and it must be indented one level. In Python, the <block of statements>
must include at least one line of code, otherwise Python won’t be able to understand it.
Let’s briefly discuss indentation in Python, since it is very important and is usually something that trips up new Python developers. Most programming languages use symbols such as curly braces {}
to surround blocks of statements in code, making it easy to tell where one block ends and another begins. Similarly, those languages also use symbols such as semicolons ;
at the end of each line of code, indicating the end of a particular statement.
These programming languages use those symbols to make it clear to both the developer and the computer where a particular line or block of code begins and ends, and it makes it very easy for tools to understand and run the code. As an interesting side effect, it also means that the code doesn’t need to follow any particular structure beyond the use of those symbols - many of the languages allow developers to place multiple statements, or even entire programs, on a single line of code. Likewise, indentation is completely optional, and only done to help make the code more readable.
Python takes a different approach. Instead of using symbols like semicolons ;
and curly braces {}
to show the structure of the code, Python uses newlines and indentation for this purpose. By doing so, Python is simultaneously simpler (since it has fewer symbols to learn) and more complex (the indentation has meaning) than other languages. It’s really a tradeoff, though most Python programmers will admit that not having to deal with special symbols in Python is well worth the effort of making sure that the indentation is correct, especially since most other languages follow the same conventions anyway, even if it isn’t strictly required.
So, how can we indent code in Python? Typically, we use four consecutive spaces for each level of indentation. So, below the conditional statement if <boolean expression>:
shown above, we would place four spaces before the first line of the <block of statements>
, and then continue to do so for each line below that should be included in the block of code.
Thankfully, most text editors used for programming, such as Codio, Atom, Visual Studio Code, and more, are all configured to convert tabs to spaces. So, we can simply press the Tab key on the keyboard to insert the correct amount of spaces for one level of indentation. Likewise, we can usually use Shift+Tab to remove four spaces.
Finally, it is worth noting that there is a special symbol that actually is a tab in text, which is represented as \t
in a string. Like the newline symbol, we can’t see it in our UI in most cases, but it is there. Some non-programming text editors, such as Notepad in Windows, will insert that symbol instead of four spaces when we press the Tab key. If we try to run a program that contains those symbols, the Python interpreter may not be able to read our program and will give us an error about inconsistent use of tabs and spaces. If that happens, we’ll need to make sure our program is consistently using only tabs or spaces for indentation. Most editors used for programming have a special function for converting tabs to spaces, and there are lots of online tools available for this as well.
Let’s go through a couple of code tracing examples in Python Tutor to see how an if statement works in code.
Consider this program in Python:
x = int(input("Enter a number: "))
if x == 7:
print("That's a lucky number!")
print("Thanks for playing!")
We can see this example by clicking on this Python Tutor link. At first, our window should look something like this:
When we step through the program, the first line of code will ask the user to input a number, so Python Tutor will show an input box at the bottom of the window:
For this first time through the program, let’s assume the user inputs the string "42"
as input:
So, when we click Submit, we’ll see the integer value
$ 42 $ stored in the variable x
in the Global
frame:
At this point, we’ve reached the if statement. The first step is to evaluate the Boolean expression x == 7
. Since x
is actually storing the value
$ 42 $, this statement will evaluate to False
. So, when we click the Next button on the state below:
We’ll see that the program arrow jumps past the block of statements in the if statement. So, the next line will simply print the goodbye message:
The entire process is shown in the animation below:
As we can see, when the Boolean expression evaluates to False
, we’ll just skip the block of statements inside of the if statement.
Now let’s see what happens when the Boolean expression evaluates to True
instead. To see that, we can go back to the point where our program is asking for input, as shown below:
This time, we’ll assume the user inputs the string "7"
as input.
So, when we click Submit, we’ll see the integer value
$ 7 $ stored in the variable x
in the Global
frame:
This time, when we reach the if statement, we’ll see that the Boolean expression x == 7
will evaluate to True
. So, when we click the Next button, we’ll be taken to the block of statements inside of the if statement:
Here, we’ll print the special message for finding a lucky number:
Then, we’ll print the program’s goodbye message:
The entire process can be seen in this animation:
So, when the Boolean expression evaluates to True
, we’ll run the code inside the if statement. If it is False
, then we’ll just skip past the if statement and continue with the rest of our program.
Another way to visualize an if statement is using a flowchart. Consider the following Python code:
x = int(input("Input: "))
if x > 0:
print(x)
print("Goodbye")
This Python code can also be represented as the flowchart shown below:
When we read flowcharts like this, we typically go from top to bottom, starting with the circle labeled “START”. The first step is to read input from the user, and store that value in the variable x
.
Then, we reach a decision node, which uses a diamond shape. This node asks if the value in the variable x
is greater than 0. Notice that there are two lines coming from the decision node: one labeled “True” and another labeled “False”. They correspond to the possible values that can result from evaluating the Boolean expression x > 0
. So, if the result of that expression is True
, then we’ll follow the path that exits the diamond from the side labeled “True”, and we’ll output the value x
to the terminal. Once that is done, we can follow that path around to the next box that will output the string "Goodbye"
.
If that result is False
, then we’ll follow the downward path labeled “False” and skip the path to the side. Instead, we’ll just reach the line that prints "Goodbye"
and then end the program.
As we learn how to use if statements in our code, we might find it easier to use flowcharts or other tools to help understand exactly what our program will do. The path that a program takes through a flowchart like this is called the control flow of the program. We’ll discuss that topic a bit more toward the end of this lab.
Python also supports another kind of conditional statement, the if-else statement. An if-else statement contains a single Boolean expression, but two blocks of code. If the Boolean expression evaluates to True
, then one block of statements is executed. If it is False
, then the other block will be executed.
The general structure of an if-else statement in Python is shown below:
if <boolean expression>:
<block of statements 1>
else:
<block of statements 2>
Notice that the else
keyword is followed by a colon, just like the first line of the if-else statement. As we’d expect, the first block of statements is executed if the <boolean expression>
portion is True
and the second block is executed if it is False
.
Once again, let’s go through a couple of code traces using Python Tutor to explore how an if-else statement works in Python.
Here’s a simple Python program that contains an if-else statement:
x = int(input("Enter a number: "))
if x >= 0:
print("Your number is positive!")
else:
print("Your number is negative")
print("Thanks for playing!")
This program accepts an input from the user, converts it to an integer, and then determines if the integer is a positive or negative number. Let’s go through this program a couple of times in Python Tutor to see how it works. As always, you can copy and paste this code into Python Tutor, or click this Python Tutor link to follow along!
We’ll start an input prompt as shown here:
Let’s assume that the user inputs the string "5"
here.
That means that the integer value
$ 5 $ will be stored in the variable named x
:
Now we’ve reached the if-else statement. So, we’ll need to evaluate the Boolean expression x >= 0
. Since x
is currently storing the value
$ 5 $, this expression will evaluate to True
. Therefore, we’ll move into the first block of statements in the if-else statement:
From here, we’ll print the message that the user’s input was a positive number, as well as the goodbye message at the end of the program. It will entirely skip the second block of statements in the if-else statement, as we can see here:
A full execution of this program is shown in the animation below:
What if the user inputs a negative value, such as $ -7 $? In that case, we’ll be at this state in our program:
From here, we’ll evaluate the Boolean expression x >= 0
again. This time, however, we’ll see that it evaluates to False
, so we’ll jump down to the second block of statements inside the if-else statement:
This will print a message to the user that the input was a negative number, and then it will print the goodbye message. We completely skipped the first block of statements:
A complete run through this program is shown in this animation:
So, as we expect, an if-else statement allows us to run one block of statements or the other, based on the value of the Boolean expression. It is a very powerful piece of code, and it allows us to write programs that perform different actions based on the input provided by the user or the values of other variables.
Another way to visualize an if-else statement is using a flowchart. Consider the following Python code:
x = int(input("Input: " ))
if x >= 0:
print(x)
else:
print(-1 * x)
print("Goodbye")
This code can also be represented as the flowchart shown below:
Once again, we start at the top of the flowchart at the circle labeled “START”. From there, we read an input from the user and store it in the variable x
. At this point, we reach our if-else statement’s decision node, represented by the diamond. Here, we are using the Boolean expression x >= 0
to determine which branch to follow. So, if the user inputs a positive value, then we’ll follow the path to the right that is labeled “True”, which will simply print the value of x
to the screen.
However, if the user inputs a negative value for x
, then the Boolean expression will be False
and we’ll follow the path to the left labeled “False”. In this branch, we’ll print the value of (-1 * x)
to the screen, which simply removes the negative sign from the value in x
.
Finally, after each path, we’ll merge back together to run the last piece of code, which prints the "Goodbye"
message to the screen.
Notice that there is no way to print both x
and -1 * x
in the same execution of this program. If we follow the arrows through the flowchart, we see that we can only follow one branch or the other, and not both. This is an important concept to remember when working with if-else statements! The control flow of the program can only pass through one of the two blocks of statements, but not both.
One important concept to understand when writing if statements and if-else statements is the control flow of the program. Before we learned about conditional statements, our programs had a linear control flow - there was exactly one pathway through the program, no matter what. Each time we ran the program, the same code would be executed each time in the same order. However, with the introduction of conditional statements, this is no longer the case.
Because of this, testing our programs becomes much more complicated. We cannot simply test it once or twice, but instead we should plan on testing our programs multiple times to make sure they work exactly the way we want. So, let’s go through some of the various ways we can test our programs that include conditional statements.
First, let’s consider this example program:
x = int(input("Enter a number: "))
y = int(input("Enter a number: "))
if x + y > 10:
print("Branch 1")
else:
print("Branch 2")
if x - y > 10:
print("Branch 3")
else:
print("Branch 4")
We can represent the two if-else statements in this program in the following flowchart as well:
First, let’s consider how we can achieve branch coverage by executing all of the branches in this program. This means that we should test the program using different sets of inputs that will run different branches in the code. Our goal is to execute the code in each branch at least once. In this example, we see that there are four branches, helpfully labeled with the numbers one through four in the print()
statements included in each branch.
So, let’s start with a simple set of inputs, which will store
$ 6 $ in both x
and y
. Which branches will be executed in this case? Before reading ahead, see if you can work through the code above and determine which branches are executed?
In the first if-else statement, we’ll see the x + y
is equal to
$ 12 $, which is greater than
$ 10 $, making that Boolean statement True
. So, we’ll execute the code in branch 1 this time through. When we reach the second if-else statement, we’ll compute the value of x - y
to be
$ 0 $, which is less than
$ 10 $. In this case, that makes the Boolean statement False
, so we’ll execute branch 4. Therefore, by providing the inputs 6
and 6
, we’ve executed the code in branches 1 and 4.
So, can we think of a second set of inputs that will execute the code in branches 2 and 3? That can be a bit tricky - we want to come up with a set of numbers that add to a value less than
$ 10 $, but with a difference that is greater than
$ 10 $. However, we can easily do this using negative numbers! So, let’s assume that the user inputs
$ 6 $ for x
and
$ -6 $ for y
. When we reach the first if-else statement, we can compute the result of x + y
, which is
$ 0 $. That is less than
$ 10 $, so the Boolean statement is False
and we’ll execute the code in branch 2. So far, so good!
Next, we’ll reach the second if-else statement. Here, we’ll compute the result of x - y
. This time, we know that
$ 6 - -6 $ is actually
$ 12 $, which is greater than
$ 10 $. So, since that Boolean expression is True
, we’ll run the code in branch 3. That’s exactly what we wanted!
Therefore, if we want to test this program and try to execute all the branches, we only have to provide two pairs of inputs:
However, that’s only one way we can test our program. There are many other methods we can follow!
A more thorough way to test our programs is to achieve path coverage. In this method, our goal is to execute all possible paths through the program. This means that we want to execute every possible ordering of branches! So, since our program has 4 branches in two different if-else statements, the possible orderings of branches are listed below:
As we can see, there are twice as many paths as there are branches in this case! We’ve already covered ordering 2 and 3 from this list, so let’s see if we can find inputs that will cover the other two orderings.
First, we want to find a set of inputs that will execute branch 1 followed by branch 3. So, we’ll need two numbers that add to a value greater than 10, but their difference is also greater than 10. So, let’s try the value
$ 12 $ for x
and
$ 0 $ for y
. In that case, their sum is greater than
$ 10 $, so we’ll execute branch 1 in the first if-else statement. When we reach the second if-else statement, we can evaluate x - y
and find that it is also
$ 12 $, which is greater than
$ 10 $ and we’ll execute branch 3. So, we’ve covered the first ordering!
The last ordering can be done in a similar way - we need a pair of inputs with a sum less than
$ 10 $ and also a difference less than
$ 10 $. A simple answer would be to input
$ 4 $ for both x
and y
. In this case, their sum is
$ 8 $ and their difference is
$ 0 $, so we’ll end up executing branch 2 of the first if-else statement, and branch 4 of the second if-else statement. So, that covers the last ordering.
As we can see, path coverage will also include branch coverage, but it is a bit more difficult to achieve. If we want to cover all possible paths, we should test our program with these 4 sets of inputs now:
There’s still one more way we can test our program, so let’s try that out as well.
Finally, we should also use a set of inputs that test the “edges” of the Boolean expressions used in each if-else statement. An edge is a value that is usually the smallest or largest value before the Boolean expression’s output would change. So, an edge case is simply a set of input values that are specifically created to be edges for the Boolean expressions in our code.
For example, let’s look at the first Boolean logic statement, x + y > 10
. If we are simply considering whole numbers, then the possible edge cases for this Boolean expression are when the sum of x + y
is exactly
$ 10 $ and
$ 11 $. When it is
$ 10 $ or below, the result of the Boolean expression is False
, but when the result is
$ 11 $ or higher, then the Boolean expression is true
. So,
$ 10 $ and
$ 11 $ represent the edge between True
values and False
values. The same applies to the Boolean expression in the second if-else statement.
So, to truly test the edge cases in this example, we should come up with a set of values with the sum and difference of exactly
$ 10 $ and exactly
$ 11 $. For the first one, a very easy solution would be to set x
to
$ 10 $ and y
to
$ 0 $. In this case, both the sum and the difference is
$ 10 $, so we’re on the False
side of the edge. Our program will correctly execute branches 2 and 4.
For the other edge, we can use a similar set of inputs where x
is
$ 11 $ and y
is still
$ 0 $. In this case, both the sum and difference is
$ 11 $, so each Boolean expression will evaluate to True
and we’ll execute branches 1 and 3.
Why is this important? To understand that, we must think a bit about what was intended by this program. What if the program should execute branches 1 and 3 if the value is greater than or equal to
$ 10 $? In that case, both of our chosen edge cases should execute branches 1 and 3, but instead we saw that the edge case
$ 10 $ executed branches 2 and 4 instead. This is a simple logic error that happens all the time in programming - we simply forgot to use the greater than or equal to symbol >=
instead of the simple greater than symbol >
in our code. So, a minor typo like that can change the entire execution of our program, and we wouldn’t have noticed it in any of our previous tests. This is why it is important to test the edge cases along with other inputs.
In fact, it would probably be a good idea to add a third edge case, where the sum and difference equal
$ 9 $, just to be safe. That way, we can clearly see the difference between True
and False
values in this program.
So, the final set of values we may want to test this program with are listed below:
As we can see, even a simple program with just a few lines of code can require substantial testing to really be sure it works correctly! This doesn’t even include other types of testing, where we make sure it works properly with invalid input values, decimal numbers, and other situations. That type of testing is really outside of the scope of this lab, but we’ll learn more about some of those topics later in this course.
Let’s go through one more example to get some more practice with creating test inputs for conditional statements. For this example, let’s consider the following program in Python:
a = int(input("Enter a number: "))
b = int(input("Enter a number: "))
if a // b >= 5:
# Branch 1
print(f"{b} goes into {a} at least 5 times")
else:
# Branch 2
print(f"{a} is less than 5 times {b}")
if a % b == 0:
# Branch 3
print(f"{b} evenly divides {a}")
else:
# Branch 4
print(f"{a} / {b} has a remainder")
This program is a bit trickier to evaluate. First, it accepts two numbers as input. Then, it will check to see if the first number is at least 5 times the second number. It will print an appropriate message in either case. Then, it will check to see if the first number is evenly divided by the second number, and once again it will print a message in either case.
To achieve branch coverage, we need to come up with a set of inputs that will cause each print statement to be executed. Thankfully, the branches are numbered using comments in the Python code, so it is easy to keep track of them.
Let’s start with an easy input - we’ll set a
to
$ 25 $ and b
to
$ 5 $. This means that a // b
is equal to
$ 5 $, so we’ll go into branch 1 in the first if statement. The second if statement will go into branch 3, since a % b
is exactly
$ 0 $ because
$ 5 $ stored in b
will evenly divide the
$ 25 $ stored in a
without any remainder. So, we’ve covered branches 1 and 3 with this input.
For another input, we can choose
$ 21 $ for a
and
$ 5 $ for b
. In the first if statement, we’ll see that a // b
is now
$ 4 $, so we’ll go into branch 2 instead. Likewise, in the second if statement we’ll execute branch 4, since a % b
is equal to
$ 1 $ with these inputs. That means we’ve covered branches 2 and 4 with these inputs.
Therefore, we can achieve branch coverage with just two inputs:
Recall that there are four paths through a program that consists of two if statements in a row:
We’ve already covered the first and last of these paths, so we must simply find inputs for the other two.
One input we can try is
$ 31 $ and
$ 5 $. In the first if statement, we’ll go to branch 1 since a // b
will be
$ 6 $ this time. However, when we get to the second if statement, we’ll find that there is a remainder of
$ 1 $ after computing a % b
, so we’ll go to branch 4 instead. This covers the second path.
The third path can be covered by inputs
$ 20 $ and
$ 5 $. In the first if statement, we’ll go to branch 2 because a // b
is now
$ 4 $, but we’ll end up in branch 3 in the second if statement since a % b
is exactly 0.
Therefore, we can achieve path coverage with these four inputs:
What about edge cases? For the first if statement, we see the statement a // b >= 5
, so that clues us into the fact that we’ll probably want to try values that result in the numbers
$ 4 $,
$ 5 $, and
$ 6 $ for this statement. Thankfully, if we look at the inputs we’ve already tried, we can see that this is already covered! By being a bit deliberate with the inputs we’ve been choosing, we can not only achieve branch and path coverage, but we can also choose values that test the edge cases for various Boolean expressions as well.
Likewise, the Boolean statement in the second if statement is a % b == 0
, so we’ll want to try situations where a % b
is exactly
$ 0 $, and when it is some other value. Once again, we’ve already covered both of these instances in our sample inputs!
So, with a bit of thinking ahead, we can come up with a set of just 4 inputs that not only achieve full path and branch coverage, but also properly test the various edge cases that are present in the program!
Let’s try some simple practice problems. These problems are not graded - they are just for you to practice before doing the real exercises in the lab itself. You can find the answers below each question by clicking the button below each question.
Consider the following Python program:
a = int(input("Enter a whole number: "))
b = int(input("Enter a whole number: "))
if (a + b) % 3 == 0:
print("Branch 1")
else:
print("Branch 2")
a = a * 2
b = b * 3
if (a + b) % 3 == 0:
print("Branch 3")
else:
print("Branch 4")
What output is printed when the user inputs the values $ 2 $ and $ 4 $?
Consider the program in the previous question. Write a set of possible inputs to the program that will achieve path coverage. For each set of inputs, identify which branches will be executed when that input is provided to the program.
Think carefully! The same if statement is executed twice with different values, but those values are related to each other.
Consider the following Python program:
x = int(input("Enter an integer: "))
y = int(input("Enter an integer: "))
if x * 5 > y:
print("Branch 1")
else:
print("Branch 2")
if y % 5 == x:
print("Branch 3")
else:
print("Branch 4")
What output is displayed when the user inputs the values $ 4 $ and $ 29 $?
Consider the program in the previous question. Write a set of possible inputs to the program that will achieve path coverage. For each set of inputs, identify which branches will be executed when that input is provided to the program.
Write a complete Python that performs the following actions:
Your program should include at least two conditional statements.
Write a complete Python program in that file that performs the following actions:
Recall that you can use Boolean comparators such as <
and >
to compare two strings in Python. Also, pay special attention to the capitalization of the words “First” and “last” in the description above.
Your program should include at least two conditional statements.
Bonus food for thought: look at your completed answer - is there a path that is not possible to actually test? Why is that?
Conditional statements are a very powerful tool for programmers to use. So far in this lab, we’ve explored simple conditional statements, including the if statement and if-else statement in Python. However, up to this point we’ve only looked at how we can use a single conditional statement at a time in a program, which can be very limiting. Now we’re going to explore how we can combine conditional statements in a variety of different ways to build even more complex programs. These combined, or nested conditional statements, are commonly used across all programming languages, but learning how to build and debug them takes time and practice. We’ll work through several examples, and then you’ll get a chance to try it yourself.
To explore the various ways we can use conditional statements in code, let’s start by examining the same problem using three different techniques. We’ll use the classic Rock Paper Scissors game. In this game, two players simultaneously make one of three hand gestures, and then determine a winner based on the following diagram:
For example, if the first player displays a balled fist, representing rock and the second player displays a flat hand, representing paper, then the second player wins because “paper covers rock” according to the rules. If the players both display the same symbol, the game is considered a tie.
So, to make this work, we’re going to write a Python program that reads two inputs representing the symbols, and then we’ll use some conditional statements to determine which player wins. A basic skeleton of this program is shown below:
p1 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 1: ")
p2 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 2: ")
# determine the winner
Our goal, therefore, is to replace the comment # determine the winner
with a set of conditional statements and print statements to accomplish this goal.
First, let’s try to write this program using what we already know. We’ve already learned how to use conditional statements, and it is completely possible to write this program using just a set of linear conditional statements and nothing else. So, to do this, we need to determine each possible combination of inputs, and then write an if statement for each one. Since there are $ 3 $ possible inputs for each player, we know there are $ 3 * 3 = 9 $ possible combinations:
Player 1 | Player 2 | Output |
---|---|---|
rock | rock | tie |
rock | paper | player 2 wins |
rock | scissors | player 1 wins |
paper | rock | player 1 wins |
paper | paper | tie |
paper | scissors | player 2 wins |
scissors | rock | player 2 wins |
scissors | paper | player 1 wins |
scissors | scissors | tie |
We also have to deal with a final option where one player or the other inputs an invalid value. So, in total, we’ll have $ 10 $ conditional statements in our program!
Let’s first try to make a quick flowchart of this program. Since we need $ 10 $ conditional statements, our flowchart will have $ 10 $ of the diamond-shaped decision nodes. Unfortunately, that would make a very large flowchart, so we’ll only include the first three decision nodes in the flowchart shown here:
As we can see, this flowchart is very tall, and just consists of one decision node after another. Thankfully, we should know how to implement this in code based on what we’ve previously learned. Below is a full implementation of this program in Python, using just linear conditional statements:
p1 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 1: ")
p2 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 2: ")
if p1 == "rock" and p2 == "rock":
print("tie")
if p1 == "rock" and p2 == "paper":
print("player 2 wins")
if p1 == "rock" and p2 == "scissors":
print("player 1 wins")
if p1 == "paper" and p2 == "rock":
print("player 1 wins")
if p1 == "paper" and p2 == "paper":
print("tie")
if p1 == "paper" and p2 == "scissors":
print("player 2 wins")
if p1 == "scissors" and p2 == "rock":
print("player 2 wins")
if p1 == "scissors" and p2 == "paper":
print("player 1 wins")
if p1 == "scissors" and p2 == "scissors":
print("tie")
if not (p1 == "rock" or p1 == "paper" or p1 == "scissors") or not (p2 == "rock" or p2 == "paper" or p2 == "scissors"):
print("error")
This program seems a bit complex, but if we step through it one step at a time it should be easy to follow. For example, if the user inputs "scissors"
for player 1 and "rock"
for player 2, we just have to find the conditional statement that matches that input and print the correct output. Since we were careful about how we wrote the Boolean expressions for these conditional statements, we know that it is only possible for one of them to evaluate to true. So, we’d say that those Boolean expressions are mutually exclusive, since it is impossible for two of them to be true at the same time.
Things get a bit more difficult in the last conditional statement. Here, we want to make sure the user has not input an invalid value for either player. Unfortunately, the only way to do that using linear conditional statements is to explicitly check each possible value and make sure that the user did not input that value. This can seem pretty redundant, and it indeed is! As we’ll see later in this lab, there are better ways we can structure this program to avoid having to explicitly list all possible inputs when checking for an invalid one.
Before moving on, it is always a good idea to run this code yourself, either directly in Python or using Python Tutor, to make sure you understand how it works and what it does.
There is an alternative way we can write this program using linear if statements. Instead of having an if statement for each possible combination of inputs, we can instead have a single if statement for each possible output, and construct more complex Boolean expressions that are used in each if statement. Here’s what that would look like in Python:
p1 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 1: ")
p2 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 2: ")
if (p1 == "rock" and p2 == "rock") or (p1 == "paper" and p2 == "paper") or (p1 == "scissors" and p2 == "scissors"):
print("tie")
if (p1 == "rock" and p2 == "paper") or (p1 == "paper" and p2 == "scissors") or (p1 == "scissors" and p2 == "rock"):
print("player 2 wins")
if (p1 == "rock" and p2 == "scissors") or (p1 == "paper" and p2 == "rock") or (p1 == "scissors" and p2 == "paper"):
print("player 1 wins")
if not (p1 == "rock" or p1 == "paper" or p1 == "scissors") or not (p2 == "rock" or p2 == "paper" or p2 == "scissors"):
print("error")
In this example, we now have just four if statements, but each one now requires a Boolean expression that includes multiple parts. So, this program is simultaneously more and less complex than the previous example. It has fewer lines of code, but each line can be much trickier to understand and debug.
Again, feel free to run this code in either Python Tutor or directly in Python to confirm that it works and make sure you understand it before continuing.
In terms of style, both of these options are pretty much equivalent - there’s no reason to choose one over the other. However, we’ll see later in this lab, there are much better ways we can write this program using chaining and nesting with conditional statements.
The Python programming language has its own style guide, known as “PEP 8” to most Python programmers. One of the major conventions proposed in that guide is limiting the length of each line of code to just 79 characters, in order to make the code more readable. However, as we’ve seen above, it is very easy to exceed that length when dealing with complex Boolean expressions, and we’ll see this again as we add multiple levels of indentation when we nest conditional statements.
In this course, we won’t worry about line length, even though some text editors may mark those lines as being incorrect in Python. Likewise, in many examples we won’t wrap the lines to a shorter length, except for readability purposes in the videos and slides.
That said, it’s definitely a good style to try and follow, and we encourage you to think about ways you can write your code to keep it as concise and readable as possible. Having shorter lines of code, while still using descriptive variable and function names, is a good start!
File:Rock-paper-scissors.svg. (2020, November 18). Wikimedia Commons, the free media repository. Retrieved 16:57, February 28, 2022 from https://commons.wikimedia.org/w/index.php?title=File:Rock-paper-scissors.svg&oldid=513212597. ↩︎
In the previous example, we saw a set of linear if statements to represent a Rock Paper Scissors game. As we discussed on that page, the Boolean expressions are meant to be mutually exclusive, meaning that only one of the Boolean expressions will be true no matter what input the user provides.
When we have mutually exclusive Boolean expressions like this, we can instead use if-else statements to make the mutually exclusive structure of the program clearer to the user. Let’s see how we can do that.
To chain conditional statements, we can simply place the next conditional statement on the False
branch of the first statement. This means that, if the first Boolean expression is True
, we’ll execute the True
branch, and then jump to the end of the entire statement. If it is False
, then we’ll go to the False
branch and try the next conditional statement. Here’s what this would look like in a flowchart:
This flowchart is indeed very similar to the previous one, but with one major change. Now, if any one of the Boolean expressions evaluates to True
, that branch will be executed and then the control flow will immediately drop all the way to the end of the program, without ever testing any of the other Boolean expressions. This means that, overall, this program will be a bit more efficient than the one with linear conditional statements, because on average it will only have to try half of them before the program ends.
Now let’s take a look at what this program would look like in Python:
p1 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 1: ")
p2 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 2: ")
if p1 == "rock" and p2 == "rock":
print("tie")
else:
if p1 == "rock" and p2 == "paper":
print("player 2 wins")
else:
if p1 == "rock" and p2 == "scissors":
print("player 1 wins")
else:
if p1 == "paper" and p2 == "rock":
print("player 1 wins")
else:
if p1 == "paper" and p2 == "paper":
print("tie")
else:
if p1 == "paper" and p2 == "scissors":
print("player 2 wins")
else:
if p1 == "scissors" and p2 == "rock":
print("player 2 wins")
else:
if p1 == "scissors" and p2 == "paper":
print("player 1 wins")
else:
if p1 == "scissors" and p2 == "scissors":
print("tie")
else:
if not (p1 == "rock" or p1 == "paper" or p1 == "scissors") or not (p2 == "rock" or p2 == "paper" or p2 == "scissors"):
print("error")
As we can see, this program is basically the same code as the previous program, but with the addition of a number of else
keywords to place each subsequent conditional statement into the False
branch of the previous one. In addition, since Python requires us to add a level of indentation for each conditional statement, we see that this program very quickly becomes difficult to read. In fact, the last Boolean expression is so long that it doesn’t even fit well on the screen!
We can make a similar change to the other example on the previous page:
p1 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 1: ")
p2 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 2: ")
if (p1 == "rock" and p2 == "rock") or (p1 == "paper" and p2 == "paper") or (p1 == "scissors" and p2 == "scissors"):
print("tie")
else:
if (p1 == "rock" and p2 == "paper") or (p1 == "paper" and p2 == "scissors") or (p1 == "scissors" and p2 == "rock"):
print("player 2 wins")
else:
if (p1 == "rock" and p2 == "scissors") or (p1 == "paper" and p2 == "rock") or (p1 == "scissors" and p2 == "paper"):
print("player 1 wins")
else:
if not (p1 == "rock" or p1 == "paper" or p1 == "scissors") or not (p2 == "rock" or p2 == "paper" or p2 == "scissors"):
print("error")
Again, this results in very long lines of code, but it still makes it easy to see that the program is built in the style of mutual exclusion, and only one of the True
branches will be executed. As before, feel free to run these programs directly in Python or using Python Tutor to confirm they work and that you understand how they work before continuing.
Now that we’ve built a program structure that enforces mutual exclusion, we’ll might notice something really interesting - the final if
statement is no longer required! This is because we’ve already exhausted all other possible situations, so the only possible case is the last one. In that case, we can remove that entire statement and just replace it with the code from the True
branch. Here’s what that would look like in Python:
p1 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 1: ")
p2 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 2: ")
if p1 == "rock" and p2 == "rock":
print("tie")
else:
if p1 == "rock" and p2 == "paper":
print("player 2 wins")
else:
if p1 == "rock" and p2 == "scissors":
print("player 1 wins")
else:
if p1 == "paper" and p2 == "rock":
print("player 1 wins")
else:
if p1 == "paper" and p2 == "paper":
print("tie")
else:
if p1 == "paper" and p2 == "scissors":
print("player 2 wins")
else:
if p1 == "scissors" and p2 == "rock":
print("player 2 wins")
else:
if p1 == "scissors" and p2 == "paper":
print("player 1 wins")
else:
if p1 == "scissors" and p2 == "scissors":
print("tie")
else:
# All other options have been checked
print("error")
In this code, there is a comment showing where the previous if statement was placed, and now it simply prints an error. Again, this is possible because we’ve tried every possible valid combination of inputs in the previous Boolean expressions, so all that is left is invalid input. Try it yourself! See if you can come up with any valid input that isn’t already handled by the Boolean expressions - there shouldn’t be any of them.
To help build programs that include chaining conditional statements, Python includes a special keyword elif
for this exact situation. The elif
keyword is a shortened version of else if
, and it means to replace the situation where an if
statement is directly placed inside of an else
branch. So, when we are chaining conditional statements, we can now do so without adding additional levels of indentation.
Here’s what the previous example looks like when we use the elif
keyword in Python:
p1 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 1: ")
p2 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 2: ")
if p1 == "rock" and p2 == "rock":
print("tie")
elif p1 == "rock" and p2 == "paper":
print("player 2 wins")
elif p1 == "rock" and p2 == "scissors":
print("player 1 wins")
elif p1 == "paper" and p2 == "rock":
print("player 1 wins")
elif p1 == "paper" and p2 == "paper":
print("tie")
elif p1 == "paper" and p2 == "scissors":
print("player 2 wins")
elif p1 == "scissors" and p2 == "rock":
print("player 2 wins")
elif p1 == "scissors" and p2 == "paper":
print("player 1 wins")
elif p1 == "scissors" and p2 == "scissors":
print("tie")
else:
# All other options have been checked
print("error")
There we go! That’s much easier to read, and in fact it is much closer to the examples of linear conditionals on the previous page. This is the exact same program as before, but now it is super clear that we are dealing with a mutually exclusive set of Boolean expressions. And, we can still have a single else
branch at the very end that will be executed if none of the Boolean expressions evaluates to True
.
A structure of mutually exclusive statements like this is very commonly used in programming, and Python makes it very simple to build using the elif
keyword.
Using chained conditional statements like this makes it easy to detect and handle errors in the final else
block, since all other options have already been checked. However, some programmers prefer to explicitly check for errors in the input at the start of the code, before any other work is done. This is especially common when working with loops to prompt the user for new input in case of an error, which we’ll learn about in a later lab.
When you are reading code, it is important to check both the start and end of a block of code when looking for possible error checks, since they could be included in either place. Recognizing common coding styles and conventions such as where to check for errors will help us better understand code written by others, and also make our code more readable by others.
We’ve already seen how we can chain conditional statements by placing a new conditional statement inside of the False
branch of another conditional statement. If we think about that, however, that implies that we probably should be able to place conditional statements inside of the True
branch as well, or really anywhere. As it turns out, that’s exactly correct. We call this nesting, and it is really quite similar to what we’ve already seen in this lab.
Using nested conditional statements, we can really start to rethink the entire structure of our program and greatly simplify the code. Let’s take a look at how we can do that with our Rock Paper Scissors game.
In our Rock Paper Scissors game, we are really checking the value of two different variables, p1
and p2
. In all of our previous attempts, we built complex Boolean expressions that checked the values of both variables in the same expression, such as p1 == "rock" and p2 == "scissors"
. What if we simply checked the value of one variable at a time, and then used nested conditional statements in place of the and
operator? What would that look like?
Here’s an example of a Rock Paper Scissors game that makes use of nested conditional statements:
Here, we begin by checking if the value in p1
is "rock"
. If it is, then we’ll go to the True
branch, and start checking the values of p2
. If p2
is also "rock"
, then we know we have a tie and we can output that result. If not, we can check to see if it is "paper"
or "scissors"
, and output the appropriate result. If none of those are found, then we have to output an error.
In the False
branch of the first conditional statement, we know that p1
is not "rock"
, so we’ll have to check if it is "paper"
and "scissors"
. Inside of each of those conditional statements, we’ll also have to check the value of p2
, so we’ll end up with a significant number of conditional statements in total!
Let’s see what such a program would look like in Python:
p1 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 1: ")
p2 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 2: ")
if p1 == "rock":
if p2 == "rock":
print("tie")
elif p2 == "paper":
print("player 2 wins")
elif p2 == "scissors":
print("player 1 wins")
else:
print("error")
elif p1 == "paper":
if p2 == "rock":
print("player 1 wins")
elif p2 == "paper":
print("tie")
elif p2 == "scissors":
print("player 2 wins")
else:
print("error")
elif p1 == "scissors":
if p2 == "rock":
print("player 2 wins")
elif p2 == "paper":
print("player 1 wins")
elif p2 == "scissors":
print("tie")
else:
print("error")
else:
print("error")
In this example, we have an outer set of chained conditional statements checking the value of p1
, and then each of the branches will check the value of p2
and determine which output is correct. It is very similar in structure to the chained conditional example on the previous page, just laid out a bit differently. As before, try running this program yourself in either Python or Python Tutor to make sure it works and you understand how it is structured.
Looking at this code, one thing we might quickly notice is that we now have four places that print "error"
instead of just one. This is because we now have to check the values of p2
in three separate places, and can’t simply assume that the final else
case in the outermost conditional statement is the only case where an error might be found.
One way we can simplify this code is by including a specific conditional statement just to check for any error situations, and handle those upfront before the rest of the code. So, we can rewrite this code as shown here to accomplish that:
p1 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 1: ")
p2 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 2: ")
if not (p1 == "rock" or p1 == "paper" or p1 == "scissors") or not (p2 == "rock" or p2 == "paper" or p2 == "scissors"):
print("error")
else:
if p1 == "rock":
if p2 == "rock":
print("tie")
elif p2 == "paper":
print("player 2 wins")
else:
print("player 1 wins")
elif p1 == "paper":
if p2 == "rock":
print("player 1 wins")
elif p2 == "paper":
print("tie")
else:
print("player 2 wins")
else:
if p2 == "rock":
print("player 2 wins")
elif p2 == "paper":
print("player 1 wins")
else:
print("tie")
By confirming that both p1
and p2
only contain either "rock"
, "paper"
, or "scissors"
first, we can then make some pretty handy assumptions later in our code. For example, now the outermost conditional statement only explicitly checks if p1
contains "rock"
or "paper"
, and then the third block is simply an else
clause by itself. We can do this because we already know that "scissors"
is the only other possible value that can be stored in p1
, so we don’t have to explicitly check for it. We can make similar changes to the nested conditional statements as well. So, just by adding one complex Boolean expression and conditional statement to our program, we were able to remove 4 that we no longer needed!
In fact, we can even take this one step further. Now that we know that both p1
and p2
only contain valid values, we can easily determine if that match has ended in a tie by simply checking if the variables contain the same value. So, with a bit of restructuring, we can simplify our program as shown here:
p1 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 1: ")
p2 = input("Enter \"rock\", \"paper\", or \"scissors\" for player 2: ")
if not (p1 == "rock" or p1 == "paper" or p1 == "scissors") or not (p2 == "rock" or p2 == "paper" or p2 == "scissors"):
print("error")
else:
if p1 == p2:
print("tie")
elif p1 == "rock":
if p2 == "paper":
print("player 2 wins")
else:
print("player 1 wins")
elif p1 == "paper":
if p2 == "rock":
print("player 1 wins")
else:
print("player 2 wins")
else:
if p2 == "rock":
print("player 2 wins")
else:
print("player 1 wins")
This code presents a clear, easy to read version of a Rock Paper Scissors program. At the top, we receive input from the users, and then the first conditional statement is used to determine if the input is valid. If not, it will print an error.
If the input is valid, then we can make some assumption about the various possible inputs in our program’s logic, which greatly reduced the number of conditional statements. We are also checking for ties at the beginning, so in the False
branch of that conditional statement we can also assume that the values in p1
and p2
are different, further reducing the number of items we have to check.
We’ll revisit this example later in this course to show how we can convert the first conditional statement into a loop, which will prompt the user for new input if an invalid input is provided. This will make our program even better and easier for anyone to use.
Now that we’ve seen how we can chain and nest multiple conditional statements in our code, we need to address a very important concept: variable scope.
In programming, variable scope refers to the locations in code where variables can be accessed. Contrary to what we may think based on our experience, when we declare a variable in our code, it may not always be available everywhere. Instead, we need to learn the rules that determine where variables are available and why.
The example below demonstrates block scope, which is NOT how Python handles scope. We are using Python code so the example is understandable, but Python handles scope differently. See the next section for how Python actually handles scope.
First, let’s talk about the most common type of variable scope, which is block scope. This type of variable scope is used in many programming languages, such as Java, C#, C/C++, and more. In block scope, variables are only available within the blocks where they are declared, including any other blocks nested within that block. Also, variables can only be accessed by code executed after the variable is initially declared or given a value.
So, what is a block? Effectively, each function and conditional statement introduces a new block in the program. In languages such as Java and C/C++, blocks are typically surrounded by curly braces {}
. In Python, blocks are indicated by the level of indentation.
For example, consider the following Python code:
# Global block
x = int(input("Enter a number: "))
if x > 5:
# block A
y = int(input("Enter a number: "))
if y > 10:
# block B
z = 10
else:
# block C
z = 5
elif x < 0:
# block D
a = -5
else:
# block E
b = 0
print("?")
This program contains six different blocks of code:
Global
block, which is all of the code at the top level of the fileA
, which is all of the code inside of the True
branch of the outermost conditional statement.B
, the True
branch of the inner conditional statementC
, the False
branch of the inner conditional statementD
, the True
branch of the first elif
clause of the outer conditional statementE
, the False
branch of the outermost conditional statementInside of each of those blocks, we see various variables that are declared. For example, the variable x
is declared in the Global
block. So, with block scope, this means that the variable x
is accessible anywhere inside of that block, including any blocks nested within it.
Below is a list of all of the variables in this program, annotated with the blocks where that variable is accessible in block scope:
x
- blocks Global
, A
, B
, C
, D
, E
y
- blocks A
, B
, C
z
- blocks B
and C
more on this latera
- block D
b
- block E
One thing that is unique about this example is the variable z
, which is declared in both block B
and block C
. What does this mean when we are dealing with block scope? Basically, those variables named z
are actually two different variables! Since they cannot be accessed outside of the block that they are declared in, they should really be considered as completely separate entities, even though they are given the same name. This is one of the trickiest concepts when dealing with scope!
So, if this program is using block scope, what variables can be printed at the very end of the program, where the question mark ?
is found in a print()
statement? In this case, only the variable x
exists in the Global
block’s scope, so it is the only variable that we can print at the end of the program.
If we want to print the other variables, we must declare them in the appropriate scope. So, we can update our code as shown here to do that:
# Global block
# variable declarations in Global block
y = 0
z = 0
a = 0
b = 0
x = int(input("Enter a number: "))
if x > 5:
# block A
y = int(input("Enter a number: "))
if y > 10:
# block B
z = 10
else:
# block C
z = 5
elif x < 0:
# block D
a = -5
else:
# block E
b = 0
print("?")
With this change, we can now access all of the variables in the Global
scope, so any one of them could be printed at the end of the program. This also has the side effect of making the variable z
in both blocks B
and C
the same variable, since it is now declared at a higher scope.
Python, however, uses a different type of variable scope known as function scope. In function scope, variables that are declared anywhere in a function are accessible everywhere in that function as soon as they’ve been declared or given a value. In Python, the Global
scope is treated the same as a function’s scope, so variables declared in the Global scope in Python are accessible everywhere. So, once we see a variable in the code while we’re executing within a function or in the Global scope, we can access that variable anywhere within the same scope.
We haven’t introduced functions in Python quite yet, but it is important to go ahead and use the correct terminology here. For now, you can think of everything as being part of the “Global” function in Python. We’ll revisit this concept later when we introduce functions.
Let’s go back to the previous example, and look at that in terms of function scope
# Global block
x = int(input("Enter a number: "))
if x > 5:
# block A
y = int(input("Enter a number: "))
if y > 10:
# block B
z = 10
else:
# block C
z = 5
elif x < 0:
# block D
a = -5
else:
# block E
b = 0
print("?")
Using function scope, any of the variables x
, y
, z
, a
or b
could be placed in the print()
statement at the end of the code, and the program could work. However, there is one major caveat that we must keep in mind: we can only print the variable if it has been given a value in the program!
For example, if the user inputs a negative value for x
, then the variables y
, z
, and b
are never given a value! We can confirm this by running the program in Python Tutor. As we see here, at the end of the program, only the variables x
and a
are shown in the list of variables within the Global
frame in Python Tutor on the right:
Because of this, we have to be careful when we write our programs in Python using function scope. It is very easy to find ourselves in situations where our program will work most of the time, since it usually executes the correct blocks of code to populate the variables we need, but there may be situations where that isn’t guaranteed.
This is where testing techniques such as path coverage are so important. If we have a set of inputs that achieve path coverage, and we are able to show that the variables we need are available at the end of the program in each of those paths, then we know that we can use them.
Alternatively, we can build our programs in Python as if Python used block scope instead of function scope. By assuming that variables are only available in the blocks where they are given a value, we can always ensure that we won’t reach a situation where we are trying to access a variable that isn’t available. For novice programmers, this is often the simplest option.
Dealing with scope is a tricky part of learning to program. Thankfully, in Python, as well as many other languages, we can easily use tools such as Python Tutor and good testing techniques to make sure our programs are well written and won’t run into any errors related to variable scope.
Let’s go through another worked example to see how we can translate a problem statement into a working program. We’ll also take a look at how we can test the program to verify that it is working as intended.
Here’s a short and simple game that can be played by three players:
Three players each guess a positive integer greater than $ 0 $, and then share them simultaneously. The winner is chosen following this formula:
- If any two players have chosen the same number, the game is a tie.
- If all players have chosen even numbers, or all players have chosen odd numbers, then the smallest number wins.
- Otherwise, the largest number wins.
This game has some similarities to Rock Paper Scissors, but the logic is quite a bit different. So, let’s work through how we would build this program using conditional statements.
Our first step should be to build a simple program that handles user input. So, we’ll create a new Python program that contains three variables to store user input. We’ll also use the input()
function to read input, and the int()
function to convert each input to an integer. Below that, we’ll add some print statements for testing. At this point, our program should look similar to this:
p1 = int(input("Enter a positive integer for player 1: "))
p2 = int(input("Enter a positive integer for player 2: "))
p3 = int(input("Enter a positive integer for player 3: "))
# debugging statements
print(f"player 1 chose {p1}")
print(f"player 2 chose {p2}")
print(f"player 3 chose {p3}")
With this code in place, we’ve already created a Python program that we can run and test. So, before moving on, let’s run this program at least once to verify that it works correctly. This will help us quickly detect and correct any initial errors in our program, and make it much easer to debug logic errors later on.
So, when we run this program, we should see output similar to this:
Now that we have confirmed our program is working, let’s start writing the logic in this program. Deciding which conditional statement to write first is a very important step in designing a program, but it is difficult to learn what works best without lots of practice. If we look at the problem statement above, we see that there are several clear conditions that we’ll have to check:
However, there is one more condition that we should also keep in mind. This one isn’t clearly stated in the rules, but implied in the problem statement itself:
So, to write an effective program, we should first make sure that all of the inputs are greater than $ 0 $. We can do this using a simple conditional statement. We’ll also remove our debugging statements, as they are no longer needed:
p1 = int(input("Enter a positive integer for player 1: "))
p2 = int(input("Enter a positive integer for player 2: "))
p3 = int(input("Enter a positive integer for player 3: "))
if p1 <= 0 or p2 <= 0 or p3 <= 0:
print("Error")
else:
print("All numbers are greater than 0")
In this conditional statement, we are checking to see if any one of the numbers is less than or equal to $ 0 $. Of course, using some Boolean algebra and De Morgan’s law, we can see that this is equivalent to checking if all numbers are not greater than $ 0 $. Either approach is valid.
Once we know we have valid input, the next step in determining a winner is to first determine if there are any ties. For this, we simply need to check if any two players input the same number. Since there are three players, we need to have three different Boolean expressions to accomplish this. In our program, we could add a conditional statement as shown here:
p1 = int(input("Enter a positive integer for player 1: "))
p2 = int(input("Enter a positive integer for player 2: "))
p3 = int(input("Enter a positive integer for player 3: "))
if p1 <= 0 or p2 <= 0 or p3 <= 0:
print("Error")
elif p1 == p2 or p2 == p3 or p3 == p1:
print("Tie")
else:
print("Not a Tie")
This is a pretty simple Boolean expression that will check and see if any possible pair of inputs is equal. We use the or
Boolean operator here since any one of those can be true in order for the whole game to be a tie.
Next, let’s tackle whether the inputs are all odds or all evens. If we look at the rules above, this seems to be the next most logical thing we’ll need to know in order to determine the winner of the game. Recall that we can determine if a number is even or odd by using the modulo operator %
and the number
$ 2 $. If that result is
$ 0 $, the number is even. If not, the number is odd.
In code, we can express that in a conditional statement as shown here:
p1 = int(input("Enter a positive integer for player 1: "))
p2 = int(input("Enter a positive integer for player 2: "))
p3 = int(input("Enter a positive integer for player 3: "))
if p1 <= 0 or p2 <= 0 or p3 <= 0:
print("Error")
elif p1 == p2 or p2 == p3 or p3 == p1:
print("Tie")
elif p1 % 2 == 0 and p2 % 2 == 0 and p3 % 2 == 0:
print("All numbers are even")
elif p1 % 2 != 0 and p2 % 2 != 0 and p3 % 2 != 0:
print("All numbers are odd")
else:
print("Numbers are both even and odd")
Notice that we are using the and
Boolean operator in these conditional statements, because we want to be sure that all numbers are either even or odd.
In this example, we’re also choosing to use chained conditional statements with the elif
keyword instead of nesting the conditionals. This helps clearly show that each outcome is mutually exclusive from the other outcomes.
However, we chose to nest the program logic inside of the outermost conditional statement, which checks for valid input. This helps us clearly see the part of the program that is determining who wins the game, and the part of the program that is validating the input. Later on, we’ll see how we can rewrite that conditional statement into a loop to prompt the user for new input, so it makes sense for us to keep it separate for now.
Once we know if the numbers are either all even or all odd, we know that the winning number is the smallest number of the three inputs. So, how can we determine which number is the smallest? We can use a couple of nested conditional statements!
Let’s handle the situation where all numbers are even first. We know that the smallest number must be smaller than both other numbers. So, we can use a couple of Boolean expressions to check if that is the case for each number:
p1 = int(input("Enter a positive integer for player 1: "))
p2 = int(input("Enter a positive integer for player 2: "))
p3 = int(input("Enter a positive integer for player 3: "))
if p1 <= 0 or p2 <= 0 or p3 <= 0:
print("Error")
elif p1 == p2 or p2 == p3 or p3 == p1:
print("Tie")
elif p1 % 2 == 0 and p2 % 2 == 0 and p3 % 2 == 0:
if p1 < p2 and p1 < p3:
print("Player 1 wins")
elif p2 < p1 and p2 < p3:
print("Player 2 wins")
else:
print("Player 3 wins")
elif p1 % 2 != 0 and p2 % 2 != 0 and p3 % 2 != 0:
print("All numbers are odd")
else:
print("Numbers are both even and odd")
Here, we start by checking if player 1’s number is smaller than both player 2’s and player 3’s. If so, then player 1 is the winner. If not, we do the same for player 2’s number. If neither player 1 nor player 2 has the smallest number, then we can assume that player 3 is the winner without even checking.
As it turns out, we would end up using the exact same code in the situation where all numbers are odd, so for now we can just copy and paste that set of conditional statements there as well:
p1 = int(input("Enter a positive integer for player 1: "))
p2 = int(input("Enter a positive integer for player 2: "))
p3 = int(input("Enter a positive integer for player 3: "))
if p1 <= 0 or p2 <= 0 or p3 <= 0:
print("Error")
elif p1 == p2 or p2 == p3 or p3 == p1:
print("Tie")
elif p1 % 2 == 0 and p2 % 2 == 0 and p3 % 2 == 0:
if p1 < p2 and p1 < p3:
print("Player 1 wins")
elif p2 < p1 and p2 < p3:
print("Player 2 wins")
else:
print("Player 3 wins")
elif p1 % 2 != 0 and p2 % 2 != 0 and p3 % 2 != 0:
if p1 < p2 and p1 < p3:
print("Player 1 wins")
elif p2 < p1 and p2 < p3:
print("Player 2 wins")
else:
print("Player 3 wins")
else:
print("Numbers are both even and odd")
That covers the situations where all players have input either even or odd numbers. We can quickly test this program a couple of times by providing various inputs that match those cases. So, when we run this program, we should see output like this:
The last step is to determine the largest number in the case that the numbers are not all even or odd. Since we’ve already written some code to handle the opposite case, let’s quickly copy and tweak that code to handle this case. We can then place that code False
branch of our conditional statement handling the logic of the program:
p1 = int(input("Enter a positive integer for player 1: "))
p2 = int(input("Enter a positive integer for player 2: "))
p3 = int(input("Enter a positive integer for player 3: "))
if p1 <= 0 or p2 <= 0 or p3 <= 0:
print("Error")
elif p1 == p2 or p2 == p3 or p3 == p1:
print("Tie")
elif p1 % 2 == 0 and p2 % 2 == 0 and p3 % 2 == 0:
if p1 < p2 and p1 < p3:
print("Player 1 wins")
elif p2 < p1 and p2 < p3:
print("Player 2 wins")
else:
print("Player 3 wins")
elif p1 % 2 != 0 and p2 % 2 != 0 and p3 % 2 != 0:
if p1 < p2 and p1 < p3:
print("Player 1 wins")
elif p2 < p1 and p2 < p3:
print("Player 2 wins")
else:
print("Player 3 wins")
else:
if p1 > p2 and p1 > p3:
print("Player 1 wins")
elif p2 > p1 and p2 > p3:
print("Player 2 wins")
else:
print("Player 3 wins")
There we go! We’ve written a complete program that implements the problem statement given above. Did you think it would end up being this complex? Sometimes even a simple problem statement ends up requiring quite a bit of code to implement it fully.
The next step is to perform some testing of our program to make sure it is fully working. To do that, we need to come up with a set of inputs that would achieve branch coverage, and possibly even path coverage. However, this time our program is spread across three different sets of conditional statements, so it makes it a bit more difficult to do. So, let’s focus on each individual statement separately and see if we can find a set that work for each of them.
First, we see the top-level statement has 5 branches to cover, so we need to come up with 5 different sets of inputs in order to achieve branch coverage. We can keep the inputs very small, just to make our testing a bit simpler. Here are the 5 branches to cover, and an input that will reach each one:
-1, -1, -1
1, 1, 1
2, 4, 6
1, 3, 5
1, 2, 3
Notice how we are trying to come up with the simplest possible inputs for each branch? That will make it easier to combine these inputs with the ones used in other conditional statements to find an overall set of inputs that will achieve branch coverage for the entire program.
Next, we can look at the code to find the smallest number. This code is used when the inputs are either all even or all odd. So, we know that either inputs 2, 4, 6
or 1, 3, 5
will execute this code.
Within the code itself, we see three branches, depending on which player wins. So, if we start with the input 2, 4, 6
, we’ll see that this will execute the branch where player 1 wins. To execute the other branches, we can simply reorder the inputs:
2, 4, 6
4, 2, 6
4, 6, 2
That will achieve branch coverage for the “smallest” conditional statement, and even overlaps with one of the inputs used in the top-level statement.
The same applies to the conditional statement to find the largest number, but in this case we need a mix of even and odd numbers in the input. So, the input 1, 2, 3
will execute this code, and that input results in player 3 winning. Once again, we can reorder that input a bit to execute all three branches of this code:
3, 1, 2
1, 3, 2
1, 2, 3
Therefore, based on our analysis, we can achieve branch coverage across the entire program using 9 different inputs:
-1, -1, -1
1, 1, 1
2, 4, 6
4, 2, 6
4, 6, 2
1, 3, 5
3, 1, 2
1, 3, 2
1, 2, 3
This will execute each branch of all conditional statements in the program at least once.
Once we’ve achieved branch coverage, we should also quickly look at path coverage: is there any possible pathway through this program that we haven’t tested yet? As it turns out, there are two, but they are somewhat difficult to find. Can you spot them? See if you can figure it out before continuing on in this page.
The paths we missed are situations where the numbers are all odd, but a player other than player 1 wins. While we tested all possible branches in the “smallest” conditional statement, we didn’t test it using all odd numbers more than once. So, to truly achieve path coverage, we should add two more inputs to our list above:
-1, -1, -1
1, 1, 1
2, 4, 6
4, 2, 6
4, 6, 2
1, 3, 5
3, 1, 5
3, 5, 1
3, 1, 2
1, 3, 2
1, 2, 3
So, with a total of 11 inputs, we can finally say we’ve achieved both branch coverage and path coverage of this program.
Let’s try some simple practice problems. These problems are not graded - they are just for you to practice before doing the real exercises in the lab itself. You can find the answers below each question by clicking the button below each question.
Consider the following Python program:
p1 = int(input("Enter a positive integer for player 1: "))
p2 = int(input("Enter a positive integer for player 2: "))
p3 = int(input("Enter a positive integer for player 3: "))
if p1 <= 0 or p2 <= 0 or p3 <= 0:
print("Error")
else:
twos = (p1 + p2 + p3) // 2
threes = (p1 + p2 + p3) // 3
fours = (p1 + p2 + p3) // 4
if p1 == p2 or p2 == p3 or p3 == p1:
print("Tie")
elif p1 != twos and p2 != threes and p3 != fours:
print("Draw")
else:
if p1 == twos:
print("Player 1 Wins")
if p2 == threes:
print("Player 2 Wins")
if p3 == fours:
print("Player 3 Wins")
Explain, in your own words, a set of rules for the game that this program is simulating.
A fully correct answer is a succinct set of rules for the game and what leads to the various outcomes. A partially correct answer is a step-by-step description of each line in the program and the output it will produce based on the input.
Hint: it is possible for multiple players to win a round in this game.
Consider the Python program in the question above. Give a set of inputs that will achieve branch coverage for this program. You do not have to achieve path coverage!
Hint: try 3, 2, 1
as one possible input. What branches will that execute?
Write a complete Python program that meets the specification below.
A childcare supervisor has developed a set of rules to determine if children under their care may play indoors or outdoors. Here are the rules:
Write a program that will help determine if the children may play indoors, outdoors, or both. The program will prompt the user for four inputs in the following order:
yes
or no
.yes
or no
.yes
or no
.The program should output either indoors
, outdoors
, or both
, depending on the rules described above. Your program must make use of chained and/or nested conditional statements.
Hint: after reading input, the yes
and no
answers can be converted to Boolean values once and stored in Boolean variables. This will simplify the Boolean expressions used in the conditional statements.
In this lab, we introduced several major important topics in Python. Let’s quickly review them.
True
False
bool()
procedure to convert valuesFalse
, the value 0
, the value None
, or anything with 0 length, including the empty string, it will return False
.True
.and
or
not
==
equal!=
not equal<
less than<=
less than or equal to>
greater than>=
greater than or equal toStrings are compared using lexicographic order
not
and
or
if <boolean expression>:
<block of statements>
if <boolean expression>:
<block of statements 1>
else:
<block of statements 2>
False
to True
Conditional statements are mutually exclusive when only one of the many branches will be executed for any possible input.
if condition_1:
print("1")
else
if condition_2:
print("2")
else:
if condition_3:
print("3")
else:
print("4")
is equivalent to:
if condition_1:
print("1")
elif condition_2:
print("2")
elif condition_3:
print("3")
else:
print("4")
if condition_1:
if condition_2:
print("1 and 2")
elif condition_3:
print("1 and 3")
else:
print("1 and not 2 or 3")
elif condition_4:
if condition_2:
print("4 and 2")
elif condition_3:
print("4 and 3")
else:
print("4 and not 2 or 3")
else:
print("neither 1 nor 4")
Variable scope refers to what parts of the code a particular variable is accessible in. Python uses function scope, which means that a variable defined anywhere in a function is available below that definition in any part of the same function.
Other languages use block scope, where variables are only available within the block where they are defined.
Over the last few labs, we’ve explored how we can change the control flow of our programs using conditional statements. A program that doesn’t contain any conditional statements will have a linear control flow. Each time we run the program, it will execute the same steps in the same order, but it may use different data based on input from the user.
When a program contains a conditional statement, its control flow can split into different branches. This means that each program execution may take a different path through the program, but it will eventually reach the end. There is no way to go back and repeat a step that has already been done!
In this lab, we’re going to introduce the other important method for modifying the control flow of our programs - a loop or iteration statement. In programming, a loop is a construct that allows us to repeatedly execute the same piece of code, either for a set number of times, or while a particular Boolean condition is true
. Each time we execute the code inside the loop, we call that an iteration of the loop.
Loops are one of the most useful ways we can modify the control flow in our programs. With both loops and conditional statements in our list of available programming skills, we can write programs that execute different branches of code based on a Boolean expression, and we can also repeatedly execute the same piece of code either a defined number of times or while a Boolean expression evaluates to True
. This allows us to build some truly useful programs.
In this lab, we’re going to explore ways to use loops in Python. Python supports two different types of loops, while loops and for loops, both of which can be used in a variety of different ways in our programs. We’ll also explore how to use these loops to receive input from the user and handle the case where a user provides invalid input, and finally we’ll look at some ways to test our code containing loops to confirm that they are working correctly and won’t cause issues.
Let’s get started!
The first type of loop to explore in Python is the while loop. A while loop uses a Boolean expression, and will repeat the code inside of the loop as long as the Boolean expression evaluates to True
. These loops are typically used when we want to repeat some steps, but we aren’t sure exactly how many times it must be done. Instead, we typically know that there is some condition that must be True
while we repeat the code, and once it turns False
we can stop looping and continue with the program.
The general syntax for a while loop in Python is shown here:
while <boolean expression>:
<block of statements>
Notice that this syntax is very similar to an if statement. We start with the keyword while
, followed by some sort of a <boolean expression>
that will evaluate to either True
or False
. The Boolean expression in Python does not need to be placed inside of parentheses. After the Boolean expression is a single colon :
. Then, starting on the next line and indented one level is a <block of statements>
that will be executed when the Boolean expression evaluates to True
. Once again, just like in an if statement, we know which statements are part of the block within a while loop based solely on indentation.
It is possible for the Boolean expression to evaluate to False
initially, which will bypass the loop entirely and never execute the code inside of it. Likewise, if we aren’t careful, we can also write a loop where the Boolean expression will always be True
, and the loop will run infinitely, causing our program to lock up and never terminate.
To truly understand how a while loop works in Python, let’s go through a simple code tracing example in Python Tutor. Consider the following Python program:
x = int(input("Enter a number from 0 to 100: "))
total = 0
while total % 100 != x:
total = total + 9
print(f"The smallest multiple of 9 that ends in {x} is {total}")
See if you can figure out what this program does before moving on!
As always, you can copy and paste this code into Python Tutor, or click this Python Tutor link.
When we load this code in Python Tutor, we should see the usual default state. The first line of the program will prompt the user for input and store it in the variable x
.
For this example, let’s assume the user inputs the string "27"
.
Therefore, we’ll store the value
$ 27 $ in the variable x
.
The next line will store the value
$ 0 $ in the variable total
, as shown here:
At this point, we’ve reached the beginning of the while loop. So, we’ll need to determine if the Boolean expression evaluates to True
or False
. In this case, the value of total % 100
is equal to
$ 0 $, so it is not equal to the value stored in x
. Therefore, the Boolean expression is True
, and we should enter the loop.
Inside of the loop, we are simply adding
$ 9 $ to the value in total
. So, we’ll see that variable is updated, and the execution pointer goes back to the top of the loop.
Now that we are back at the top of the loop, we need to check the Boolean expression again. This time the value of total % 100
is
$ 9 $, which still isn’t equal to the value stored in x
, so we’ll enter the loop again.
We’ll add
$ 9 $ to the total
here, and jump to the top again.
Once again, we will check the Boolean expression and see that it is still True
, so we’ll enter the loop and add
$ 9 $ to the total
yet again before repeating the process from the top of the loop. At this point, we should see this state in Python tutor.
When we compute total % 100
, we get the value
$ 27 $, which is the same as the value stored in x
. This will make the Boolean expression evaluate to False
, so we can skip the loop and jump to the bottom of the program.
Here, we are simply printing out the output, and then the program will terminate.
A full animation of this program’s execution in Python Tutor is shown here.
Tracing the execution of a while loop is quite simple, especially with tools such as Python Tutor to help us make sure we’re updating the variables in the correct order.
Python also includes a second type of loop that is very useful, the for loop. A for loop is used when we want to repeat the steps a certain number of times. However, in Python, we can’t just say that we want to repeat something
$ 10 $ times. Instead, we use the built-in range()
function in Python to generate a list of numbers that we use in our loop. Then, our for loop will repeat once for each number in the list, and we can even access that number using an iterator variable
We’ll learn more about lists in Python in a later lab. For now, we’re just going to use the range()
function to generate them for use with for loops.
The range()
function can be used in three different ways, depending on the number of arguments given when calling the function:
range(stop)
- with a single argument, the range()
function will generate a list of numbers that begins at
$ 0 $ and stops before it reaches the stop
value. For example, range(10)
will generate a list of numbers from
$ 0 $ through
$ 9 $. This is great, since there will be
$ 10 $ numbers in total, so a for loop using range(10)
will repeat
$ 10 $ times.range(start, stop)
- with two arguments, the range()
function will generate a list of numbers that begins at start
and stops before it reaches the stop
value. So, range(3,8)
will generate the list [3, 4, 5, 6, 7]
. There will be stop - start
numbers in the range.range(start, stop, step)
- with three arguments, the range()
function will generate a list of numbers that begins at start
, increases by step
each time, and stops before the stop
value. Therefore, range(0, 10, 2)
will generate the list [0, 2, 4, 6, 8]
. We can also use a negative value for step
to count backwards. So, range(5, 0, -1)
will generate the list [5, 4, 3, 2, 1]
.You can read more about the Python range()
function in the Python Documentation
The general structure of a for loop in Python is shown here:
for <iterator variable> in <list>:
<block of statements>
The first time we reach a for loop, we’ll keep track of the <list>
that we’ll be iterating over. In general, once you start repeating in a for loop, the <list>
cannot be changed.
If the list contains at least one item, then we’ll store the first item in the list in the <iterator variable>
and enter the for loop to execute the code in the <block of statements>
When we reach the end of the block, we’ll jump back to the top and check to see if the <list>
contains another item. If so, we’ll store that item in the <iterator variable>
and execute the code in the <block of statements>
again.
This process will repeat until we’ve used every item in the <list>
. Then, we’ll jump to the end of the for loop and continue the program from there.
To really see how a for loop works in Python, let’s use Python Tutor to trace through a quick sample program. Consider this Python program:
x = int(input("Enter a number: "))
line = ""
for i in range(x):
line = line + "*"
print(line)
Before working through the code in Python Tutor, see if you can determine what this program does just by reading it!
To step through the code, we can load it into Python Tutor by copy and pasting it there, or by clicking this Python Tutor link.
We’ll start with the usual default state as shown here. The first line will read the input from the user.
For this example, let’s assume the user inputs the string "5"
:
This means that we’ll store the number
$ 5 $ in the variable x
. The program will also create an empty string value in the line
variable, which we’ll use to generate output.
At this point, we should have reached the beginning of the for loop.
Behind the scenes, Python will generate a list of numbers based on the range()
function. Since the range()
function is called with a single argument, we know it will be a list of numbers starting at
$ 0 $ and ending before it reaches the value in x
, which is
$ 5 $. So, the list will contain the numbers [0, 1, 2, 3, 4]
, and it will cause the for loop to repeat
$ 5 $ times. For the first iteration of the loop, Python will store the first number in the list,
$ 0 $, in the iterator variable i
. In programming, we typically use the variable name i
to represent our iterator variable. So, once the program has entered the loop, we should see this state:
Inside of the loop, the first statement will update the line
value by adding an asterisk *
to the end of the string. So, after this statement is executed, the line
variable will store a string containing a single asterisk.
The second statement will print the line
variable to the output.
At this point, we’ve reached the end of the for loop, so our execution pointer jumps back up to the top of the loop. Then, we check to see if the list we are iterating through contains more values. Again, this list isn’t shown in Python tutor, so we have to mentally keep track, but we can see the current value in i
, and we know what the list contains by looking at the range()
function. So, we’ll update i
to store the value
$ 1 $, and repeat the loop once again.
Just like before, this iteration will add an asterisk to the string in the line
variable, and then print that to the output before repeating once again.
There are still more items in the list, so we’ll update i
to the value
$ 2 $ and enter the loop:
In the loop, we’ll update line
to contain one more asterisk, and then print it before looping back to the top.
The list still isn’t empty, so we’ll update i
to be
$ 3 $ this time:
And then we’ll update line
and print it.
By now, we should have a pretty good idea of what this loop does. There’s one more item in the list, so we’ll update i
to be
$ 4 $:
Inside the loop, we’ll add one more asterisk to line
and then print it.
Finally, we’re back at the top of the loop. Since there are no more items left in the list, we can jump to the bottom of the for loop and continue the program from there.
In this case, there’s no more code left in the main()
function, so it will end and the program will terminate.
Looking at the output, we see that this program will print a right triangle of asterisks that is x
lines tall and x
characters wide at the bottom.
A full execution of this program is shown in this animation.
As we can see, working with for loops in Python is a quick and easy way to repeat a block of statements a specific number of times using the range()
function.
It is possible to convert any for loop using the range()
function in Python to a while loop. This can be done following a simple three-step pattern:
Let’s look at the code from the example above:
x = int(input("Enter a number: "))
line = ""
for i in range(x):
line = line + "*"
print(line)
To convert this for loop into a while loop, we can follow the three steps listed above. First, we must set an initial value for our iterator variable i
before the loop. Since the first value stored in i
is
$ 0 $, we’ll set it to that value.
x = int(input("Enter a number: "))
line = ""
i = 0
for i in range(x):
line = line + "*"
print(line)
Next, we need to update the value of the iterator variable at the end of each loop iteration. Since we didn’t provide an argument for the step
value in the range()
function, we know that i
will just be incremented by
$ 1 $ each time. So, we’ll add a short line to increment i
at the bottom of the loop:
x = int(input("Enter a number: "))
line = ""
i = 0
for i in range(x):
line = line + "*"
print(line)
i = i + 1
Finally, we can switch the for loop to a while loop. In the Boolean expression, we want to repeat the loop while the iterator variable i
has not reached the maximum value. So, we’ll use the Boolean expression i < x
in the new while loop:
x = int(input("Enter a number: "))
line = ""
i = 0
while i < x:
line = line + "*"
print(line)
i = i + 1
There we go! That’s the basic process for converting any for loop using range()
in Python to be a while loop. It’s a useful pattern to know and recognize when it is used in code.
Loops in Python are also a great way to handle situations where a user must input a value that meets certain criteria. Previously, we used an if statement to determine if the input was valid, but if it wasn’t valid all we could do was print an error and end the program. If we use a loop instead, we can prompt the user to provide additional input until we receive a valid value.
To make this process easy to use, we can develop a block of code just to handle input from the user. For example, if we want the user to input a percentage, we could use code similar to this:
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
while(x < 0 or x > 1):
print("Invalid Input!")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
This code starts by prompting the user to input a number, and stores it as a floating-point value in the variable x
. Then, it will reach the start of the while loop. The Boolean expression will check to see if the value provided is not a percentage, so if it is less than
$ 0 $ or greater than
$ 1 $ it is considered invalid.
If the input is invalid, we’ll print an error and prompt the user for input again. We’ll keep repeating this process until the user provides a value between
$ 0 $ and
$ 1 $, at which point the loop will terminate. At this point, we know that the value stored in x
is a valid value.
With code such as that in our program, we can use it to actually read input directly from the user. For example, we can write a quick program to calculate a weighted average of exam scores as shown below:
print("This program will compute weighted average for three exam scores")
print("Enter the first exam's score")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
while(x < 0 or x > 1):
print("Invalid Input!")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
exam1_score = x
print("Enter the first exam's weight")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
while(x < 0 or x > 1):
print("Invalid Input!")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
exam1_weight = x
print("Enter the second exam's score")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
while(x < 0 or x > 1):
print("Invalid Input!")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
exam2_score = x
print("Enter the second exam's weight")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
while(x < 0 or x > 1):
print("Invalid Input!")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
exam2_weight = x
print("Enter the third exam's score")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
while(x < 0 or x > 1):
print("Invalid Input!")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
exam3_score = x
print("Enter the third exam's weight")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
while(x < 0 or x > 1):
print("Invalid Input!")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
exam3_weight = x
total = exam1_score * exam1_weight + exam2_score * exam2_weight + exam3_score * exam3_weight
print(f"Your total score is {total}")
A quick execution of this program is shown here:
Take a minute to confirm that the program works by running it yourself, either in Python Tutor or directly in Python. You can even adapt this program to help calculate your final grade in this course!
Learning to how to create building blocks of larger programs, such as loop for checking input described here, is a great way to develop our programming skills. If we can learn to take a large program and break it down into smaller, repeatable chunks, then the entire program becomes much easier to develop and maintain.
Are you starting to ask yourself if there is a better way to handle repeated blocks of code like this? If so, that’s great! We’ll cover functions, which is a way to build repeatable pieces of code that can be used throughout our programs, in the next chapter!
Another important aspect of working with loops in code is learning how to properly test programs that contain loops. So, let’s look at some of the methods we can use when testing our programs containing loops to make sure they work correctly and won’t run into any problems.
Loops, like conditional statements, introduce different branches and paths into our programs. So, one of the first steps when testing these programs is to develop a set of inputs that will execute each branch of the code, as well as finding inputs that will execute multiple different paths through the program. Since loops may repeat many times, typically we only worry about two possible paths when dealing with loops - one path where it is skipped entirely and another path where the loop is entered at least once.
Let’s consider this simple Python program that contains loops:
x = int(input("Enter a number: "))
y = int(input("Enter a number: "))
i = 0
line = ""
while i < x + y:
line = line + "*"
print(line)
i = i + 1
while i > x - y:
print("=", end="")
i = i - 1
print()
This program will simply print a right triangle of asterisks *
that is x + y
characters wide and tall, similar to a previous example in this lab. However, it will also print a final line of equals signs =
below that, consisting of x - y
characters. To test this program, we want to try and find some input values for x
and y
that achieve branch and path coverage.
To achieve branch coverage, we want to execute each line of code in this program at least once. An easy way to do that is to provide inputs so that the sum of x
and y
is a positive number in order to enter the first loop. After the first loop terminates, the value of i
will be equal to the sum of x
and y
, provided that value is greater than
$ 0 $. So, in order to also enter the second loop, we need to make sure the difference of x
and y
is smaller than their sum.
A set of inputs that achieves this would be
$ 3 $ and
$ 1 $ for x
and y
, respectively. Their sum is
$ 4 $, so the first loop will be executed 4 times. Then, the difference between the values is
$ 2 $, so the second loop will executed twice to reduce the value in i
to
$ 2 $.
So, we can say that the inputs 3
and 1
are enough to achieve branch coverage.
To achieve path coverage, we must consider three additional paths through the program:
First, in order to enter neither loop, we must have a set of inputs where the sum is less than or equal to $ 0 $, and also where the difference is less than or equal to $ 0 $. The simplest way to achieve this is to set both inputs to exactly $ 0 $! Sometimes the simplest inputs are the easiest for testing.
To enter the first loop only, we need the situation where the sum of x
and y
is greater than 0
, but then their difference is even larger. An easy way to accomplish this is to set y
to a small negative number. For example, we could set x
to
$ 3 $ and y
to
$ -1 $. That will make their sum
$ 2 $ but the difference
$ 4 $, so we’ll only execute the first loop but not the second one.
Finally, if we want to execute the second loop only, we need to make both the sum and the difference of x
and y
a number less than
$ 0 $. In that case, we can simply set both x
and y
to negative values, but make sure that x
is overall a smaller value than y
. So, we could set x
to
$ -3 $ and y
to
$ -1 $. In that case, their sum is
$ -4 $ but their difference is
$ -2 $. That will cause the program to skip the first loop but enter the second loop.
So, we can achieve path coverage with these four sets of inputs:
3, 1
0, 0
3, -1
-3, -1
When testing loops, we should also check to make sure that the loops will eventually terminate if we ever enter them. Thankfully, in this program, it is very easy to reason about this and convince ourselves that it works.
Consider the first loop - the only way to enter that loop is if the value of i
is less than the sum of x
and y
, and each time the loop iterates the value in i
will be incremented by
$ 1 $. Since i
is getting larger each time, eventually it will reach a value that is greater than or equal to x + y
and the loop will terminate.
The same argument can be made for the second loop. In that loop, i
is initially greater than the difference of x
and y
, but i
is decremented each time, so eventually the loop will terminate.
Technically speaking, we can come up with a loop variant for each loop and show that it is monotonically decreasing. This is easily done by simply finding the difference between the iterator variable i
and the termination condition of the loop, either the sum or the difference of x
and y
. We won’t ask you to get to this level of detail when testing your own loops, but it is a very useful concept to understand when working with loops.
There we go! We were able to quickly come up with a set of inputs that will execute all possible branches and paths in this program, and we can also show that each of the loops will properly terminate anytime they are executed. So, we know that our program at least won’t crash, no matter what integers are provided as input. We can also examine the output of each test and make sure that it matches what the expected output should be in order to show that our program not only doesn’t crash, but that it provides the correct output.
Now that we’ve learned all about using loops in Python, let’s go through a complete worked example to see how all the pieces fit together. As we continue to develop larger and more complex programs, it is helpful to observe the entire process from start to finish so we can see how to easily develop a program one step at a time. Once we’ve created our program, we’ll also perform a bit of testing to make sure that it works correctly.
For this example, let’s write a program to play a simple number guessing game. Here’s how it works:
The game will select two random numbers from $ 0 $ to $ 100 $, one for each player. Then, each player will guess a number in the range, and the game will print either “higher” if that player’s secret number is larger than the guess, or “lower” if the player’s secret number is smaller than the guess. Players will alternate turns until one player correctly guesses their secret number and wins the game.
This game is pretty simple, but there are still quite a few different parts that we need to build in order to write the entire program. So, let’s go through the process of building a complete program in Python to play this game.
Before we start building our program, we need some way to generate a random number. Thankfully, Python has a built-in library called random that has many functions for working with random numbers in our programs.
To use a library in Python, we must first import it into our code. This is done using a simple import
statement at the very top of our program’s code, followed by the name of the library we want to use. So, for this library, we’ll need to have the following import
statement at the top of our source code:
import random
Once we’ve done that, we can use the function random.randint(a, b)
to generate a random number that is between a
and b
, inclusive. Mathematically, we can say that it generates an integer
$ x $ such that
$ a <= x <= b $.
So, here’s a quick test program we can use to explore how to use random numbers in Python:
import random
a = int(input("Enter a minimum value: "))
b = int(input("Enter a maximum value: "))
x = random.randint(a, b)
print(f"A random number between {a} and {b} is {x}")
Take a minute to run this program and modify it a bit to make sure you understand how to work with random numbers in Python before continuing.
For this program, we need to start by generating two random numbers from
$ 0 $ to
$ 100 $, one for each player. So, we can create and store those numbers in the code, and we’ll also need to import the random
library at the top of our file.
import random
p1_secret = random.randint(0, 100)
p2_secret = random.randint(0, 100)
Once we have the secret numbers, then we can sketch out the game itself. We’ll use a while loop to repeat until a player has guessed correctly, and we’ll also use a simple integer variable to keep track of the current player. Inside of the loop, we should get a new guess from a player, and also switch between players. So, let’s add that to our program structure using some comments:
import random
p1_secret = random.randint(0, 100)
p2_secret = random.randint(0, 100)
player = 1
while( ): # player has not guessed correctly
# swap player
# get new guess from player
# check if secret is higher or lower
From here, we can start to work on the actual logic in our program.
The trickiest part of this program is coming up with the correct Boolean expression to use in our while loop. That helps us determine if the player has guessed correctly and that the loop should stop. However, this is a bit complex since there are two different players, each one with their own secret number. So, in our Boolean expressions, we have to determine what the current player is as well as whether the most recent guess was correct.
To do this, we can use the and
and or
Boolean operators to combine several Boolean expressions together. For example, we can determine if the current player is player 1 using player == 1
, and then we can determine if the most recent guess is equal to player 1’s secret number using guess == p1_secret
. If we want both of those to be True
in order to exit the loop, we can combine them using the and
operator. We can do something very similar for player 2 as well. Then, if we want the loop to terminate if either of those is true, we can combine the two statements using the or
operator.
So, all together, we want to repeat the loop as long as that entire statement is not True
. So, the full Boolean expression would be:
not ((player == 1 and guess == p1_secret) or (player == 2 and guess == p2_secret))
That’s pretty complex, but it easily encompasses all of the rules of the game!
To get input from the user, we can write a simple loop to handle input is very similar to the ones we saw earlier in this lab. We simply want to make sure the user has provided a value that is between $ 0 $ and $ 100 $, and prompt the user for another input if not. We’ll also add in a variable to print the current player in the prompt for input.
So, our loop to get input might look something like this:
x = int(input(f"Enter a guess for player {player}: "))
while x < 0 or x > 100:
print("Invalid Input!")
x = int(input(f"Enter a guess for player {player}: "))
Then, in our code, we can simply use the new input loop to read a player’s current guess as shown here:
import random
p1_secret = random.randint(0, 100)
p2_secret = random.randint(0, 100)
player = 1
while(not ((player == 1 and guess == p1_secret) or (player == 2 and guess == p2_secret))): # player has not guessed correctly
# swap player
x = int(input(f"Enter a guess for player {player}: "))
while x < 0 or x > 100:
print("Invalid Input!")
x = int(input(f"Enter a guess for player {player}: "))
guess = x
# check if secret is higher or lower
Next, we need to add some conditional statements to check if the secret is higher or lower than the guess, and also to swap between players. First, let’s look at swapping players. This is pretty simple, since all we need to do is find out what the current player is, and switch that value to the other player. So, we can do that using a simple if statement as shown here:
import random
p1_secret = random.randint(0, 100)
p2_secret = random.randint(0, 100)
player = 1
while(not ((player == 1 and guess == p1_secret) or (player == 2 and guess == p2_secret))): # player has not guessed correctly
if player == 1:
player = 2
else:
player = 1
x = int(input(f"Enter a guess for player {player}: "))
while x < 0 or x > 100:
print("Invalid Input!")
x = int(input(f"Enter a guess for player {player}: "))
guess = x
# check if secret is higher or lower
The process for checking a guess is very similar - we can simply check to see if the guess is higher or lower than the secret for the current player, and print the output accordingly. First, we’ll need to figure out the current player:
if player == 1:
# check player 1's secret
else:
# check player 2's secret
Then, in each of those branches, we’ll check the guess:
if player == 1:
if guess < p1_secret:
print("Higher")
elif guess > p1_secret:
print("Lower")
else:
print("Correct!")
else:
if guess < p2_secret:
print("Higher")
elif guess > p2_secret:
print("Lower")
else:
print("Correct!")
This is a pretty straightforward set of conditional statements, but it goes to show how many lines of code are required even for a simple guessing-game program like this one.
So, at this point, we can put all of the pieces together and test our program. Here is the complete program that we’ve written so far:
import random
p1_secret = random.randint(0, 100)
p2_secret = random.randint(0, 100)
player = 1
while(not ((player == 1 and guess == p1_secret) or (player == 2 and guess == p2_secret))): # player has not guessed correctly
if player == 1:
player = 2
else:
player = 1
x = int(input(f"Enter a guess for player {player}: "))
while x < 0 or x > 100:
print("Invalid Input!")
x = int(input(f"Enter a guess for player {player}: "))
guess = x
if player == 1:
if guess < p1_secret:
print("Higher")
elif guess > p1_secret:
print("Lower")
else:
print("Correct!")
else:
if guess < p2_secret:
print("Higher")
elif guess > p2_secret:
print("Lower")
else:
print("Correct!")
So, let’s try to run this program and see if it works. Unfortunately, right from the start we have an error in our code, since when we try to execute this program, we’ll get the following error:
Take a minute to see if you can spot and fix the error before continuing!
Unfortunately, in our Boolean expression inside of the while loop, we’re referencing the guess
variable, but that variable isn’t actually created until later inside the while loop itself. So, we’ll need to set the guess
variable to a value outside of the while loop first.
Also, we must be very careful about what value we use. If we set guess
to be
$ 0 $ initially, there is a very small chance that player 1 could win the game immediately if their secret number is
$ 0 $, since that is a valid guess. Instead, let’s set guess
to be
$ -1 $ so that it will never be equal to a secret number.
After we make that change, when we run the program again, we may notice one other error:
Can you spot it by looking at this screenshot?
Unfortunately, our program starts with player 2’s turn instead of player 1. Why is that? Well, if we look closely at our code, we see that we are initially setting the player to player 1, but then immediately switching players inside of the while loop before we prompt for input. If we think about it, we can’t move the code that swaps players to later in the while loop, because then our Boolean expression to determine if the while loop should terminate will be incorrect. So, instead, we can simply initialize the player
variable to be
$ 2 $, and then it will be switched to
$ 1 $ before we prompt for input the first time.
So, our final working program’s code with these two changes is shown here:
import random
p1_secret = random.randint(0, 100)
p2_secret = random.randint(0, 100)
player = 2
guess = -1
while(not ((player == 1 and guess == p1_secret) or (player == 2 and guess == p2_secret))): # player has not guessed correctly
if player == 1:
player = 2
else:
player = 1
x = int(input(f"Enter a guess for player {player}: "))
while x < 0 or x > 100:
print("Invalid Input!")
x = int(input(f"Enter a guess for player {player}: "))
guess = x
if player == 1:
if guess < p1_secret:
print("Higher")
elif guess > p1_secret:
print("Lower")
else:
print("Correct!")
else:
if guess < p2_secret:
print("Higher")
elif guess > p2_secret:
print("Lower")
else:
print("Correct!")
A full example of running this program, complete with a very lucky guess, is shown in the screenshot below:
Now that we have a complete and working program, let’s look at what it might take to test it. Unfortunately, testing a program that uses random numbers can be very tricky, since each time you run the program you might receive different outputs even if you provide the same inputs.
Thankfully, we can still perform some testing to achieve branch and path coverage by choosing our guesses carefully. Here are the basic methods we’ll want to try:
"Higher"
for output, unless the secret number is
$ 0 $."Lower"
for output, unless the secret number is
$ 100 $."Correct"
. Using a bit of logic, it is always possible to win the game in no more than
$ 7 $ guesses."Invalid Input!"
for output at least once.Following those simple strategies and keeping track of the various outputs received, it is pretty easy to eventually test all branches and most, if not all, possible paths through the program.
Checking for loop termination on this program is a bit trickier, since the secret numbers are random and the guesses that a player makes are also unpredictable. So, while we can’t say for sure that the program will terminate, we can always prove that there is a possible condition where the loop will terminate, and that condition depends entirely on the user providing the correct input. So, as long as it is possible, we can say that the loop is at least not an infinite loop, and the program will work correctly if the user makes the correct guess.
There we go! We’ve just explored a complete example of writing a program in Python, complete with loops and conditional statements. We also introduced random numbers and how we can easily use those in our programs. Beyond that, we explored how to test these programs and correct minor errors found in testing. Hopefully this process is very helpful as we continue to work on larger and more complex programs.
Let’s try some simple practice problems. These problems are not graded - they are just for you to practice before doing the real exercises in the lab itself. You can find the answers below each question by clicking the button below each question.
Consider the following pseudocode program:
x = int(input("Enter a positive integer: "))
while(x <= 0):
print("Invalid Input!")
x = int(input("Enter a positive integer: "))
a = 1
for i in range(9):
if x % a == 0:
print(f"{a}")
a = a + 1
Explain, in your own words, the output that this program produces in relation to the input provided.
A fully correct answer is a succinct description of the output as it relates to the input. A partially correct answer is a step-by-step description of each line in the program and the output it will produce based on the input.
Consider the program above. Devise a set of inputs that will cause each of the 9 possible numerical outputs to print at least once, and for each input, list the numbers that will be printed as output.
Consider the following Python program:
x = int(input("Enter a positive integer: "))
while(x <= 0):
print("Invalid Input!")
x = int(input("Enter a positive integer: "))
a = ""
while x > 0:
if x % 2 == 0:
a = "0" + a
else:
a = "1" + a
x = x // 2
print(a)
Explain, in your own words, the output that this program produces in relation to the input provided.
A fully correct answer is a succinct description of the output as it relates to the input. A partially correct answer is a step-by-step description of each line in the program and the output it will produce based on the input.
Hint: when numbers are stored digitally on a computer, what numerical format is used?
Consider the Python program above. Devise a set of inputs that you feel are sufficient to test the program, and briefly explain why those inputs are sufficient.
Hint: Achieving coverage is very simple, but providing enough input to be confident that the program is generating the correct output should also be considered.
Write a complete Python program that meets the specification below.
The Fibonacci Sequence is a sequence of numbers where the next number is the sum of the previous two numbers. The sequence starts with the numbers $ 1 $ and $ 1 $, making the next number $ 2 $. The following number is $ 3 $, since it is the sum of the second and third number of the sequence. The sequence continues indefinitely.
For this program, ask the user to input a number that must be greater than $ 5 $. If the user inputs a number that is $ 5 $ or less, display an error and prompt the user for another input until a valid input is received.
Then, determine if the number provided as input is part of the Fibonacci Sequence. This can be done by computing the sequence one number at a time until the desired number is reached or passed. Print either "In sequence"
or "Not in sequence"
depending on the result found.
Write a complete Python program that meets the specification below.
Write a program that will print the first $ 30 $ multiples of a given number, but only multiples where the last digit is equal to a second given number.
For this program, ask the user to provide two integers - the first between $ 1 $ and $ 100 $, inclusive, and the second between $ 0 $ and $ 9 $, inclusive. If the user provides an invalid value for either input, print an error message and prompt the user for another input until a valid input is received.
Then, the program should compute the first $ 30 $ multiples of the first given number. This would be equivalent to multiplying the number by $ 1 $ all the way through multiplying the number by $ 30 $. Then, if that computed multiple ends with the same digit as the second number, it should be printed. Otherwise, no output is produced for that number.
For example, if the first given number is $ 9 $ and the second number is $ 1 $, then the output should be:
81
171
261
Your program should make use of a for loop and the Python range()
function.
Hint: what is the value of 81 % 10
? What about 27 % 10
? Does that value help you determine whether the number should be printed or not?
Up to this point, we explored how we can use iterative structures in our code, such as while loops and for loops, to repeat steps a certain number of times or while a Boolean condition is true. This is a very powerful tool, since it allows us to build programs that can repeatedly ask the user to provide input until a valid value is received, or even perform a repeated calculation until it reaches a desired result.
Loops, just like conditional statements, can also be nested inside of one another, allowing us to build even more complex programs. In fact, you may have already done this using loops and code that contains loops in a previous lab without realizing it. Next, we’ll explore how to combine loops in many different ways, and learn how we can test and debug these complex programs.
Later in this course, we’ll learn about two different collection types in Python, and we’ll quickly see how we can use loops to work with them quickly and easily.
To create a nested loop in Python, we can simply place a loop structure inside of the block of statements that is repeated by another loop structure. This is very similar to how we can nest conditional statements as well.
For example, consider this short Python program:
i = 1
while i < 10:
j = i
while j < 10:
print(f"{j} ", end="")
j = j + 1
print("")
i = i + 1
print("Complete!")
In this example, we see a Python while loop that uses the variable i
in its Boolean expression first, and then inside of that loop we see another loop that uses j
in its Boolean expression. When we run this code, each time we execute the steps inside of the outer while loop, we’ll have to completely go through the inner while loop as well. It can be very complex to keep track of everything, but with a bit of practice we’ll learn some strategies for mentally working through loops.
Before continuing, take a look at that program’s code and see if you can determine what it will print!
To really understand how a set of nested loops work, let’s go through a code tracing example using Python Tutor. To follow along, copy this code to Python Tutor or click this Python Tutor link to load Python Tutor in a web browser.
When we first load this code in Python Tutor, we should see the following state:
When we look at the bottom of the Python Tutor page, we’ll see that we are on step 1 of 183 steps! That’s a very large number of steps to deal with! Up to this point, most of our programs have been around 20 or 30 steps in total. So, already we’re seeing that nested loops can quickly create programs that execute many more steps.
Here, we are setting up the iterator variable i
that is used for the outermost loop. So, we’ll store the value $1$ in i
and then move to the next line:
Now we’re at the start of our outer while loop. That means that we’ll need to evaluate the Boolean expression and determine if it is True
or False
. In this case, we can see that i < 10
will evaluate to True
, so we should enter the loop.
Once we are inside the loop, we’ll start setting up the iterator variable j
for the inner loop. In this code, we are storing the current value of i
in j
, so at this point j
will also contain $1$.
At this point, we need to decide if we should enter the inner while loop. So, once again we’ll look at the Boolean expression, j < 10
, and see that it is also True
and we should enter the loop.
Inside of the inner while loop, we’ll perform two actions. First, we’ll print the current value of j
to the output, but we won’t move to the next line since the end
parameter is set to an empty string in the print()
function:
Then, we’ll increment the value of j
by $1$ before looping back to the top of the innermost loop:
Notice that when we reach the end of the innermost loop, we jump back to the beginning of that loop, NOT to the beginning of the outermost loop. This is an important concept to learn - since we are only dealing with the inner loop at this point, we must continue to repeat its steps until the Boolean expression evaluates to False
. So, since the Boolean expression j < 10
still evaluates to True
, we should enter the loop once again.
Inside the loop, we’ll print the current value of j
to the same line of output as before:
And once again we’ll increment the value of j
by $1$ and jump back to the top of the innermost while loop:
Hopefully at this point we have a pretty good idea of what each step of the innermost while loop does. It will simply print the value of j
and increment it by $1$, repeating those steps until j
is greater than or equal to $10$. So, once the inner while loop terminates, we’ll see this state:
We jumped from step 10 to step 32 just to complete the inner while loop. Finally, at this point we’ll print an empty string, which will move our output to the next line, and then we’ll increment the value in i
by $1$ before looping to the top of the outer while loop:
Now we must check to see if we should enter the outer while loop again by checking the Boolean expression i < 10
. Since that evaluates to True
, we’ll enter the loop a second time.
Inside the loop, we’ll reset the value of j
to be the current value stored in i
, which is now $2$, and then we’ll reach the inner while loop:
Here, we can evaluate the Boolean expression j < 10
, which is currently True
, and determine that we should enter the inner while loop.
Inside the loop, the code hasn’t changed, so we can use our understanding from before to quickly figure out what this loop does. It will print the current value in j
and then increment j
by $1$, so we’ll end up at this state:
At this point, we can easily assume that the inner loop will do pretty much the same thing as before - it will print all of the values from i
up through $9$ to the output, all on the same line. So, once the inner loop has completely finished, we’ll see the following state in Python Tutor:
We were able to quickly jump from step 42 all the way to step 64 just by understanding what the inner loop is doing and extrapolating from our previous experience. Now, we can finish up this iteration of the outer loop by printing a newline and then incrementing i
by $1$, and then we’ll be back at the beginning of the outer while loop:
At this point, we’ve completely worked through two iterations of the outermost while loop, including two complete executions of the innermost while loop. So, we’re at a good point to make a prediction about the output of the program as a whole, without executing the next 120 steps. It looks like the inner loop will print the values from i
through $9$ on a single line, and then each line will start with the value of i
being incremented by $1$ each time. So, overall, we can expect the entire outer while loop to produce the following output:
1 2 3 4 5 6 7 8 9
2 3 4 5 6 7 8 9
3 4 5 6 7 8 9
4 5 6 7 8 9
5 6 7 8 9
6 7 8 9
7 8 9
8 9
9
And, indeed, if we jump ahead to the last line of the program we’ll see exactly that situation in Python Tutor:
The program ends by printing the string "Complete!"
. At the end of the program, we’ll see the following state:
Working through an example program such as this one is a great way to explore how nested loops work in Python
Writing code that has nested while loops can be quite tricky, as there are several pitfalls that we might encounter. Here are a few tips to keep in mind when designing code that uses nested while loops:
Nested loops present a very difficult challenge for programmers, because they are short snippets of code that may end up resulting in hundreds or even thousands of individual steps to be executed. So, anything we can do to make the loops simpler and easier to understand will greatly improve our ability to write programs with fewer bugs.
For loops can also be nested, just like while loops. In fact, nesting for loops is often much simpler than nesting while loops, since it is very easy to predict exactly how many times a for loop will iterate, and also because it is generally easier to determine if a for loop will properly terminate instead of a while loop.
A great way to explore using nested for loops is by printing ASCII Art shapes. For example, consider the following Python program that contains nested for loops:
for i in range(3):
for j in range(5):
print("* ", end="")
print("")
Just by looking at the code, can you predict what shape it will print? We can check the result by running the program on the terminal directly. If we do so, we should receive this output:
* * * * *
* * * * *
* * * * *
It printed a rectangle that is $ 3 $ rows tall (the outer for loop) and $ 5 $ columns wide (the inner for loop). If we look back at our code, this hopefully becomes very clear. This is a very typical structure for nested for loops - the innermost loop will handle printing one line of data, and then the outer for loop is used to determine the number of lines that will be printed.
The process for dealing with nested for loops is nearly identical to nested while loops. So, we won’t go through a full example using Python Tutor. However, feel free to run any of the examples in this lab in Python Tutor yourself and make sure you clearly understand how it works and can easily predict the output based on a few changes.
Let’s look at a few more examples of nested for loops and see if we can predict the shape that is created when we run the code.
for i in range(5):
for j in range(i + 1):
print("* ", end="")
print("")
This time, we’ve updated the inner for loop to use range(i + 1)
, so each time that inner loop is reached, the range will be different based on the current value of i
from the outer loop. So, we can assume that each line of text will be a different size. See if you can figure out what shape this will print!
When we run the code, we’ll get this output:
*
* *
* * *
* * * *
* * * * *
We see a triangle that is
$ 5 $ lines tall and
$ 5 $ columns wide. If we look at the code, we know that we’ll have 5 rows of output based on the range(5)
used in the outer loop. Then, in the inner loop, we can see that the first time through we’ll only have a single item, since i
is
$ 0 $ and the range used is range(i + 1)
.
The next iteration of the outer loop will have
$ 1 $ stored in i
, so we’ll end up repeating the inner loop
$ 2 $, or i + 1
, times. We’ll repeat that process until the final line, which will have
$ 5 $ characters in it to complete the triangle.
From here, we can quickly modify the program to change the structure of the triangle. For example, to flip the triangle and make it point downward, we can change the range in the inner loop to range(i, 5)
instead!
What if we want to flip the triangle along the vertical axis, so that the long side runs from the top-right to the bottom-left? How can we change our nested loops to achieve that outcome?
In that case, we’ll not only have to print the correct number of asterisks, but we’ll also need to print the correct number of spaces on each line, before the asterisks. One possible way to achieve this is shown in this code:
for i in range(5):
for j in range(4 - i):
print(" ", end="")
for j in range(i + 1):
print("* ", end="")
print("")
Here, we have two for loops nested inside of our outer loop. The first loop creates a range using the expression 4 - i
, and the second loop uses the expression i + i
. So, when i
is equal to
$ 0 $ during the first iteration of the outer loop, the first inner loop will be executed
$ 4 $ times, and the second loop only
$ 1 $ time. Then, on the next iteration of the outer loop, the first inner loop will only run
$ 3 $ times, while the second loop now runs
$ 2 $ times. Each time, the sum of the iterations will be
$ 5 $, so our lines will always be the same length.
When we run the code, we’ll get this output:
*
* *
* * *
* * * *
* * * * *
Up to this point, we’ve just been writing loops to print shapes using ASCII art. While that may seem trivial, understanding the “shape” that will be produced by a given set of nested loops can really help us understand how the code itself will function with other data.
Consider this example code:
total = 0
count = 0
for i in range(10):
for j in range(i + 1):
total = total + (i * j)
count += 1
print(f"sum: {total}, count: {count}")
main()
Before we even try to run this code, can we guess what the final value of the count
variable will be? Put another way, can we determine how many times we’ll execute the code in the innermost loop, which will help us understand how long it will take for this program to run?
Let’s compare this code to the first example shown above:
for i in range(5):
for j in range(i + 1):
print("* ", end="")
print("")
Notice how the code structure is very similar? The outermost loop runs a given number of times, and then the inner loop’s iterations are determined by the value of the outer loop’s iterator variable. Since we know that the second example produces a triangle, we can guess that the program above runs in a similar way. So, the first iteration of the outer loop will run the inner loop once, then twice on the second iteration, and so on, all the way up to $ 10 $ iterations. If we sum up all the numbers from $ 1 $ to $ 10 $, we get $ 55 $.
Now let’s run that code and see if we are correct:
As this example shows, we can use our basic understanding of various looping structures to help us understand more complex programs, and even predict their output without running or tracing the program. This is a very useful skill to learn when working with nested loops.
Testing nested loops can also be very tricky, especially because they can make our program’s control flow even more complex. So, let’s briefly go through an example to see how to determine what inputs we can use to test nested loops.
Consider the following example program:
x = int(input("Enter a positive integer: "))
while x <= 0:
print("Invalid input!")
x = int(input("Enter a positive integer: "))
y = int(input("Enter a positive integer: "))
while y <= 0:
print("Invalid input!")
y = int(input("Enter a positive integer: "))
while y <= x:
for i in range(x - y):
print("*", end="")
y = y + 2
print("")
print("Complete!")
Let’s work through the process of generating some test cases for this program to see if it runs without any errors. As always, our biggest concern is to make sure that we don’t reach a situation where the program enters an infinite loop, and that we also try to provide inputs that will enter each loop at least once, and also bypass each loop if possible.
First, we see that the code uses a loop twice to make sure the user inputs only positive values. So, some of our first test cases could involve trying values such as $ -1 $, $ 0 $ and $ 1 $ to check the edge cases of that while loop’s Boolean expression. When we run the program and provide those inputs, we should see it enter the loop in that function at least once.
We can also bypass that loop by simply making sure we enter a positive integer each time. So, we know we have a few test cases available that will achieve branch coverage for that loop.
Next, let’s consider the outermost while loop after the input loops. That loop uses the Boolean expression y <= x
to determine if the loop should be entered or not. So, we want to come up with a few tests that check the edge cases of that Boolean expression. We can start by choosing a value to use for x
, such as
$ 5 $. Then, the edge cases of that Boolean expression would be when y
is either
$ 4 $,
$ 5 $, or
$ 6 $.
If y
is
$ 6 $, the Boolean expression would be False
and it should bypass the loop. We can easily test this to make sure that is the case:
Likewise, if y
is
$ 4 $, we know that it should enter the outermost loop. Inside, we see a for loop that will iterate based on the expression x - y
. Effectively, it will compute the difference between x
and y
and then iterate that many times. So, if x
is
$ 5 $ and y
is
$ 4 $, the difference between those values will be
$ 1 $. So, we’ll enter the innermost for loop at least once. When we run the program with these inputs, we’ll see the following output:
What if we set both x
and y
to be the same value? We know that the inner for loop will run x - y
times, so if both x
and y
are the same value, this would be a way to bypass that loop, while still entering the outermost while loop since y <= x
will be True
. However, when we try to run the program with these inputs, we’ll see something interesting happen:
Our program will quickly start printing blank lines of output to the terminal. So quickly, in fact, that it is hard to even see what happens. As it turns out, we accidentally caused our program to enter an infinite loop! When this happens, the only way to stop the program is to close the terminal window it is running in, or use the CTRL+C keyboard command to interrupt it. So, let’s see why this infinite loop is occurring, and figure out how we can fix it.
Did you spot the infinite loop when you first read the program’s code? It can be really tricky to find, which is why we have to be very good about choosing test cases that will explore many different ways to run the program.
In this case, the infinite loop is caused by the interaction between the two loops. In the outermost while loop, we have the Boolean expression y <= x
to enter the loop. However, inside that loop, the for loop will only execute x - y
times, which also happens to be the loop variant for the outer while loop. The key to the infinite loop lies in the fact that the only line inside of the outer while loop that will change the value of either x
or y
is also inside of the inner for loop. So, if we don’t execute the for loop’s code at all, then the value of x
and y
won’t change either, and we’ll continually repeat the steps of the outermost while loop.
There are many ways to fix this problem, but the simplest would be to change the Boolean expression of the outermost while loop to be y < x
. That will ensure that there is at least one iteration of the innermost for loop, and the infinite loop condition will be avoided.
So, as this example shows, testing nested loops is just like testing regular loops - we want to try and find inputs that will enter the loop at least once, as well as inputs that will bypass the loops if possible, just to make sure there aren’t any strange situations that may arise. While this won’t find all errors that are present in code containing nested loops, it is a great way to start testing your programs.
Now that we’ve explored how to create programs that contain nested loops, let’s work through a complete example problem to see how we can convert a problem statement into working code.
Consider the following problem statement:
Write a program to print the sum of the first
n
prime numbers, wheren
is provided as input from the user.
This is a very simple problem statement, but it can be very complex to build a program that satisfies it. So, let’s go through the steps and see if we can get it to work.
The first thing we’ll need to do is get input from the user, since we really can’t do anything else until we know what our goal is. While the problem statement doesn’t include any information about what inputs are acceptable, we can infer that only positive integers should be used as input. So, we can borrow the loop for input from earlier in this module to handle all of our input:
x = int(input("Enter a positive integer: "))
while x <= 0:
print("Invalid input!")
x = int(input("Enter a positive integer: "))
n = x
Reusing existing code, such as the loop for input we just included here, is a great way to build our programs. We can always think of previous code that we’ve already written and tested as possible building blocks for future programs, and reusing existing code is a great way to speed up our development process.
Next, we need some way to determine if a number is a prime number. Recall from mathematics that a prime number is a number that is only equally divisible by $ 1 $ and itself. For example, $ 7 $ is prime, but $ 8 $ is not since it can be evenly divided by $ 4 $.
Thankfully, we know that we can use the modulo operator %
to determine if a number if evenly divisible by another number. So, all we need to do is check if each number less than our chosen number can equally divide it. If none of them can, then our chosen number is prime.
So, let’s write code that performs this operation. We know that we need to check all of the numbers from
$ 2 $ up to but not including x
. We can do this using a for loop and the range()
function:
for i in range(2, x):
Inside of the for loop, we want to check and see if the iterator variable i
can evenly divide the chosen number n
using the modulo operator:
for i in range(2, x):
if x % i == 0:
# i equally divides x
Here’s where things get a bit tricky - if i
can equally divide x
, we know that x
is not prime. So, let’s add a Boolean value to keep track of this. We’ll set it initially to True
before the loop, and then we can set it to False
as soon as we know the number is not prime:
is_prime = True
for i in range(2, x):
if x % i == 0:
is_prime = False
# what if we get here?
We might realize that we don’t have to check any other values once we have determined that x
is not prime. So, in our code, we can just use the break
statement to end the loop early and go to the next part of the code. In effect, we can use this to shortcut the rest of the loop.
is_prime = True
for i in range(2, x):
if x % i == 0:
is_prime = False
break
# what if we get here?
We don’t introduce the break
and continue
statements in this class just to simplify things, but you are welcome to use them if you understand them.
However, what happens if we check all of the values from
$ 2 $ up to x
and don’t find a single one that will equally divide our chosen number x
? In that case, we’ll reach the end of our for loop, and is_prime
will still be True
. So, we’ve confirmed that x
is indeed a prime number!
is_prime = True
for i in range(2, x):
if x % i == 0:
is_prime = False
# is_prime stores whether nx is prime or not
There we go! That’s a quick and easy way to determine if a given number is prime. This is a great example of a pattern that we’ll see many times as we write complex programs whenever we must determine if a particular situation is true for a given list of numbers. Inside of the loop, we’ll check each case and set a value to False
if it isn’t true. If it is true, then we’ll complete the entire loop without setting that value to False
, so we’ll know that it is True
at the end of the code. We’ll see this pattern again in a later lab.
The code above is very simple, but not very efficient. With a bit of thought, it is easy to determine that we only have to actually check numbers up to x /2
, since it is impossible for any number larger than that to evenly divide x
. Likewise, we can do some quick math to eliminate numbers that end in an even digit or
$ 5 $, since those numbers will never be prime.
Finally, we could use a more advanced mathematical technique such as the Sieve of Eratosthenes to generate all prime numbers more quickly. However, for this example, we’ll stick to the simplest approach.
Finally, now that we have the code to determine if a number is prime, we can complete our program by simply iterating through all possible numbers until we’ve found n
prime numbers, and then print the sum:
x = int(input("Enter a positive integer: "))
while x <= 0:
print("Invalid input!")
x = int(input("Enter a positive integer: "))
n = x
count = 0
x = 2
total = 0
while count < n:
is_prime = True
for i in range(2, x):
if x % i == 0:
is_prime = False
if is_prime:
total = total + x
count = count + 1
x = x + 1
print(f"The sum of the first {n} prime numbers is {total}")
This program requires three additional variables. The variable count
is used to keep track of the number of prime numbers found, so we’ll increment it inside of the if
statement each time we find a prime number. Likewise, the total
variable keeps track of the sum of the prime numbers, which we’ll print at the end. Finally, we use x
as our iterator variable, so we must make sure that we increment x
each time the while loop iterates, outside of the if statement. We’ll start x
at
$ 2 $, since
$ 1 $ is not a prime number mathematically. A very common programming mistake is to forget to increment x
outside the if statement, resulting in an infinite loop. Also, we chose to use a while loop instead of a for loop since our program’s goal is to sum up the first n
prime numbers, which is better expressed as a while loop instead of a for loop.
Feel free to run this program in Python or using Python Tutor to confirm that it works as expected before continuing.
Testing this program is a bit complex, since we can only provide a single input. We can easily provide a negative value or $ 0 $ to confirm that the loop handling input is working correctly, but beyond that we have little control over the rest of the program just by changing inputs.
Instead, we have to rely on our ability to read and analyze the code to determine if it is working properly. For example, we can look at the second loop in the main part of the code. It is a while loop, which relies on the count
variable increasing until it is greater than or equal to n
before it will terminate. We can see that count
will be incremented each time the value of x
is prime, so as long as we are able to find enough prime numbers, and assuming our code to check prime numbers works correctly, this loop should eventually terminate.
In the code to check if a number is prime, we have a simple for loop, which will always terminate eventually. There is no way to bypass it, so we don’t really have to worry about that code not eventually terminating.
However, proving that this program creates the correct answer is a bit trickier. One of the best ways to do this is to simply check a few answers manually. For example, with a bit of searching online, we can find a list of prime numbers. According to Wikipedia, the first 9 prime numbers are:
2
3
5
7
11
13
17
19
23
The sum of those numbers is easy to calculate using a calculator or other device, and eventually we can be fairly certain that the correct answer is $ 100 $. When we run our program, we should hopefully get the same result:
We can test a few other numbers until we are sure that our program is working correctly.
Hopefully this example is a good look at how to build a program using loops that meets a given problem description. We were able to put together various types of loops and conditional statements in our program, and then test it to be sure it works. As you continue to work on projects in this class, feel free to refer back to these examples for ideas and blocks of code that you may want to use in your own programs. Good luck!
Let’s try some simple practice problems. These problems are not graded - they are just for you to practice before doing the real exercises in the lab itself. You can find the answers below each question by clicking the button below each question.
Consider the following Python program:
x = int(input("Enter a positive integer: "))
while x <= 0:
print("Invalid input!")
x = int(input("Enter a positive integer: "))
n = x
for i in range(n - 1):
for j in range(n - i - 1):
print(" ", end="")
for j in range(i + 1):
print("* ", end="")
print("")
for i in range(n):
for j in range(n):
print("* ", end="")
print("")
Describe the shape that will be printed when this program is executed. Try to do so without running the code directly, but feel free to check your answer after guessing.
Consider the following Python program:
x = 3
y = 5
while x => 0:
for i in range(y):
print("* ")
print("")
x = x + 1
This code is supposed to print a rectangle of asterisks that is x
rows tall and y
columns wide. However, it contains multiple syntax and logic errors preventing it from working correctly. Describe how to fix the errors in the given code to produce the desired output.
Write a complete Python program in that file that meets the specification below.
Write a program that will print a Parallelogram of asterisks that is m
rows tall and n
columns wide, where the topmost row is furthest toward the left. The values m
and n
should be provided by the user as input. If the user inputs a value that is
$ 0 $ or negative, the program should print an error and prompt for input again.
For example, if the user inputs $ 3 $ and $ 5 $, the program should provide the following output:
* * * * *
* * * * *
* * * * *
Notice that each asterisk is separated by a space, and each successive row of the parallelogram begins one space to the right of the previous row.
In this lab, we introduced several major important topics in Python. Let’s quickly review them.
While loops in Python will execute while a Boolean expression evaluates to true
.
while <boolean expression>:
<block of statements>
The range()
function in Python is used to generate a list of numbers. It can be used in three ways:
range(stop)
- numbers from $0$ up to (but not including) stop
range(start, stop)
- numbers from start
up to (but not including) stop
range(start, stop, step)
- numbers from start
up to (but not including) stop
, with step
between each number.For loops in Python will execute a set number of times.
for <iterator variable> in <list>:
<block of statements>
Loops can be used to request new input from the user if invalid input is received.
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
while(x < 0 or x > 1):
print("Invalid Input!")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
Loops can be tested for both branch and path coverage. In general, achieving path coverage involves writing code that will enter the loop, and also code that will bypass the loop entirely.
Loops should also be tested for termination and situations that may result in infinite loops. Using a loop variant and showing that it is monotonically decreasing is a helpful technique.
Loops in Python can be nested, just like any other statement. While loops and for loops can be nested in any combination.
When testing nested loops, it is important to consider situations where a loop will be executed and where it will be bypassed. It is also important to consider the loop variants for while loops to ensure that there aren’t situations where a loop will accidentally run infinitely.
Nested loops can result in programs that execute many individual steps, even using just a few lines of code. It is always helpful to think about the number of times a loop will execute and make sure that it isn’t executing more times than necessary.
As our Python programs continue to get larger and more complex, we may notice that we are reusing certain pieces of code over and over again. A great example is the loop structure to repeatedly get input from the user until a valid input is received.
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
while(x < 0 or x > 1):
print("Invalid Input!")
x = float(input("Enter a percentage as a decimal number from 0 to 1: "))
This is a very useful piece of code, and we can easily copy and paste it wherever we need it in our programs. Unfortunately, this means that anytime we want to update or change that code, we have to manually change every location where it is used in our programs. In a large program, this can be very tricky to do correctly without causing other errors in the code.
Thankfully, Python allows us to create small, repeatable blocks of code called functions that we can write once, and then use anywhere we’d like within our programs. We’ve already used many different functions in our programs, from the print()
function used to display output to the user and the input()
function used to get input from the user, all the way to the random.randint()
function we’ve used to generate random numbers in our code.
Functions also allow us to break our larger programs up into smaller, easier to understand steps. This makes our code much easier to read, test, and debug. Learning how to build programs in terms of individual functions instead of just simple steps is a big part of becoming a more experienced programmer. So, in this lab, we’re going to learn all about how to build, use, and test our own functions in Python.
A simple function in Python uses the following structure:
def function_name():
<block of statements>
Let’s break this structure down to see how it works:
def
, which is short for define. In effect, we are stating that we are defining a new function using the special def
keyword._
. Function names beginning with an underscore _
have special meaning, so we won’t use those right now._
.()
. This is where we can add the parameters for the function. We’ll see how to add function parameters later in this lab.:
. This tells us that the block of statements below the function definition is what should be executed when we run the function. We’ve already seen colons in both conditional statements and loops, and here it is used in a similar way.}
or any other marker to indicate the end of the function - only the indentation tells us where the function’s code ends. Again, this is very similar to how conditional statements and loops are structured in Python.Let’s look at an actual function in Python. In this case, we’ll create a simple hello_world
function in Python:
def hello_world():
print("Hello World")
Really, this structure is exactly like we’d expect it to be, based on the structures we’ve learned so far in Python. We start with the def
keyword, then the name of the function. After that, we have parentheses ()
where the parameters would go, and then a colon :
to show the start of the block of statements. Inside of the function, we indent the block of statements that make up the code to be run when this function is run - in this case we just print the string "Hello World"
to the terminal. That’s really all there is to it!
Likewise, calling a function in Python is exactly the same as what we’ve seen with the print()
function - we simply state the name of the function, followed by a set of parentheses where we would place the arguments if the function required any parameters:
hello_world()
So, a complete Python program that includes the hello_world
function and calls that function would look like this:
def hello_world():
print("Hello World")
hello_world()
Try placing this code in a Python file and see if you can run it. It should work!
Notice that the function call for hello_world
is not indented, and we included a couple of blank lines after the end of the definition of that function before the next piece of code. We do this for two reasons:
hello_world
should not be part of the definition for the function, we don’t want to indent it.Finally, let’s briefly go through an example of tracing a program that includes a couple of functions and function calls. Here’s a quick example we can use:
def foo():
print("eye")
def bar():
print("to")
foo()
bar()
foo()
To trace this example, copy-paste the code into Python Tutor, or click this Python Tutor link.
When the program first loads, we should see this setup:
Again, this is very familiar to us based on what we’ve seen so far. So, let’s click the Next > button to step through the first part of the code:
As we can see, the first thing that Python sees is the definition for the foo
function. Instead of keeping a separate list of functions, Python Tutor simply adds that function to the global frame just like any variable. The function itself is added to the Objects list, and then there is an arrow, connecting the variable in the global frame to the function in the objects list. In fact, this is exactly how Python handles functions - they are just treated like variables that are pointers to the function objects themselves! So, when we click Next > again, we’ll see it do the same to the bar
function as well:
At this point, we are ready to execute the first function call, which will run the foo
function. So, when we click the Next > button, we’ll see the arrow jump to the start of that function:
Here, we see Python tutor do something a bit different than in our own code traces - instead of keeping all of the variables in a single box, it creates a new frame to keep track of the variables that are created in the foo
function. When we look an example of a function with parameters, we’ll see where this becomes very useful. For now, we can just ignore it and move on by clicking Next > once again:
At this point, the function is ready to print the string value "eye"
to the terminal. So, if we click Next > once again, we should see that reflected in the output of Python Tutor:
At this point, we’ve reached the end of the foo
function. So, Python Tutor will remove the foo
frame from the list of frames and it will jump back down to where the function was called from when we click Next >:
Here, we are back to the main piece of our code, and we’re ready to call the bar
function. So, we can click Next > once again:
This will jump up to the start of the bar
function and create a new frame for storing any variables created by bar
. We can click Next > again to enter the function:
And then click Next > again to print the string value "to"
to the output:
Once again, we are at the end of a function, so when we click Next > once more:
We’ll move back to the main piece of the code, removing the frame for bar
. On this line, we see yet another function call, this time to the foo
function. Hopefully by this point we can predict what is going to happen, so we can click Next > a few more times to jump to the foo
function and execute it. When it is done, we’ll see this output:
There we go! We’ve successfully traced the execution of a Python program that contains multiple function calls. The full process is shown in this animation:
Using tools like Python Tutor to trace through code is a great way to make sure we understand exactly what our computer is doing when it is running our code.
Let’s review a couple other concepts related to functions in Python.
We can also create a main
function in Python. The use of a main
function in Python is not required at all - Python is designed as a scripting language, meaning we can write code directly in a Python file without using any functions at all. However, it is generally considered good practice to make sure all code is part of a function, and then we can include a main
function as the starting point for any program.
So, we can update the example we saw previously to include a main
function by simply placing the three function calls in a new function called main
, and then including a call to the main
function at the bottom of the program:
def foo():
print("eye")
def bar():
print("to")
def main():
foo()
bar()
foo()
main()
From here on out in this course, we’ll follow this convention in our complete Python programs. Specifically:
main
as the starting point of the program.main
function at the bottom of the program.This will make our Python programs easy to follow, and it will help us later on down the road if we choose to learn another language such as Java or C# that requires a main function.
Functions in Python can also require parameters. To include a parameter in a function, we simply have to include the name of the parameter in the parentheses ()
at the end of the function definition. Multiple parameters should be separated by commas ,
. For example, we can update our hello_world
function to include a couple of parameters:
def hello_world(first_name, last_name):
print("Hello ", end="")
print(first_name, end=" ")
print(last_name)
Here, we are defining two parameters, named first_name
and last_name
, that are used as part of the function. Those parameters can be treated just like any other variable within the function.
Once we have a function that requires parameters, we can call it by including arguments in the parentheses ()
that are part of the function call. For example, we can call the hello_world
function above and provide two arguments like this:
hello_world("Willie", "Wildcat")
When we run this code, it will place the string value "Willie"
in the parameter variable first_name
, and the string value "Wildcat"
will be placed in the parameter value last_name
within the frame for the hello_world
function. When the code in hello_world
is executed, we should see the following output:
Hello Willie Wildcat
To see how this works, let’s work through a full example. Here’s a more complex Python program that includes parameters, arguments, and some other variable expressions:
def flip(first, last):
temp = first
first = last
last = temp
print(first)
print(last)
def main():
first = "Willie"
last = "Wildcat"
flip(first, last)
print(first)
print(last)
main()
Once again, before reading the full analysis below, take a minute to read through this code and see if you can guess what it does. It can be a bit tricky if you don’t read it carefully and think about what we’ve learned so far.
To trace this example, copy this code to Python Tutor, or click this Python Tutor link.
When we begin, our code trace will look like this example:
As we expect, the first thing that Python will do is scan through the code and record any functions it finds in the global frame. So, after pressing the Next > button a couple of times, we should reach this point:
Now we are at a function call for the main
function. So, when we click the Next > button:
Python tutor will jump to that function’s code, and it will also create a new frame for variables that are created in the main
function. The next two lines deal with creating a couple of variables, so we can click the Next > button a couple of times to execute those lines and stop when we reach the next function call:
Now we are ready to call the flip
function. This function requires two parameters, named first
and last
. Notice that those parameter names are the same names as the variables that we created in the main
function? This is a common practice in programming - sometimes it is simplest to use the same variable names in multiple functions, especially if they are storing the same data. However, it is important to understand that those variables are not related in any way except for the name, as we’ll see in this example. When we click the Next > button to jump to the start of the flip
function, we should see the following in our Python tutor trace:
It has now created a frame for the flip
function, and copied the values of the two arguments into the appropriate parameter variables. Since we listed the arguments first
and last
in that order in the function call to flip
, a copy of the values from those two variables in main
will be stored in the same two variable names in flip
. It’s an easy way to make sure the arguments are in the correct order!
The first three lines in the flip
function will swap the values in the first
and last
parameter variables. So, after we’ve executed those three lines, we should now see this setup in our trace:
Notice that the values in first
and last
inside of the flip
frame have changed, but the values in the same variables in the main
frame have not changed! This is why it is very useful to keep track of variables in frames within a code trace - we can easily tell which variables go with which function, even if they have the same names. So, the next two lines of code in the flip
function will simply print out the contents of the first
and last
parameter variables:
At this point, we’ve reached the end of the flip
function, so when we click the Next > button again, we’ll jump back down to where we left off in the main
function. At the same time, we’ll remove the flip
frame from the list of frames, completely removing the first
and last
parameter variables used in that function:
Now that we are back in the main
function, we can see that the values stored in the first
and last
variable are unchanged, just like we’d expect. This is important to understand - just because one function uses the same variable names as another function, or that a function’s parameter names match the variable names provided as arguments, they are not related and any changes in the function won’t impact the values outside of the function. So, we can finish the main
function by running the last two lines, which will print the current values of first
and last
to the screen:
Finally, once the program reaches the end of the main
function, it will jump back to the main
function call at the bottom of the program. This will remove the main
frame from the list of frames. Since there is nothing more to do, the program will end at this point:
The whole process can be seen in this animation:
There we go! We’ve explored how functions, parameters, and arguments all work in Python. Understanding this process now will make it much easier to write complex programs later on.
Python functions are also capable of returning a value, In Python, we use the return
keyword.
Let’s look at a quick example of a full Python program that includes a function that returns a value:
def square_sum(one, two):
one = one * one
two = two * two
total = one + two
return total
def main():
text_one = input("Enter the first number: ")
one = int(text_one)
text_two = input("Enter the second number: ")
two = int(text_two)
total = square_sum(one, two)
print(f"The sum of squares of {one} and {two} is {total}")
main()
To truly understand how this program works, let’s use Python Tutor to explore it step by step. Like before, copy the code into Python Tutor, or click this Python Tutor link.
At the start, our Python Tutor trace will look like this:
The first few steps are pretty straightforward, since Python will simply move through the code and record all of the functions it finds. Once we reach the call to the main()
function at the bottom, we’ll be at this state:
So, we’ll enter the main()
function and start executing the code it contains. The first line is an input()
expression, so Python will prompt the user for input. In Python Tutor, this will open a box at the bottom where we can enter the input, as shown below:
Let’s assume that the user would enter 2
for this input. So, we can type that into the box and press ENTER or click submit. Python tutor will refresh the page to accept the input, and then we’ll be at this setup:
Next, we’ll convert the string value "2"
that is currently stored in the text_one
variable to an integer using the int()
function and store it in the variable one
. On the next line, it will ask for input once again:
In this case, we’ll assume the user is inputting 3
, and then we’ll store it and convert it to an integer stored in the variable two
:
At this point, we’re ready to call the square_sum()
function. So, Python Tutor will find the arguments for the function call and prepare to store them in the parameter variables of the function. When we click next, we’ll see this setup in our window:
Recall that Python Tutor will create a new frame for the square_sum()
function in the frames area and store the variables from that function there. This will become important later when we reach the end of the function. Inside of the square_sum()
function, we just perform a few mathematical operations, culminating with the total
variable storing the sum of the squares of the two arguments, as shown here:
At this point, we’ve reached the return
statement at the end of the square_sum()
function. When we click next on Python Tutor, we’ll see something new appear:
When Python reaches the end of a function, it will record the return value in the function’s frame before leaving the function. This value is what is given back to the main()
function. So, when we click next again, we’ll see this state:
We’re back in the main()
function, and now we can see that the new total
variable stores the value $13$, which was the returned value from the square_sum()
function. So, on this last line, we’ll see that the program will create an output statement using a template string and the string format()
method, and it will display output to the user:
What’s also interesting to note here is the return value of the main()
function. Since we didn’t include a return
statement at the end of the function, the return value is set to a special value called None
in Python. We’ll learn about what that value actually represents in a future course.
The entire process is shown in the animation below:
This review of how a function returns data should be very helpful as we continue to build more complex programs. Remember - we can always paste any Python code into a tool like Python Tutor to run through it step-by-step and see what it really does.
Now that we’ve explored how to create functions, let’s work through a one of our previous worked examples to see how we could approach building that same program, but this time using some functions to simplify our code a bit.
Consider the following problem statement:
Write a program to print the sum of the first
n
prime numbers, wheren
is provided as input from the user.
This is a very simple problem statement, but it can be very complex to build a program that satisfies it. So, let’s go through the steps and see if we can get it to work.
Now that we’ve learned about functions and the main()
function, we can start with that structure. It is always good practice to include a main()
function and a call to the main()
function to our code, as shown here:
def main():
main()
Inside of the main()
function, the first thing we’ll need to do is get input from the user, since we really can’t do anything else until we know what our goal is. While the problem statement doesn’t include any information about what inputs are acceptable, we can infer that only positive integers should be used as input. So, we can borrow the loop for reading a positive input from earlier to handle all of our input. This time, we’ll put it in a function called positive_input()
, and this function will return the value input by the user once it is accepted:
def positive_input():
x = int(input("Enter a positive integer: "))
while x <= 0:
print("Invalid input!")
x = int(input("Enter a positive integer: "))
return x
def main():
n = positive_input()
main()
We’ll also add a call to that function in our main()
function, and store the input provided by the user in the variable n
inside of our main()
function. Notice that the variable x
in the positive_input()
function is the value that is returned, but we are storing it in a different variable inside of the main()
function. We’ll have to keep track of the location of that value as we think about our program. Thankfully, Python Tutor can help us learn how to do this, so feel free to follow along and build this program in Python Tutor and run it there to see how it works.
Building reusable functions, such as the positive_input()
function we just created here, is a great way to build our programs. We can always think of previous code that we’ve already written and tested as possible building blocks for future programs, and reusing existing code is a great way to speed up our development process.
Next, we need some way to determine if a number is a prime number. Recall from mathematics that a prime number is a number that is only equally divisible by $ 1 $ and itself. For example, $ 7 $ is prime, but $ 8 $ is not since it can be evenly divided by $ 4 $.
Thankfully, we know that we can use the modulo operator %
to determine if a number if evenly divisible by another number. So, all we need to do is check if each number less than our chosen number can equally divide it. If none of them can, then our chosen number is prime.
So, let’s write another function that performs this operation. We’ll start by creating a function that accepts our chosen number as a parameter:
def is_prime(n):
Next, we know that we need to check all of the numbers from
$ 2 $ up to but not including n
. We can do this using a for loop and the range()
function:
def is_prime(n):
for i in range(2, n):
Inside of the for loop, we want to check and see if the iterator variable i
can evenly divide the chosen number n
using the modulo operator:
def is_prime(n):
for i in range(2, n):
if n % i == 0:
# i equally divides n
Here’s where things get a bit tricky - if i
can equally divide n
, we know that n
is not prime and we don’t have to check any other values. So, in our function, we can just use the return False
statement to return the value False
from this function. As soon as the function reaches a return
statement, even if it is inside of a loop, it will immediately stop the function and go back to where it was called from. In effect, we can use this to shortcut the rest of the loop.
def is_prime(n):
for i in range(2, n):
if n % i == 0:
return False
# what if we don't return false?
However, what happens if we check all of the values from
$ 2 $ up to n
and don’t find a single one that will equally divide our chosen number n
? In that case, we’ll reach the end of our for loop, but our function hasn’t returned anything. So, we’ll need to add one extra line to the end of the function to return True
if we get to that point.
def is_prime(n):
for i in range(2, n):
if n % i == 0:
return False
return True
There we go! That’s a quick and easy function to determine if a given number is prime. This function is a great example of a pattern that we’ll see many times as we write complex programs whenever we must determine if a particular situation is true for a given list of numbers. Inside of the loop, we’ll check each case and return False
if it isn’t true. If it is true, then we’ll complete the entire loop without returning False
, so we’ll need to return True
at the end of the function. We’ll see this pattern again in a later lab.
Finally, now that we have the is_prime()
function, we can complete our main()
function by simply iterating through all possible numbers until we’ve found n
prime numbers, and then print the sum:
def main():
n = positive_input()
count = 0
i = 2
sum = 0
while count < n:
if is_prime(i):
sum = sum + i
count = count + 1
i = i + 1
print(f"The sum of the first {n} prime numbers is {sum}")
This program requires three additional variables. The variable count
is used to keep track of the number of prime numbers found, so we’ll increment it inside of the if
statement each time we find a prime number. Likewise, the sum
variable keeps track of the sum of the prime numbers, which we’ll print at the end. Finally, we use i
as our iterator variable, so we must make sure that we increment i
each time the while loop iterates, outside of the if statement. We’ll start i
at
$ 2 $, since
$ 1 $ is not a prime number mathematically. A very common programming mistake is to forget to increment i
outside the if statement, resulting in an infinite loop. Also, we chose to use a while loop instead of a for loop since our program’s goal is to sum up the first n
prime numbers, which is better expressed as a while loop instead of a for loop.
The full program is shown below:
def positive_input():
x = int(input("Enter a positive integer: "))
while x <= 0:
print("Invalid input!")
x = int(input("Enter a positive integer: "))
return x
def is_prime(n):
for i in range(2, n):
if n % i == 0:
return False
return True
def main():
n = positive_input()
count = 0
i = 2
sum = 0
while count < n:
if is_prime(i):
sum = sum + i
count = count + 1
i = i + 1
print(f"The sum of the first {n} prime numbers is {sum}")
main()
Finally, notice that this program doesn’t contain a loop nested within another loop in the same function, but because we are calling the is_prime()
function, which contains a loop, from within a loop inside of the main()
function, it actually has a nested loop structure inside of it! Feel free to run this program in Python or using Python Tutor to confirm that it works as expected.
Let’s try some simple practice problems. These problems are not graded - they are just for you to practice before doing the real exercises in the lab itself. You can find the answers below each question by clicking the button below each question.
Write the output that is displayed to the user after running the following Python program:
def foo(word):
print(word, end=" ")
def bar(word1, word2, word3):
foo(word1)
foo(word2)
print("don't make a", end=" ")
print(word3)
def main():
bar("two", "wrongs", "right")
main()
Construct a function (not an entire program) that meets the following specification. When the function is called using this line of code:
fun("s", "e", "l" "r")
it should produce the following output:
seller
When the same function is called using this line of code:
fun("p", "i", "p", "n")
it should produce the following output:
pippin
Write a complete program in Python that meets the following specification:
display
that requires a single parameter. When run, that function should print the value of the parameter to the terminal, but without a newline character at the end.square
that will call the display
function multiple times to create an ASCII art square as shown in the output example below. The square
procedure may not use the print(expression)
statement directly - it must call the display
function to produce output.main
function that only calls the square
function.main
function at the end of the code to start the program.When the program is run, it should produce the following output:
* * * *
* * * *
* * * *
* * * *
In this lab, we introduced several major important topics in Python. Let’s quickly review them.
A function in Python is a piece of code that is given a name, which can then be run by calling it elsewhere in the program.
To create a function, we use the following structure:
def function_name(parameter1, parameter2):
<block of statements>
To call a function, we use the following structure:
function_name(argument1, argument2)
()
.return expression
will return the value of expression
to where the function was called from.So far, we’ve worked with many different programming structures, including functions, if statements, loops, and more. However, so far we’ve been a bit limited in how much data we can easily store. This is because a traditional variable can only store one value at a time!
If we want to store lots of data, it would be much easier if we had a way to store it in a structured way so that we can access it using a single variable name and possibly some other identifier.
Thankfully, Python includes a great way to do this using various collection data types. A collection is a data structure that allows us to store multiple data items in a single variable, and each item is identified in some way using either an index or a key.
In this lab, we’ll explore one collection type in Python, which is the list. Let’s get started!
The first collection we’ll review in Python is the list. A list in Python allows us to store many individual values, or elements, in a single variable. To keep track of the elements, each one is assigned an index, which is an integer that uniquely identifies the element’s position in the list. In Python, just like in many other programming languages, the indexes begin at $ 0 $ and count up from there.
There are many ways to create a list in Python. The simplest is to simply use an empty set of square brackets []
to represent an empty list, and then store that in a variable using an assignment statement:
list_1 = []
If we know what values we want to store in the list, we can include them inside of the square brackets. For multiple items, we can separate them by commas:
list_2 = [1, 2, 3]
We can store any value in a list, including strings, numbers, Boolean values, and even other lists. We won’t cover lists inside of lists in this course, but it is important to know that it can be done.
Once we’ve created a list, there are two ways to add items to a list. First, if we want to add a new item to the list and expand it’s size by one, we can use the append()
method. For example, we can start with a list containing three items and then add a fourth item:
list_2 = [1, 2, 3]
list_2.append(4)
To access existing items in a list, we can use the index of the item inside of square brackets after the name of the list. Consider this example:
list_2 = [1, 2, 3]
print(list_2[0]) # 1
print(list_2[1]) # 2
print(list_2[2]) # 3
The list stored in list_2
initially contains the items 1
, 2
, and 3
. To access the first item, we can use index
$ 0 $, as in list_2[0]
. We can similarly use index
$ 1 $ and
$ 2 $ to access the other items.
We can also use the index to update a value stored in a particular location of the list. In effect, each location in the list can be treated just like a variable in an assignment statement.
list_2 = [1, 2, 3]
list_2[1] = 5
print(list_2) # [1, 5, 3]
In this example, we are replacing the value 2
, at index
$ 1 $ in the list, with the new value 5
. As we can also see in that example, we can even print an entire list at once in a print statement!
Thankfully, tools like Python Tutor make it very easy to work with lists in Python and understand what is happening in memory on the computer itself. Let’s walk through a brief example program that includes lists in Python Tutor:
def main():
sums = []
total = 0
for i in range(1, 5):
total = total + i
sums.append(total)
print(sums)
main()
As always, we can copy this code to Python Tutor, or click this Python Tutor link to open it in a web browser. We can skip ahead a few steps to the point where the execution pointer enters the main()
function and we see this state:
The very first line of code in the main()
function will create a new list and store it in the sums
variable. So, when we execute that line of code, we’ll see some new information appear in the frames and objects area in Python Tutor:
As we can see, in Python lists are treated like objects, so the sums
variable in the main()
function’s frame points to an empty list object in the objects list. As we add elements to the list, they’ll show up in the object itself. We’ll come back to this concept later in this lab to show why it is important to know that Python treats lists like objects instead of other variables.
The next line creates the total
variable, setting it equal to
$ 0 $, and then we’ll reach the for loop:
This for loop iterates four times, from
$ 1 $ up to but not including
$ 5 $. So, we’ll enter the loop with i
storing the value
$ 1 $.
Inside of the loop, we’ll add the value of i
to the total
:
Then, we’ll append that new value in total
to the end of the sums
list. Since that list is empty, it will become the first item in the list, as we can see here:
Notice that the list in the object area now includes a single element. On that element, we can see a small
$ 0 $ at the top of the box, which is the index of that element. Then, at the bottom and in a larger font, we see the value stored in that element,
$ 1 $. Just like with other variables, even though the assignment statement references the total
variable, we are actually storing the value in the list, not a reference to the variable.
At this point, we’ve looped back to the top of the for loop, so we’ll increment i
by one and enter the loop again:
Inside of the loop, we’ll add the new value of i
to total
, and then append that value to the sums
list. After both of those steps, we should see the following state in Python Tutor:
Notice that we now have two elements in the list. The first item, at index
$ 0 $, is still
$ 1 $, but now we’ve appended a second element at index
$ 1 $ that stores the value
$ 3 $. We’ve reached the top of the loop, so we’ll increment i
and repeat those steps again. After the next loop iteration, we’ll see this state:
The process repeats one more time, leading to this state at the end of the for loop:
Notice that each time the loop iterates, we get a new value added to the sums
list. Finally, we’re out of items to iterate over in the range, so we’ll jump to the bottom of the for loop and continue executing code from there:
This last line of code will print the current contents of the sums
list to the terminal. Python does a great job of formatting lists on the terminal so they are easy to read and understand. So, when we execute this line, we should see the following state, with output added in the print output section at the upper-right of the screen:
As we can see, adding elements to a list using a for loop works very easily, and Python Tutor does a great job showing us how Python will store that data in memory. The image below shows a full animation of this entire program.
Later in this lab, we’ll see why this particular structure is used and why it is important.
One great way to work with lists in Python is using a loop. Recall from an earlier lab that a for loop actually iterates over a list itself, and that the range()
function is simply used to generate a list that the for loop can use. Likewise, while loops can also be used in a similar way.
Let’s look at a quick example of how we can iterate through the items in a list using a loop in Python.
Consider the following example program in Python:
def main():
nums = [3, 1, 4, 1, 5, 9, 2]
print("The first seven digits of pi are...")
for i in nums:
print(i)
main()
In this example, the for loop will iterate through the values in the nums
list instead of a given range. So, during the first iteration, i
will store the value
$ 3 $, then
$ 1 $, then
$ 4 $, and so on until it reaches the end of the list. When the program is executed, we should receive this output:
Using a list in a for loop is an excellent way to go through each element quickly, and it allows us to build for loops that don’t require the use of the range()
function to generate a list that is in some sort of sequential order. We’ll see this pattern used very often in our programs using lists.
Finally, it is not recommended to edit the contents of the list while inside of the for loop using this method. Since we are only getting the individual values from each list element, we cannot easily make changes to the list itself without causing issues in the loop itself. Instead, if we want to make changes to the list while we are iterating through it, it is highly recommended to use a while loop structure as shown below.
It is also possible to iterate through a list in Python using a while loop. However, instead of iterating through the items themselves, we can use an iterator variable that references the indexes of the elements in the list. Consider this example program:
def main():
nums = [3, 1, 4, 1, 5, 9, 2]
print("The first seven digits of pi are...")
i = 0
while i < len(nums):
print(nums[i])
i = i + 1
main()
This program is effectively the same as the one above, except that it uses a while loop to iterate through the items in the nums
list. We start by setting the iterator variable i
to be
$ 0 $, the first index in the list. Then, in the while loop, we use the special len()
function, which is used to find the size of the list. Since the list contains seven items, the length of the list is
$ 7 $. Another way to think about the length of the list is that it is always one more than the highest index - if the last item in the list is at index
$ 6 $, then the length of the list overall must be
$ 7 $.
Then, inside of the loop, we’ll use the iterator variable i
inside of square brackets to access each individual element in the nums
list and print it to the terminal. We’ll also need to remember to increment i
by one each time.
This method is very useful if we want to do more with the list inside of our loop, such as edit individual elements or make changes to the overall structure of the list. This works because we can directly manipulate the value in the iterator variable i
, and then use it to access individual elements in the list and update them. Also, if the size of the list changes, it will be checked using the len()
function after each iteration of the while loop.
So, as a general rule, we should use a for loop when we just want to iterate through the list and not make any changes to the list while inside of the loop. If we do need to make changes, it is better to use a while loop and an iterator variable to access the list elements directly.
Earlier in this lab, we saw Python Tutor create a list variable within the frame of a function, but that variable actually pointed to a list object that was stored in the global objects list. As it turns out, this seemingly minor distinction is a very important concept to understand how lists and functions interact with each other.
To demonstrate what is going on, let’s explore a program that contains multiple functions that operate on lists using Python Tutor once again. For this example, consider the following Python program:
def increment_list(nums):
i = 0
while i < len(nums):
nums[i] = nums[i] + 1
i = i + 1
def append_sum(nums):
sum = 0
for i in nums:
sum = sum + i
nums.append(sum)
def main():
nums = [1]
for i in range(4):
increment_list(nums)
append_sum(nums)
print(nums)
main()
Before we go through the full example, take a minute to read through the code and try to piece together how it works. For example, notice that the increment_list()
and append_sum()
functions do not return a value! Since they don’t return a value, will they really have any impact on the program itself?
To find out, let’s run through this program in Python Tutor. As always, you can copy the code to Python Tutor, or click this Python Tutor link to open it in a web browser.
Python Tutor begins by adding all of the functions to the global frame, and then entering the main()
function when it is called at the bottom of the code. Once the program enters the main()
function, we should see this state:
The main()
function begins by creating a single list in the nums
variable, which initially stores a single element, the number
$ 1 $. So, we’ll see the nums
variable added to the main()
function’s frame, with a pointer arrow to the list itself in the list of objects to the right:
Next, we’ll reach a for loop that will iterate
$ 4 $ times. So, we’ll store the first iterator value,
$ 0 $, in the iterator variable i
and enter the for loop:
Inside of the for loop, the first step is to call the increment_list()
function, which accepts a single parameter. So, we’ll provide the nums
list as an argument to that function call. Just like always, Python Tutor will create a new frame for the increment_list()
function when we call it, and it will populate all of the parameters for that function with the arguments that are provided, as shown here:
In this state, we see something interesting. In the increment_list()
frame, we see the parameter nums
is also a pointer to a list, but notice that it points to the exact same list object that the main()
frame points to. This is because we are only sending the pointer to the increment_list()
function so it knows where to find the list in the objects in memory, but it doesn’t actually duplicate the list.
In programming, we call this method of dealing with parameters call by reference, since we are simply passing references, or pointers, to the various objects but not the objects themselves. In technical terms, we’d say that the list is a mutable, or changeable, object, so we’ll use call by reference instead of the usual call by value process.
The next step will be to enter the increment_list()
function itself:
In this function, we are getting ready to use a while loop to iterate through the nums
list. Since we’re going to be editing the values stored in the list, we should use a while loop instead of a for loop. So, we’ll start by setting the initial value of our iterator variable i
to
$ 0 $, and then we’ll reach the while loop in code:
When we’ve reached the while loop, we’ll need to make sure that our iterator variable is less than the size of the list. Since i
is currently storing
$ 0 $, and our list contains at least one item, this Boolean expression is True
and we’ll enter the loop:
Inside of the loop, our goal is to increment each item in the list by
$ 1 $. So, we’ll use the iterator variable i
inside of square brackets to both access the item in the list currently on the right-hand side of the assignment statement, and then we can also use it as the location we want to store the computed value, which is on the left-hand side of the assignment statement.
Then, we’ll increment our iterator variable i
by
$ 1 $, and then jump back to the top of the loop:
At this point, we must check our Boolean expression again. This time, our iterator variable i
is storing
$ 1 $, but the length of the list is also
$ 1 $, so the Boolean expression is False
and we should skip past the loop. Since there is no additional code in the increment_list()
function after the loop, the function will simply return here.
Notice that the function itself is not returning a value. Since Python uses call by reference, we don’t have to return the nums
list back to the main()
function even though we updated it, because the main()
function still has a pointer to that exact same list object in memory. So, once the function returns, we’ll end up back in the main()
function as shown here:
Notice that the frame for the increment_list()
function is now gone, but the changes it made to the list are still shown in the list object to the right. This is why working with lists and functions can be a bit counter-intuitive, since it works completely differently than single variables such as numbers and Boolean values. A bit later in this lab, we’ll discuss how string values can be treated as lists, even though they are technically stored as single variable values.
The next step in this program is to call the append_sum()
method, using the same nums
list as before. So, we’ll create a new frame for that function and populate it once again with a pointer to the nums
list in memory - the very same nums
list that the main()
function is using:
In the append_sum()
function, our goal is to sum up all of the values currently in the list, and then append that value to the end of the list. So, we’ll need to create a sum
variable to keep track of the total, which will initially store
$ 0 $:
Thankfully, since we are not going to update the list from within our loop, we can use a simple for loop to iterate through the list. So, we’ll start by storing the first value in the list,
$ 2 $, into our iterator variable i
, and then we’ll enter the loop.
Inside of the loop, all we are doing is adding the value currently stored in i
to the sum
variable. So, we’ll store
$ 2 $ in sum
and jump back to the top of the loop.
At this point, we’ve used every element in the list in our loop, so the loop will terminate and we’ll jump down to the code directly below the loop as shown here:
Finally, the last line of code in this function will append the value of sum
to the end of the list.
At this point, we’ve modified the list to contain a new element. However, in Python Tutor we can clearly see that this item was just added to the existing list object in memory. So, when we get back to the main()
function, we’ll be able to access the existing list, which will now include this new element as well.
Back in the main()
function, the last step in the loop is to print the current contents of the list to the terminal. So, after we execute that line, we should see some output in our print output box at the top right of the screen:
There we go! That’s one whole iteration through the loop in the main()
function. This process will repeat
$ 3 $ more times. Each time, we’ll first increment the elements in the list using the increment_list()
function, as shown here:
Then, we’ll sum up the existing elements in the list and append that value to the end of the list, which will result in this state:
Finally, we’ll print the contents of the list at that point, and jump back to the top of the loop:
Each time, the list will be updated by the various functions that the main()
function calls. Thankfully, since Python uses call by reference, each function is able to update the same list object in memory, making it quick and easy to work with lists and functions in Python. At the end of the entire program, we’ll see this state:
A full animation of this program, with some steps omitted, is shown here:
Functions that use lists as parameters are a very common technique in programming, and it is very important to understand how they work. If we aren’t careful, it is very easy to make a change to a list inside of a function call, and then later assume that the original list is unchanged. Likewise, if we pass a list as an argument to a function that works differently than we think it should, it could end up changing our data without us even noticing it. Hopefully this example makes it very clear how Python functions handle lists as parameters.
One of the coolest features in Python is the ability to easily create slices of lists. A slice is simply a portion of a list that can be stored and used as a separate list, allowing us as programmers to quickly create and manipulate new lists based on existing lists.
There are two basic ways to create a list slice in Python.
nums[start:end]
- this will create a slice of the list stored in nums
that begins at the index start
and ends before the index end
. For example, if nums
is storing the list [2, 4, 6, 8, 10]
, then nums[1:3]
would create the list slice [4, 6]
.nums[start:end:step]
- this will create a slice of the list stored in nums
that begins as the index start
, moves step
indexes between each successive items, and ends before the index end
. For example, if nums
is storing the list [2, 4, 6, 8, 10]
, then nums[0:5:2]
would create the list slice [2, 6, 10]
.The method for creating list slices is very similar to how the range()
function is used in Python. In effect, if the same values are provided as arguments to the range()
function, then it will produce the list of indexes that will be used to generate the list slice.
Beyond the simple syntax, there are a few other rules to understand about list slices:
start
will be
$ 0 $ at the start of the list, end
will be the size of the list, and step
will be
$ 1 $. So, each of these are valid ways to slice a list, and there are many more possible combinations:nums[:]
- this will effectively copy the list and include every item in the slice.nums[start:]
- this will include all items in the list starting at start
.nums[:end]
- this will include all items in the list before end
.nums[::step]
- this will include all items in the list starting at index
$ 0 $ and moving forward step
indexes eachnums[-1]
- this will access the last item in the listnums[-3:]
- this will create a slice containing the last three items in the list.nums[:-2]
- this creates a slice of the entire list except for the last two items.For example, we can start with a simple list, and then try the various list slicing methods to see what elements would be included in the new list. Here’s an example program that shows some of the various ways to manipulate lists in Python:
def main():
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nums)
# simple slices
print(nums[3:7])
print(nums[5:])
print(nums[:5])
print(nums[::2])
print(nums[1::2])
# negative numbers
print(nums[-1])
print(nums[-7:-3])
print(nums[-3:])
print(nums[:-3])
print(nums[::-1])
main()
When we execute the program above, we should see the following output:
List slicing is a great way to use and manipulate lists in Python, but it takes a bit of practice to learn all the various ways that it can be used. Feel free to play around with all of the various examples above using some sample data to see how they work.
The range()
function in Python is used to generate a list, but unfortunately we can’t directly use that list in our code without a bit of manipulation. For example, we can try to store the result of the range()
function in a variable as shown here:
nums = range(10)
print(nums[3:7])
However, when we try to run that code, we’ll see this output:
range(3, 7)
This is because the range()
function doesn’t actually create a list - it is it’s own data structure type! Since a range can be constructed from just three numbers, it is much simpler to store just those numbers in memory instead of the entire list of numbers that it represents. When we create a slice of a range object, it just generates a new range instead of a list. So, to actually convert a range to a list, we must use the list()
function in Python:
nums = list(range(10))
print(nums[3:7])
This will produce the expected output:
[3, 4, 5, 6]
You can learn more about this by reading Python’s Range documentation.
Throughout this course, we’ve seen several different ways to work with strings. In Python, just like in many other programming languages, it is possible to treat strings as a list of individual characters. Because of this, we can write many useful programs that use and manipulate strings in a variety of ways.
Let’s look at one quick example program that can manipulate strings just like a list! Consider the following Python program:
import random
import string
def encode(secret, step):
output = ""
for i in secret:
output = output + i
for j in range(step - 1):
output = output + random.choice(string.ascii_lowercase)
return output
def decode(code, step):
return code[::step]
def main():
secret = input("Enter a secret word")
step = int(input("Enter a positive integer"))
code = encode(secret, step)
print("Your code is:")
print(code)
print()
decoded = decode(code, step)
print("I can decode it back to:")
print(decoded)
main()
This is a very simple encoding program that will allow the user to enter a secret phrase, encode it by inserting many random characters between the characters of the word itself, and then show that it can be decoded once again. This is very similar to how a Scytale encodes messages.
Let’s briefly walk through this example using Python Tutor to see how it works. As always, we can copy and paste this code to Python Tutor, or click this Python Tutor link to open it in a browser window.
We can skip ahead to the point where the code enters the main function, as shown in this state:
Let’s assume that the user inputs the string "password"
for the secret word. That will be stored in the secret
variable. On the next line, we’ll ask the user to input a positive integer:
For this input, we’ll assume the user chooses to input the number
$ 5 $. So, we’ll store that in the step
variable in our main()
function, as shown here:
At this point, we’re ready to call the encode()
function, which requires two parameters. We’ll use the variables secret
and step
as the arguments to those parameters, so Python Tutor will create a new frame for the encode()
function and store those values within it:
At this point, we can notice one very important difference between strings and lists. Even though a string can be treated like a list, as we’ll see in this example program, it is still stored as a single variable item in the frame. So, the encode()
function’s frame now contains a copy of the string secret
, not a pointer to the original variable in the main()
function’s frame. Technically, we would say that strings are an immutable data type, so we cannot change them from within a function like we can do with lists.
Inside of the encode()
function, we’ll start by creating a new variable output
, which is initially set to store an empty string.
Then, we’ll reach a for loop. This for loop will iterate through each character in the secret
string, one at a time. So, just like with a list, each character will be stored in the iterator variable i
so we can use it inside of our for loop. For the first iteration, we’ll store the character 'p'
in the iterator variable i
, then we’ll enter the loop:
Inside of the loop, the first step is to append the current iterator variable i
to the output
string. So, we’ll place the character 'p'
at the end of that string:
Then, we’ll reach a second for loop. This loop will repeat step - 1
times, so we’ll enter the loop and set the iterator variable j
to be
$ 0 $ initially.
Inside of this loop, we have one complex line of code that we haven’t seen before. First, we have the string.ascii_lowercase
list, which is a built-in list that is part of the string
library which contains all
$ 26 $ lowercase letters of the English alphabet. To use this list, we have to include the import string
line at the top of our file. Then, we use a special function named random.choice()
, which is used to choose a random element from a list. So, we’ll also have to include the import random
line at the top of our file to use that library as well. Finally, we’ll add that character to the end of the output
string.
We’ll repeat this process a few more times. Once we exit the innermost for loop, we should be at this state:
In the output
variable, we now see the first character of our secret word, 'p'
, followed by four random characters. These random characters make it more difficult for someone to decode our message. We’ll repeat this process for each of the other letters in our secret word. So, at the end of the encode()
function, we should be at this state:
As we can see, the output
variable appears to be completely random at first glance, which is exactly what we want. At this point, the encode()
function will return that string back to the main()
function, and it will be stored in the code
variable there.
Next, the main()
function will print out some helpful output, and then eventually it will reach the line that calls the decode()
function.
Once again, we’ll copy the values in the code
and step
variables to the decode()
function’s frame since they are provided as arguments, and then we’ll enter the decode()
function:
In this function, we see that we can decode our encoded phrase using a simple slicing operation, with the step
variable providing the key we need to get our secret word out of the encoded string. We’ll return that back to the main()
function:
There we go! We’ve shown that we can easily construct an encoded phrase in a string using a secret word and a step variable, and then we can decode that phrase using a simple string slice. At the same time, we were able to explore how strings can be used like lists by iterating through a string and creating slices of a string, but also that strings are immutable and aren’t passed using call by reference like lists are. Instead, strings use call by value in Python, so we have to remember to return our updated strings at the end of any functions that manipulate them.
Thankfully, being able to work with strings in Python using the same methods as lists makes it very easy to write a wide variety of programs that create and manipulate strings!
Finally, let’s work through another full example program in Python that uses lists and strings, just to gain some more experience with the various ways we can use the techniques we’ve learned in this lab.
For this example, consider the following problem statement:
Write a program to construct and print Pascal’s Triangle for a given number of rows. The user should be prompted to enter a positive integer indicating the number of rows, and if the user provides an invalid input the program should prompt for additional input until valid input is received.
Pascal’s Triangle is a simple mathematical construct that begins with the value $ 1 $, and then each value below on the triangle is the sum of the two values immediately above it. The first few rows of Pascal’s Triangle are shown below:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
We can easily do this with loops and lists. So, let’s go through the process of building that program from scratch.
To begin, we can start with our usual program structure consisting of a main()
function and a call to the main()
function at the bottom of the file:
def main():
main()
Next, we’ll include the positive_input()
function that we’ve used in several previous labs, which handles prompting the user for a positive integer input and will prompt the user for additional input if an invalid value is provided. Once again, we’re doing our best to reuse pieces of code we’ve already written:
def positive_input():
x = int(input("Enter a positive integer: "))
while x <= 0:
print("Invalid input!")
x = int(input("Enter a positive integer: "))
return x
def main():
main()
In our main()
function, we can quickly call the positive_input()
function to get the required piece of input from the user and store it in the variable n
:
def positive_input():
x = int(input("Enter a positive integer: "))
while x <= 0:
print("Invalid input!")
x = int(input("Enter a positive integer: "))
return x
def main():
n = positive_input()
main()
That’s the basic structure of our program to handle input. It’s very simple and mostly reused code, so it should be quick and easy to get to this point of our development process.
Next, we should add some of the initial steps to our program. For example, we know that the first row of Pascal’s Triangle is simply the number $ 1 $, so we can create an initial list that contains just that single element, and then print that to the terminal:
def main():
n = positive_input()
row = [1]
print(row)
Next, we know we need to create additional rows of Pascal’s Triangle, up to n
rows in total. So, we can quickly build a for loop that will handle that in our main()
function:
def main():
n = positive_input()
row = [1]
print(row)
for i in range(1, n):
# update row
print(row)
Notice that we are starting our range at
$ 1 $ instead of
$ 0 $. This is because we’ve already printed the first row, so we only want to print additional rows if the value of n
is greater than
$ 1 $. That’s the basic structure we need in our main()
function
Finally, we need to deal with the process of updating our row list to include the next row’s information. While we could do that directly in the main()
function, it makes the most sense to create a separate method for that. So, we’ll start by creating a new function update_row()
that requires a single parameter:
def update_row(row):
In this function, we need to use the previous row on Pascal’s Triangle to compute the next row. This is a bit tricky to figure out, but thankfully there are a few helpful rules that we can follow:
i+1
of the new row is the sum of the items at index i
and i+1
on the previous row.Using those rules, we can write a loop in code that will create a new list for the next row. First, since we’ll be using multiple items from the row
list in our loop, we’ll need to start with the generic while loop structure to iterate through the list:
def update_row(row):
i = 0
while i < len(row):
# use list elements
i = i + 1
We can also initialize our new list to start with the value $ 1 $ initially:
def update_row(row):
i = 0
new_row = [1]
while i < len(row):
# use list elements
i = i + 1
Inside of the while loop, we can append new items to our new_row
by simply summing the elements at index i
and i + 1
of our previous list:
def update_row(row):
i = 0
new_row = [1]
while i < len(row):
new_row.append(row[i] + row[i + 1])
i = i + 1
At the end, we’ll need to add the final $ 1 $ to the new row. Finally, since we’re creating a new list instead of modifying the existing one, we’ll need to return that reference back to the main function:
def update_row(row):
i = 0
new_row = [1]
while i < len(row):
new_row.append(row[i] + row[i + 1])
i = i + 1
new_row.append(1)
return new_row
It may seem like we can simply do row = new_row
in the update_row()
method instead of returning it, but that actually doesn’t work. This is because the row
variable in the update_row()
function’s frame is a reference to the previous list in memory. If we update that reference, it will point to the new list in the update_row()
function’s frame, but not in the main()
frame - that variable still points to the previous list. You can confirm this behavior by testing it in Python Tutor to see how it looks in memory.
The last step is to simply update the for loop in our main()
function to call the update_row()
function and store the returned list reference back into the row
variable. So, our final program looks like this.
def positive_input():
x = int(input("Enter a positive integer: "))
while x <= 0:
print("Invalid input!")
x = int(input("Enter a positive integer: "))
return x
def update_row(row):
i = 0
new_row = [1]
while i < len(row):
new_row.append(row[i] + row[i + 1])
i = i + 1
new_row.append(1)
return new_row
def main():
n = positive_input()
row = [1]
print(row)
for i in range(1, n):
row = update_row(row)
print(row)
main()
Now that we’ve completed this program, let’s test it once and make sure that it produces the correct output.
When we run this program, we should see the following appear in our terminal:
Uh oh! We’ve run into an error! This error is telling us that somewhere we are trying to access an index inside of a list that doesn’t exist. Helpfully, it tells us that we are doing this in the update_row()
function, right inside of the for loop:
def update_row(row):
i = 0
new_row = [1]
while i < len(row):
new_row.append(row[i] + row[i + 1])
i = i + 1
new_row.append(1)
return new_row
So, let’s see if we can figure out what is going on here. Let’s assume that it is failing when we are passing in the first row, which is simply the list [1]
. On that line of code, we are appending a new item to the end of our new_list
that is the sum of row[i]
and row[i+1]
.
Ah ha! At this point, our row
only contains a single item, so even if i
is initially set to
$ 0 $, we cannot access an element that has index
$ 1 $ in that list. That’s where our logic error is coming from!
Since we need to access elements that are at one index higher than i
, we must adjust our while loop so that it stops at the element before the last one. We can easily do that by modifying our while loop’s Boolean expression to i < len(row) - 1
. If we make that change and try our program again, we should see the correct output:
The full, working example code is shown here:
def positive_input():
x = int(input("Enter a positive integer: "))
while x <= 0:
print("Invalid input!")
x = int(input("Enter a positive integer: "))
return x
def update_row(row):
i = 0
new_row = [1]
while i < len(row) - 1:
new_row.append(row[i] + row[i + 1])
i = i + 1
new_row.append(1)
return new_row
def main():
n = positive_input()
row = [1]
print(row)
for i in range(1, n):
row = update_row(row)
print(row)
main()
There we go! We’ve written a complete program that will generate Pascal’s Triangle for any given number of rows. It uses both while and for loops and lists to accomplish that goal. Try to work through this entire example and run the program many times along the way to make sure you have a good understanding of why it works before continuing in this lab.
You might be wondering why we’ve chosen to keep the logic error in this example program instead of simply fixing it in our explanation. As it turns out, that logic error was actually made by the author while writing this example, and we felt that it was best to demonstrate how to quickly identify and resolve such an error instead of simply explaining it away. Hopefully seeing how easy it is to make simple logic errors like this will help you understand that it can happen to anyone, and it is not a big deal to have to fix small bugs along the way.
Let’s try some simple practice problems. These problems are not graded - they are just for you to practice before doing the real exercises in the lab itself. You can find the answers below each question by clicking the button below each question.
Consider the following Python program:
def positive_input():
x = int(input("Enter a positive integer: "))
while x <= 0:
print("Invalid input!")
x = int(input("Enter a positive integer: "))
return x
def foo(numbers):
output = []
for i in range(0, len(numbers) - 1, 2):
output.append(numbers[i] + numbers[i+1])
if len(numbers) % 2 == 1:
output.append(numbers[-1])
return output
def main():
print("First, we need the number of inputs.")
n = positive_input()
print(f"Now enter {n} numbers:")
numbers = []
for i in range(n):
numbers.append(positive_input())
while len(numbers) > 1:
numbers = foo(numbers)
print(numbers[0])
main()
Explain, in your own words, how the output that is eventually printed relates to the list of numbers that user inputs.
A fully correct answer is a succinct description of the output as it relates to the input. A partially correct answer is a step-by-step description of each line in the program and the output it will produce based on the input.
Hint: work through this code as if the user inputs a few unique numbers, and see what is being stored at each step along the way.
Consider the following Python program:
def positive_input():
x = int(input("Enter a positive integer: "))
while x <= 0:
print("Invalid input!")
x = int(input("Enter a positive integer: "))
return x
def main():
n = positive_input()
numbers = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
while n > 0:
numbers[n % 10] = numbers[n % 10] + 1
n = n // 10
print(numbers)
main()
Explain, in your own words, how the output that is eventually printed relates to the number that the user inputs.
A fully correct answer is a succinct description of the output as it relates to the input. A partially correct answer is a step-by-step description of each line in the program and the output it will produce based on the input.
Hint: try inputting a number with many digits.
Write a complete Python program that meets the specification below.
Write a program that will require the user to input $ 10 $ positive integers that are in ascending order and stored in a list. If the user inputs a number that is not strictly greater than the previous number stored in the list, the program should simply print an error message and prompt for input again.
Once the list is populated with $ 10 $ positive integers in ascending order, it should print the list to the terminal. Then, it should compute and print the difference between the first and last value in the list.
One possible output is shown below:
[1, 3, 5, 6, 7, 8, 12, 13, 15, 16]
15
In this lab, we introduced several major important topics in Python. Let’s quickly review them.
Lists in Python allow us to store multiple elements in a single variable, with each element identified by a unique index within the list.
Lists can be created using square brackets []
:
list_a = []
list_b = [5, 3, 7]
New elements can be added to a list using the append()
method:
list_a = []
list_a.append(4)
Items in a list can be accessed and updated using square brackets:
list_b = [5, 3, 7]
list_b[2] = list_b[0] + list_b[1]
Lists can be iterated using both for loops and while loops:
list_b = [5, 3, 7]
for i in list_b:
print(i)
j = 0
while j < len(list_b)
print(list_b[j])
j = j + 1
Lists should not be changed while iterating using a for loop.
When calling a function that accepts a list as a parameter, the argument is passed using call by reference instead of call by value. The original list can be modified by the function, but it cannot be replaced with a new list unless the reference to that new list is returned from the function.
Strings can be iterated just like a list, and we can use square brackets to access individual characters in a string. However, strings (unlike lists) are still passed as call by value when provided as an argument to a function.
We can create a slice of a list by specifying a start
, end
and step
value separated by colons. The values may be omitted, and may also be negative.
list_b = [5, 3, 7]
print(list_b[-1])
print(list_b[0:1])
print(list_b[0:2:2])
print(list_b[::-1])
Previously, we learned how we can use lists in Python to store multiple values, or elements, in a single variable. Each element in a list is given an index, which allows us to uniquely reference and identify each element’s position in a list. Python lists use consecutive integers starting at $ 0 $ as indexes, which makes for a pretty easy to use data structure.
However, what if we would like to use some other value besides an integer as the index? For example, if we create a data structure to store information about a user account, wouldn’t it be much easier if we could use the username as the index, allowing us to quickly find that particular user’s data?
Thankfully, Python includes another built-in data structure, called a dictionary, that allows us to do exactly that. In a dictionary, instead of indexes we have keys, which can be any data type, that uniquely identify each item, or value, in the dictionary. Other programming languages refer to dictionaries as hash tables, hash maps, or associative arrays, which gives a clue to how they work behind the scenes.
In this lab, we’ll learn how to create and use dictionaries in Python. Dictionaries are a very powerful and useful data structure in Python, with many uses. Let’s see how they work!
In theory, a dictionary is very similar to a list in Python - it is a data structure that can store many different items within a single variable. However, instead of just storing single values as elements and assigning them sequential indexes, dictionaries store a number of key-value pairs. A key is any value that can be used as a unique identifier for the associated value to be stored in the dictionary.
For example, we can use a dictionary to store the “score” for various words in the game of Scrabble. In this case, our key would be the word stored as a string, such as "test"
, and the value would be an integer representing the score, 4
. These two items make up a key-value pair ("test", 4)
that can be stored in a dictionary.
There is one major rule that dictionaries must follow - each key in a dictionary must be unique. Or, put another way, if the dictionary already contains a value for a given key, and we try to add another value that uses the same key, the first value will be overwritten. So, we must be careful and make sure that the keys we choose to use are indeed unique so that they will work in a dictionary.
Dictionaries in Python can be created in much the same way as a list. We can create an empty dictionary using a set of curly braces {}
as shown here:
dict_1 = {}
We can also create a dictionary that contains some initial key-value pairs by placing them inside of the curly braces. Each key-value pair is separated by a comma ,
with the key provided first, followed by a colon :
and then the value. Here’s a brief example:
dict_2 = {"a": 1, "b": 2, "c": 3}
In this dictionary, the keys are the strings "a"
, "b"
, "c"
, and each one is associated with the values 1
, 2
, and 3
, respectively.
Finally, to make it a bit easier to read our code, we can also reformat the code to create a dictionary across multiple lines, with each key-value pair shown on its own line as seen in this example:
dict_3 = {
"cat": "mammal",
"lizard": "reptile",
"goldfish": "fish",
"chickadee": "bird"
}
This dictionary associates various animal species with the common name of their classification within the animal kingdom.
We can also easily add new items to a dictionary by simply providing a key inside of square brackets []
and then using an assignment statement to assign that key a value. Consider this example:
dict_1 = {}
dict_1["Kansas"] = "Topeka"
dict_1["Nebraska"] = "Lincoln"
dict_1["Missouri"] = "Jefferson City"
Here, we start by creating an empty dictionary, and then we can add key-value pairs using the names of states as the key and the state’s capital city as the value.
Notice that we use square brackets []
to access individual items in a dictionary, just like with lists, but we use curly braces {}
to create a dictionary. This can cause confusion for many new Python programmers. So, pay special attention to the syntax of each data structure, and remember that it can sometimes be difficult to tell whether a data structure in Python is a list or a dictionary without reading the code to see where it was created.
Once a dictionary contains items, we can access the value associated with a given key by providing that key within square brackets []
after the variable storing the dictionary. Here’s an example of what that looks like in Python:
dict_3 = {
"cat": "mammal",
"lizard": "reptile",
"goldfish": "fish",
"chickadee": "bird"
}
print(dict_3["cat"]) # mammal
As we can see, we’re able to provide the key "cat"
and then access the associated value "mammal"
that was stored in the dictionary.
What if we try to access a key that doesn’t exist? In that case, Python will raise a “KeyError” and the program will crash. So, we’ll have to be careful and make sure we only try to access keys that are actually stored in the dictionary, unless we are using them in an assignment statement to add a new key-value pair.
We can also update the value associated with a given key in a dictionary using an assignment statement, as shown here:
dict_3 = {
"cat": "mammal",
"lizard": "reptile",
"goldfish": "fish",
"chickadee": "bird"
}
dict_3["cat"] = "feline"
print(dict_3["cat"]) # feline
In this example, we are simply replacing the value for the key "cat"
with a new string “feline”. Remember that the keys in a dictionary must be unique, so if we try to add a new value using the same key, it will simply overwrite the existing value associated with that key.
To really understand how dictionaries work in Python, let’s go through a quick example using Python Tutor. Consider the following Python program:
def main():
base = int(input("Enter a whole number: "))
powers = {}
for i in range(6):
powers[f"{base}^{i}"] = base ** i
print(powers)
main()
This program will ask the user to provide a number as input, and then it will populate a dictionary where the key is a string representing a mathematical expression including that number, and the value will be the result of that expression. Let’s trace through this program in Python Tutor. As always, you can copy and paste this code in Python Tutor, or click this Python Tutor link to open it on the web.
When we start tracing the program in Python Tutor, we can skip ahead until it reaches the first line of code in the main()
function, at which point we’ll see this state:
This first line of code asks the user to input a value. For this example, let’s assume the user inputs the string "3"
. So, we’ll store the integer value
$ 3 $ in the base
variable as seen here:
This line will create an empty dictionary in the powers
variable. Just like lists in Python, a dictionary is actually stored in the objects area in memory, and the variable is simply a pointer, or reference, to the location in memory where the dictionary will be stored. So, after we execute that line of code, we should see this state:
At this point, we reach the for loop. This loop will iterate
$ 6 $ times, so we’ll start by storing the first value
$ 0 $ in the iterator variable i
and then entering the loop:
Inside of the loop, we see a single line of code that is pretty complex. This line will actually perform three different operations! First, it will generate a string using the string format()
method, which will become the new key that is added to the dictionary. Then, it will compute the value of the base
variable raised to the power of i
. Finally, it will store that computed value in the dictionary associated with the newly created key. Once all of those steps are complete, we’ll see a new item in the dictionary as shown here:
In Python Tutor, we can see the key for the item on the left, and then the associated value on the right. So, it is similar to how a list is represented, but instead of just showing the index, it will show the key and the value.
At this point, our program will loop back to the start of the for loop. Since there are more iterations to complete, we’ll update the iterator variable i
to
$ 1 $, and then enter the loop again.
Once inside the loop, we’ll add another item to the dictionary, and then loop back to the top.
From here, hopefully it is clear what the rest of the program will do. Each time we enter the loop, we’ll generate a new key and value pair to add to the dictionary. So, let’s skip ahead to the end of the loop.
At the end of the program, we’ll print the contents of the dictionary to the terminal. Just like with lists, Python will print dictionaries in a clean and easy to understand format, as shown in the output in Python Tutor:
A full animation of this program is included below.
As we can see, dictionaries in Python work very similar to lists. When we create a dictionary, it is stored in the objects area in memory, and we can easily add, access, and even print the contents of a dictionary in our code. Dictionaries are really one of the most flexible data structures available in Python.
Another useful way we can work with dictionaries in Python is by iterating through them. In Python 3.6 and beyond, the items in a dictionary will be presented in the same order that they were added to the dictionary. This isn’t always the case in other languages or older versions of Python
There are two ways we can iterate through a dictionary in Python. Below is an example of the first, and simplest, method:
dict_3 = {
"cat": "mammal",
"lizard": "reptile",
"goldfish": "fish",
"chickadee": "bird"
}
for key in dict_3:
print(f"{key}: {dict_3[key]}")
In this method, we are iterating through the entire dictionary using a for loop. However, the dictionary will only store the key for each key-value pair in the iterator variable. So, to access the value associated with the key, we can simply place it in square brackets after the dictionary’s variable name.
When we run this program, we should see the following output:
As we can see, it will print each key-value pair in the dictionary, one per line.
The other method we can use when looping through a dictionary allows us to access both the keys and values directly within the loop. Here’s an example of using that method:
dict_3 = {
"cat": "mammal",
"lizard": "reptile",
"goldfish": "fish",
"chickadee": "bird"
}
for key, value in dict_3.items():
print(f"{key}: {value}")
In this method, instead of iterating through the dictionary itself, we iterate through the items()
in the dictionary. When we call the items()
function in a dictionary, we are given a list of the key-value pairs that it contains, and each key-value pair is stored as a tuple. From there, we can access each of the elements in the tuple individually using a comma ,
between two variable names. It is a bit complex, and touches on a few concepts in Python that we won’t cover in this course, but hopefully it makes sense by looking at the code.
When we run this program, we’ll receive the same output as the previous example:
While iterating through dictionaries is not as common as iterating through lists, it is still very important to know that it can be done. Hopefully you’ll find this example useful as you continue to work with dictionaries in Python.
Finally, let’s briefly look at how dictionaries operate when used as parameters to functions in Python. As we can probably guess from the earlier example in Python Tutor, dictionaries also use call by reference, just like lists.
Here’s a short example program in Python that demonstrates the use of dictionaries with functions.
def add_score(scores):
name = input("Enter a name: ")
score = int(input("Enter a score: "))
scores[name] = score
def average_score(scores):
total = 0
for key, value in scores.items():
total = total + value
return total / len(scores)
def main():
scores = {}
for i in range(4):
add_score(scores)
print(average_score(scores))
main()
Let’s take a look at this program using Python Tutor to see how it works. As always, you can copy and paste this code into Python Tutor, or click this Python Tutor link in the lab to open it in a web browser.
First, Python Tutor will iterate through the code and record all of the functions in the objects area. Once it reaches the call to the main()
function at the bottom, it will jump to the code inside of that function. At that point, we should see this state:
The first thing that this program will do is create an empty dictionary in the scores
variable. So, after executing that line of code, we’ll see a new dictionary in the objects area in memory as shown here:
Next, we’ll reach a simple for loop that will iterate $ 4 $ times, so we’ll enter that loop:
Inside of the loop, we are calling the add_score()
function and providing the scores
dictionary as an argument. So, when Python Tutor jumps to execute that function’s code, we’ll see it create a new frame for the function and populate that frame with the arguments provided as part of the function call:
Since dictionaries in Python also use call by reference, we’ll see that the scores
parameter in the add_score()
function’s frame is just a reference back to the same empty dictionary that was created earlier. The scores
variable in the main()
function’s frame also points to the same object in memory.
The add_scores()
function will prompt the user to input a name and a score. So, if we assume that the user inputs the string "Name"
as the name, and "95"
as the score, we should see this state in Python Tutor:
The last line of the function will add a new key-value pair to the dictionary, with the name used as the key and the score as the value. So, when the function is ready to return, we should see this state:
At this point, the control flow will return back to the main function, so our frame for the add_score()
function will be removed. Thankfully, we can see that the new item we added to the dictionary is present in the one dictionary object in memory, which is also referenced from the main()
function’s frame. So, once we are back in the main()
function, we’ll see this state:
As we can see, dictionaries properly follow call by reference, so if we modify a dictionary that is passed to a function as an argument, we don’t have to return it back if we are just working with the same object.
At this point, the program will repeat that process three more times. We’ll skip ahead a bit to the end of the for loop in the main function, which will be this state:
Here, our program will call the average_score()
function to get the average of all of the scores in the dictionary. So, we’ll jump up to that function and provide the current dictionary as an argument, as shown here:
This function is very straightforward. However, let’s skip ahead a bit to when we are inside of the for loop, just to see what that looks like:
In our code, we are using the items()
function of the dictionary to allow us to iterate over a list of the key-value pairs in the dictionary. So, inside of that for loop, we see both a key
and a value
variable, and they are populated with the first key and value from within our dictionary.
Once we’ve iterated through the entire dictionary, we’ll be at this state:
We’re ready to return from the function, and we’ve computed that our return value will be
$ 92.5 $, as seen the frame for the average_score()
function. Once we are back in the main()
function, we’ll simply print that value to the output:
As we’ve seen in this example, working with dictionaries and functions in Python is practically identical to what we saw with lists - both data structures use call by reference when they are passed as arguments to a function. So, as long as we understand how Python is storing the data in memory, we can effectively build programs that operate the way we expect them to.
Finally, let’s go through a complete worked example program that uses dictionaries in Python. Consider the following problem statement:
Write a program that will compute the score of a given word in a game of Scrabble. Assume that there are no special squares present. The program should prompt the user to input a string that only contains letters, and then compute the total score of that string by summing the value assigned to each letter. If the user inputs a string that contains invalid characters, the program should prompt for additional input until a valid word is received.
This is a pretty simple program that can easily make use of a dictionary in Python. So, let’s go through the process of solving it!
As always, we can start our solution by including a main()
function and a call to the main()
function at the bottom of our code, as shown here:
def main():
main()
Next, we’ll probably want to create a function to handle requesting input from the user. So, we’ll add that function to our solution as well, and we’ll call it from within the main()
function itself:
def get_input():
def main():
word = get_input()
main()
In the get_input()
function, we’ll need the while loop structure we’ve already learned to handle input and verify that it is correct, so let’s go ahead and add that:
def get_input():
word = input("Enter a single word: ")
while # word is not valid
print("Error! Invalid Input!")
word = input("Enter a single word: ")
return word
def main():
word = get_input()
main()
The only part we have to figure out is how we can determine if a word contains only valid characters. There are many different approaches we could follow, but the simplest is to use the isalpha()
function that is part of the Python string
data type. This function, along with many others, are described in the Official Python Documentation.
So, let’s update our code to use that function as the Boolean expression in our while loop:
def get_input():
word = input("Enter a single word: ")
while not word.isalpha():
print("Error! Invalid Input!")
word = input("Enter a single word: ")
return word
def main():
word = get_input()
main()
There we go! that covers the get_input()
function, which will prompt the user to input a string that only contains letters.
Next, let’s look at writing a function that will accept a single word as a parameter, and then compute the score of that word according to the official Scrabble scoring rules.
We can start with a simple function definition as shown here:
def compute_score(word):
First, let’s quickly convert the word
we are given to lowercase, just in case the user has included any uppercase letters. Remember, we already know that this word should only contain letters, but it may be a mix of uppercase and lowercase. We can use the lower()
function to make this conversion:
def compute_score(word):
word = word.lower()
Next, we’ll need to iterate through each letter in the word. Since we can treat a string just like a list, we can use a for loop for this:
def compute_score(word):
word = word.lower()
for letter in word:
We’ll also need some way to keep track of the total score, and then return that value at the end of the function. This is a pretty common pattern in programming known as the accumulator pattern. So, let’s add a total
variable to our program, starting it at
$ 0 $ and then returning it at the end:
def compute_score(word):
word = word.lower()
total = 0
for letter in word:
# add letter's score to total
return total
Finally, we just need some way to convert a letter into a score in Scrabble. Thankfully, we can refer to the Official Scrabble FAQ to find out how many points each letter is worth:
There are many different ways we could build this program, but one simple way would be to assign each letter a value in a dictionary! To make things even easier, we can leave out all of the letters that are worth 1 point, and just include the letters with higher point values. By doing so, if we don’t find our desired letter in the dictionary, we can simply assume that the score must be 1!
So, let’s add that dictionary to our code:
def compute_score(word):
scores = {"d": 2, "g": 2, "b": 3, "c": 3, "m": 3, "p": 3,
"f": 4, "h": 4, "v": 4, "w": 4, "y": 4, "k": 5,
"j": 8, "x": 8, "q": 10, "z": 10}
word = word.lower()
total = 0
for letter in word:
# add letter's score to total
return total
Now that we have this dictionary available in our code, we can use it inside of the for loop to compute the total score of the word by looking up the value of each letter. However, before we can directly access a letter, we first must determine if a given letter is contained in the dictionary. In Python, we can do this using the in
keyword. So, we can add an if statement inside of our for loop that is structured as shown here:
def compute_score(word):
scores = {"d": 2, "g": 2, "b": 3, "c": 3, "m": 3, "p": 3,
"f": 4, "h": 4, "v": 4, "w": 4, "y": 4, "k": 5,
"j": 8, "x": 8, "q": 10, "z": 10}
word = word.lower()
total = 0
for letter in word:
if letter in scores:
total = total + scores[letter]
else:
total = total + 1
return total
The Boolean expression letter in scores
will return true if the current letter in our word is present as a key in the dictionary scores
. The same syntax also works for determining if an element is present in a list!
We can complete our program by simply printing the score returned by the compute_score()
function in our main()
function. So, the complete program is shown below:
def get_input():
word = input("Enter a single word: ")
while not word.isalpha():
print("Error! Invalid Input!")
word = input("Enter a single word: ")
return word
def compute_score(word):
scores = {"d": 2, "g": 2, "b": 3, "c": 3, "m": 3, "p": 3,
"f": 4, "h": 4, "v": 4, "w": 4, "y": 4, "k": 5,
"j": 8, "x": 8, "q": 10, "z": 10}
word = word.lower()
total = 0
for letter in word:
if letter in scores:
total = total + scores[letter]
else:
total = total + 1
return total
def main():
word = get_input()
print(compute_score(word))
main()
We can confirm our program works correctly by running it and testing a few different inputs, as shown below:
This example shows a great way to use dictionaries in our program - we can assign values to various keys, and then easily look them up in the dictionary as needed. Feel free to run this example in Python Tutor on your own to see how it works, and see if you can come up with your own high-scoring Scrabble words.
Let’s try some simple practice problems. These problems are not graded - they are just for you to practice before doing the real exercises in the lab itself. You can find the answers below each question by clicking the button below each question.
Consider the following Python program:
# Same function as the worked example - only accepts letters
def get_input():
word = input("Enter a single word: ")
while not word.isalpha():
print("Error! Invalid Input!")
word = input("Enter a single word: ")
return word
# Notice that this returns a new dictionary
def foo(words):
output = {}
for key, value in words.items():
letter = value[0]
remain = value[1:]
for i in range(len(key)):
new = key[0:i] + letter + key[i:]
output[new] = remain
output[key + letter] = remain
return output
def main():
word = get_input()
words = {"": word}
for i in range(len(word)):
words = foo(words)
print(words)
main()
Explain, in your own words, how the the keys of the dictionary that is printed at the end relates to the original word that is provided as input.
A fully correct answer is a succinct description of the output as it relates to the input. A partially correct answer is a step-by-step description of each line in the program and the output it will produce based on the input.
Hint: try using short words, some with unique letters such as “cat” and others with repeated letters such as “abba”. Why does the requirement that dictionary be unique help with this?
Consider the following Python program:
def main():
values = {
"M": 1000, "CM": -100, "D": 500, "CD": -100,
"C": 100, "XC": -10, "L": 50, "XL": -10,
"X": 10, "IX": -1, "V": 5, "IV": -1, "I": 1
}
n = input("Enter a string: ")
out = 0
for i in range(len(n)):
if i + 1 < len(n) and n[i:i+2] in values:
out = out + values[n[i:i+2]]
elif n[i] in values:
out = out + values[n[i]]
else:
print("Error!")
print(out)
main()
Explain, in your own words, how the output that is eventually printed relates to the string that the user inputs.
A fully correct answer is a succinct description of the output as it relates to the input. A partially correct answer is a step-by-step description of each line in the program and the output it will produce based on the input.
Hint: what numerical system uses letters such as M, D, C, L, X, V, and I?
Write a complete Python program that meets the specification below.
The program should first prompt the user to input a single positive integer. If the number provided as input is not positive, the program should print an error and prompt for input again until a valid input is received. Then, the program should use a dictionary to convert the individual digits of the number to their equivalent names, and then print the result to the terminal. The keys in the dictionary should be the individual digits, either as strings or integers, and the values should be strings containing the name of each digit.
For example, if the user provides the following input:
1234
The program should produce the following output:
one two three four
To receive full credit, your program must make use of a dictionary as described above. Your program must also make use of two additional functions in addition to the required main()
function.
Be careful about data types! You may find it easier to convert the number back to a string once you’ve determined that it is positive.
In this lab, we introduced several major important topics in Python. Let’s quickly review them.
Dictionaries in Python allow us to store key-value pairs in a single data structure. Keys are used to uniquely identify an associated value.
Dictionaries can be created using curly brackets {}
:
dict_1 = {}
dict_2 = {"a": 1, "b": 2, "c": 3}
New elements can be added to a dictionary by providing a new key in square brackets:
dict_2 = {"a": 1, "b": 2, "c": 3}
dict_2["d"] = 4
Items can also be accessed and updated using square brackets
dict_2 = {"a": 1, "b": 2, "c": 3}
dict_2["c"] = dict_2["a"] + dict_2["b"]
Dictionaries can be iterated by just the keys or by the keys and values in a tuple:
dict_3 = {
"cat": "mammal",
"lizard": "reptile",
"goldfish": "fish",
"chickadee": "bird"
}
# keys only
for key in dict_3:
print(f"{key}: {dict_3[key]}")
# keys and values
for key, value in dict_3.items():
print(f"{key}: {value}")
When calling a function that accepts a dictionary as a parameter, the argument is passed using call by reference instead of call by value. The original dictionary can be modified by the function, but it cannot be replaced with a new dictionary unless the reference to that new dictionary is returned from the function.
Extra Content!
This video uses the Atom text editor, which was discontinued by GitHub. We now recommend using Visual Studio Code instead. The text below has been updated, but the video has not. Installing and using Visual Studio Code should be very similar to what is shown here.
In this lab, we’re going to cover various ways to install Python on your computer. First, we’ll cover the basic way to install Python on a Windows system. To begin, open a web browser and go to python.org/downloads. Here you can find downloads for the latest version of Python for Windows as well as other operating systems. I’m going to click the button here to download the latest version of Python for Windows.
Once the file is downloaded, I can double click it to open and run the installer. When we run the installer, there are a couple of options that we’ll want to change. Notice by default, it will install Python into this complicated directory here, which can make it very hard to find. Also, it does not add Python to the path which we definitely want to do. So we’ll check mark this checkbox and then we will customize the installation. We’ll go ahead and check mark the boxes to install all of the optional features. When choosing advanced options, we can check mark the option to install for all users, which will place Python in the program files directory, making it much easier to find. We can click install and it will install very quickly.
Once we’ve installed Python on our computer, we can verify that it works by loading it in PowerShell. On Windows, I’m just going to search for PowerShell in the start menu to find that program. Once PowerShell loads, we can run Python using the python
command. Notice that this is different than most Linux and Mac systems where to run Python version three, we must run the command python3
. On Windows with the most recent installer, we can simply use the python
command followed by --version
to see the current version of Python. If we type python3
, it might load the Microsoft Store in prompting us to install an older version of Python. This can get a bit confusing, it’s one of the problems with Python is the inconsistent usage of the python
command to refer to Python version two, and Python version three. So if you follow this guide to install Python on your computer, just be aware that anytime you see the python3
command in documentation or in the labs, you’ll just need to use the python
command instead, which will run Python version three.
To make it easy to write Python files on Windows, you may also want to install a text editor that is specifically designed for writing code. One of the easiest to use is Visual Studio Code which is a text editor from Microsoft. So we’ll download and install that as well. Once again, once we’ve downloaded Visual Studio Code, we can double click the installer to install it. Once Visual Studio Code is installed and open, you can close all of the open tabs to get a view that looks like this.
To make it easy to program in Python, we’re going to create a folder just for the Python work in this course. To do that, I’m going to click the add folders button right here. In Windows, we recommend creating a folder inside of your users home directory for all of your programming work. By default, it will open up the Documents List. But to get to your users folder, we’re going to go to the C drive, then Users. Then we’ll find our username. And then in here, we’re going to create a new folder. And I’m going to name this cis115
to match the class. And we’ll select that folder as the folder to open in Atom. Now we see a view similar to this.
From here, we can easily add a Python file by right clicking on the folder and creating a new file. I’m going to name this file hello.py. And then inside of that file, I can type print hello world as a simple Hello World program. Once I’ve written that program, I can go to the File menu and choose to save the file. I can also use Ctrl+S to Save the file. Once I’ve done that that file should now exist on my system in that folder.
Once we’ve done this, we can run this file using Python by again opening PowerShell. In PowerShell, we need to navigate to where that is that folder is found. Notice that PowerShell already opens into our users folder. So all we should have to do is type cd cis115
to open up that folder. Then we can type python
and the name of the file that we just created to run it. There we go. That’s all it takes to run a Python program on your Windows system. It should give you enough to get started in this class. As always if you have any questions please feel free to reach out to either of the instructors or any of our TAs and we’d be happy to assist you