Programming in Python
Please note this is still under active development. Please contact me if you spot any errors.
by Sam O'Neill
This is an introductory set of units for Python that have been created as the notes for the introductory Programming unit of the undergraduate computing course, 4CM523 - Programming.
It is designed to be reasonably comprehensive and should be read in order. Each unit has a number of lessons each with their own exercise.
I hope you enjoy the course
— Sam
Executing Code and Completing Exercises
If you are taking this course then you will be completing exercises and running code via Visual Studio Code. You will be shown in the labs how to do this. You may see references to Visual Studio Code and main.py in the text, if you prefer to use your own IDE or are comfortable in Python, by all means run the code as you wish.
You can also follow the instructions here.
Alternative ways to run code and do exercises are via the following online Python interpreter - Online GDB Python Compiler.
It is also recommended that you make use of the fantastic Python Tutor. This is an excellent way to view how the code runs step by step. You can also watch a number of excellent videos on their YouTube channel.
Introduction
We will be using the Python programming language in this course.
- Python is a commonly used beginner programming language since it is considered to be more easily readable and has a simpler syntax than other mainstream languages. Syntax refers to the rules of a programming language; similarly to how grammar refers to the rules of a human language.
- Python is also a relatively powerful language. It is used to support YouTube, Google, Instagram, Reddit, and other popular software programs.
- Python has consistently been in the top three most popular programming languages over the past two decades. Other languages that have been consistently popular include Java, C, and Javascript.
In this introductory course, you will be gaining a solid foundation in the Python programming language. The programming concepts you learn will also be helpful for any future programming languages you learn in the future.
How to use this book
You should work through each of the lessons and do the TASK at the end of the lesson.
I would recommend pasting the code snippets into Python Tutor. This is an excellent way to view how the code runs step by step.
You can also refer to the many other books, tutorials and videos found online. An especially useful quick reference is the W3Schools Python Tutorial.
Unit 1 - Getting Started
This unit introduces some things that we need to get up and running. You will learn how to run a basic program, about basic exceptions in Python and about the fundamental data types that Python has to offer - these crucial allow us to handle data in our programs, e.g. storing a number or a persons name.
Hello World
It is customary that the first program that you write in a programming language is the "Hello World" program.
So as not to break this great tradition, let's start our Python journey there!
Python lets you output text to the Terminal.
You can output a piece of text using the print()
function (more on functions later in the course).
Normally we will write out python programs in a source file (script) which is where we will write our code. You will need to create and save this file in Visual Studio Code. Make sure it has the .py extension.
1. Hello World!
For now, we can output a piece of text to the terminal by putting the following line into a Python file.
print("Hello World!")
Once you have added this line, hit the Run button at the top of Visual Studio Code.
You should then see Hello World!
outputted to the terminal.
1.1 Wow! What Just Happened?
When you hit the "Run" button, Python reads your code from the source file and immediately executes it, showing the result in the terminal. Python is an interpreted language, which means that you don’t need to compile your code before running it.
If you would like to execute manually without the run button. Go to the Terminal and type:
python <NAME OF YOUR FILE>.py
This will then convert your code for execution and then run it.
1.2 Tasks
Throughout this course, you'll encounter tasks that you should complete. These are practical exercises designed to reinforce the concepts you've just learned. Some tasks will be more challenging than others, so focus on completing the easier ones before tackling the harder ones.
A TASK will always look like the one given below.
TASKs will normally appear at the bottom of the page
=== TASK ===
Create a Python program in a new file that outputs "Hello World! is cool!"
to the Terminal using the print()
function.
Comments
In programming, it is important to provide comments in your code to explain what you're doing. This is helpful for both yourself and others who might read your code later.
Comments are not interpreted by Python, so they don't affect how your code runs. Python supports two types of comments:
- Single Line Comments
- Multiple Single-Line Comments
1. Single Line Comments
Single-line comments start with a #
(hash symbol). Everything following the #
on that line will be ignored by Python. For example:
# This is a single-line comment (This won't run).
print("I am learning about comments")
Notice that comments are usually shown in a different color in your code editor.
2. Multiple Single-Line Comments
Python doesn't have a specific syntax for multiline comments. Instead, to comment out multiple lines, you need to place a #
at the start of each line. For example:
# This is a single-line comment.
# This is also a single-line comment.
print("I am learning about comments")
If you highlight multiple lines of code and press Ctrl + /
(or Cmd + /
on macOS), the editor will automatically add a #
at the start of each line, turning them into comments.
Note: While you might see triple quotes ('''
or """
) used for what looks like multiline comments, these are actually docstrings used to document functions or classes. They should not be used for regular comments.
3. A Note on Using Comments
There is some debate about how often to comment, and different programmers have different styles. Here's some basic advice:
3.1 Comment When It’s Useful
For example, this comment is not useful:
# This line prints out Hello World!
print("Hello World!")
Everyone can see what the code does, so this comment doesn’t add value. Only comment when it's not obvious what a piece of code does.
3.2 Be Professional
Your comments reflect on you, especially if others are reading your code. Avoid unprofessional comments, like:
# This code sucks!
print("Hello World!")
or:
# Workaround for Tyrion being a traitor and going off with that dragon lady!
print("Hello World!")
3.3 Keep Comments Brief and Clear
Make sure your comments are concise and to the point. If you need to write a longer explanation, split it into multiple lines. For example:
# The following is a comment that has the job of telling the person reading the source code that this prints Hello World!
print("Hello World!")
would be better as
# Prints Hello World!
print("Hello World!")
or:
# The following is a comment that has the following job.
# To tell the person reading the source code that this prints Hello World!
print("Hello World!")
=== TASK ===
Copy the following code into a new Python file.
# This is a single line comment in Python
print("Replace this line with TASK 1")
print("Hello")
print("World")
print("with Comments")
- Modify the code so that the first print statement outputs:
Single line comments start with #
. - Highlight lines 5-7 and press
Ctrl + /
(orCmd + /
on macOS) to comment out those lines.
Your final output in the terminal should look like this:
Single line comments start with #
Objects, Types, Operators, and Expressions
To do anything useful in our programs, Python will need to represent data such as numbers, words and booleans. We will learn more about these in Units 2 and 3.
For now we will introduce the most common 4 built-in data types.
1. Objects and Types
In Python, everything you work with is an object. An object can be a number, a word, or any other type of data. Each object has a specific type, which tells Python what kind of data it is and what operations can be done with it.
Python has several built-in data types, which it uses to represent data internally. We'll start by looking at four common ones:
Data Type | Description |
---|---|
int | An integer. This is a whole number, it can be positive, negative or zero. e.g. 5 |
float | A decimal number. e.g. 3.14 |
str | Text. It consists of individual characters. Strings are enclosed in single quotation marks ' or double quotation marks " . e.g. "Hello World" |
bool | The values True or False . Used to make decisions, more in Unit 3 |
A very useful function in python that we can use is type()
. The type()
function is a useful tool to check what type of data you're working with. Understanding the type of an object helps you know what operations can be performed on it and how Python will handle it in your program.
x = type(10)
print(x)
Here we are assigning the result of type(10)
to the variable x
. Basically this stores the result in memory in a name called x
. We can then print out what is stored in x
.
You could do this in one line like so.
print(type(10))
We will stick to using x
as it is more readable.
If you run either of these you will see the output:
<class 'int'>
This is python telling you that 10
is an object of type int
. For now, read class as type.
Try it for these other objects.
x = 10.3
print(x)
print(type(x))
x = "This is a string"
print(x)
print(type(x))
x = True
print(x)
print(type(x))
This first gets the type of the object, here a str
and then passes that to the print()
function.
2. Operators and Expressions
We can combine objects with operators to form expressions. When evaluated, these expressions produce a new object.
For example, we can combine the objects 10
and 5
with the +
operator,
x = 10 + 5 # new object 15 assigned to x
print(x) # prints out 15
print(type(x)) # prints out <class 'int'>
to create a new object 15
of type int
.
All built-in data types have operators that we can use to form expressions.
For example, we can test if two expressions are equal with the ==
comparison operator.
x = 10 + 5 == 3 * 5 # assign the result of comparing 10 + 5 and 3 * 5.
print(x) # prints out True
print(type(x)) # prints out <class 'bool'>
This will return True
. Python knows how to test whether two numbers are equal and returns you a new object of type bool
.
We will cover operators for numbers, strings, and bools in their respective lessons in this unit.
3. Help Function
The help()
function gets information about an object (it can also be used for other things.)
Try typing the following into the terminal or a Python file:
help(str)
You will need to click in the terminal and press Ctrl + c
to exit this.
You can also type python into the terminal to enter the Python Interative Shell and then type help(str)
and press Enter. You can open this by typing python
into a terminal.
=== TASK ===
Create a new Python file.
Edit the file to output the type of the following expressions. You will need to use the print()
and the type()
function.
Make sure you have read all of the above before attempting this (especially the end of Section 1).
12 + 5
5 + 3.0
12 / 5
12 + 5 == 5 * 3.0
The output of your program should be:
<class 'int'>
<class 'float'>
<class 'float'>
<class 'bool'>
Basic Exceptions
Every time that you try and run your code, Python will first try to parse your code, if it encounters something that it does not recognise, it will raise an exception.
This is a warning to the coder that they have done something invalid and the program will not run. Please read the exception and try to understand it.
The three main exceptions you may encounter for now will be:
SyntaxError
NameError
TypeError
1. SyntaxError
A SyntaxError
is perhaps the most common kind of complaint you get while you are still learning Python.
It means you have entered something that Python does not understand, this is commonly a spelling mistake or something you have missed.
NOTE: Always pay attention to the error message, it is telling you what is wrong with your code!
Try running the following code:
print(hello world)
You will get the following SyntaxError
because of some missing ""
around the string.
File "<stdin>", line 1
print(hello world)
^
SyntaxError: invalid syntax
2. NameError
A NameError
occurs when a local or global name is not found. This refers to variables, functions, and other things like modules and classes.
Basically, Python reserves particular words such as print
.
Try running the following code:
printf("Hello World!")
You will get the following NameError
because we have misspelled print
.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'printf' is not defined
3. TypeError
A TypeError
occurs when the data types of objects in an operation are invalid. For example, trying to divide a number by a string.
Try running the following code:
100/"5"
You will get the following TypeError
because we are trying to divide /
an int
by a str
. These two types don't play together well!
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for /: 'int' and 'str'
=== TASK ===
- Copy the following code into a new Python file.
# You need to fix the following lines
# Run the code and then use the error messages to fix each line
# This line has a SyntaxError
print(hello world)
# This line has a NameError
printf("Hello World!")
# The following lines cause a TypeError
int1 = 100
str1 = "10"
print(int1 / str1)
- You will see a
SyntaxError
on line 5 because of some missing""
around the string.
print(hello world)
Fix this so that it prints out hello world
.
- Once you have completed this run the code. You will see a
NameError
on line 8 becauseprintf
is not a valid name.
printf("Hello World!")
Fix this so that it prints out Hello World
.
- Once you have completed this run the code. You will see a
TypeError
on line 13 because we are tring to divide/
anint
by astring
.
int1 = 100
int2 = "10"
print(int1 / int2)
Fix this so that it prints out 10.0
.
References
Indentation, WhiteSpace, and Blank Lines
Python is unusual in that it requires indentation for lines of code. This will make more sense when we do if
statements, while
loops, and for
loops.
For now, all you need to know is:
- Do not put whitespace (spacing or tabs) in front of a line of code.
- Python doesn't care about blank lines.
=== TASK ===
Copy the following code into a new Python file.
# This line is not indented properly. Fix this
print("Indentation matters in Python")
# You will notice that we have blank lines
# Python doesn't care about these!
# There is extra whitespace inside the print statement
# Python doesn't care about this
print("Hello World!" )
Run the code, and you will see an IndentationError
exception on line 2.
This is due to the whitespace at the beginning of the line.
Fix this.
NOTE: You may also see that the code has a red squiggly line. This is highlighting an error!
The Hello User Program
This is a mini-program and is presented to help you understand a concept. This is a very simple program that asks the user to input their name and their age.
It then says hello back to the user.
You will need to create a new file and paste this in to have a play.
name = input("Hello, what is your name?\n")
age = input("What is your age?\n")
print(f"Hello {name}, your age is {age}.")
Key Takeaways
We see 3 things here that are of interest:
- The
input()
function. This asks the user for some input via the terminal. - We assign (
=
) the input to a variable (name
andage
) - We print back out to the user using an f-string.
The next lesson on Simple I/O (Input/Output) will explain these in more detail.
Simple I/O (Input/Output)
For most of this course, we will work with the terminal. For your programs to be of more use, we need to have a way for the user to interact with them.
1. User Input
You can ask the user for input via the terminal using the input()
function.
For example,
input("Please enter something:\n")
will print out Please enter something
and then a blank line (this is because of the \n
escape character in the string).
The user can then enter something. Try this in a Python file.
2. Storing User Input
The above code does not store the input that the user enters. To do this you need to assign it to a variable. Update the Python file as follows:
input_string = input("Please enter something:\n")
print(f"You entered - {input_string}")
Now the input is stored in the variable input_string
and we can use it in our program.
You can think of input_string
as a box that stores the input from the user. We can then get the contents of the box at different points of our program.
The line,
print(f"You entered - {input_string}")
uses a Python f-string, which just lets you put the contents of the variable into the string. Here we put the contents of input_string
in between the curly braces {}
.
We will discuss variables and f-strings a lot more in the next unit.
3. Casting the Input
Whenever you get input from the user, it will be of type str
. Sometimes we wish to convert this to a number or other type. To do this we can cast the variable to another type. We can cast to an int
using the int()
function.
input_string = input("Please enter a number:\n")
x = int(input_string)
print(f"{x} + 5 = {x+5}")
The above code asks for a number, then casts the str
to an int
and then prints the result of adding 5
to the number.
Note that the line print(f"{x} + 5 = {x+5}")
places the contents of x
into the curly braces.
What happens if you don't enter a number? Copy the code into a Python file and have a play with this.
NOTE: We could have done the casting in one line.
x = int(input("Please enter a number:\n"))
print(f"{x} + 5 = {x+5}")
=== TASK ===
Create a new Python file.
Create a simple program that asks the user for a number and then prints out 10 times that number.
If the user enters 3
the program should work as follows:
Please enter a whole number:
3
3x10 = 30
If the user enters 10
the program should work as follows:
Please enter a whole number:
10
10x10 = 100
I have seen students try to solve this with an if
statement (we haven't even introduced this yet!). Please don't do this, your program should handle any number and print out the result of multiplying it by 10
. The two examples above are just two particular cases.
HINT: You will need to cast the input to an int
.
Unit 2 - Basic Elements of Python
This unit looks at the basic elements of Python. It is really important that you get a good handle on these elements. That does not mean you have to memorise them, but you should understand them.
Programmers regularly switch languages and look up things. Your secret programming weapon is Google! You will find almost unlimited resources on Python, but knowing the right words to use in a search will certainly help you.
The lessons in this unit are:
- Variables and Assignment
- Statements vs Expressions
- Numbers
- Strings
- Casting
- String Indexing and Slicing
- String Methods
Variables and Assignment
Variables are one of the most important things in programming languages. They allow us to assign a name to an object so we can store it for use later on in our program.
1. Assignment
We assign an object to a variable using the assignment operator =
. For example,
pi = 3.14
assigns 3.14
to the variable named pi
.
We can then use the variable in our programs. Consider the following program,
pi = 3.14
radius = 6
circumference = 2 * pi * radius
print(circumference)
this will print the value of the circumference of the circle with radius 6.
The point is that the line circumference = 2 * pi * radius
now used the variables pi
and radius
to calculate the value of the circumference
.
The following image illustrates the binding of the variables to the objects in memory. Note it isn't really how Python works internally, but it is good enough as a mental picture.
2. Reassignment
Python lets you reassign a variable to another object.
The following code,
pi = 3.14
radius = 6
circumference = 2 * pi * radius
print(f"The circle with radius = {radius} has circumference = {circumference}")
radius = 0
print(f"The circle with radius = {radius} has circumference = {circumference}")
will output,
The circle with radius = 6 has circumference = 37.68
The circle with radius = 0 has circumference = 37.68
because we reassigned the variable radius
to the value 0
, but we did not recalculate the circumference
.
The following depicts the reassignment of the variable radius
.
Image reproduced from Chapter 2: Introduction to Computation and Programming Using Python.
3. Objects in Memory
Python stores objects in memory and when you assign a variable it binds that variable to the location of the object in memory.
You can find the unique id of an object by using the id()
function. This refers to the object's memory address and will be different each time you run the program.
Input the following two lines into the terminal, remember to press enter after each line:
pi = 3.14
id(pi)
For now the id()
function is a bit of a novelty. We will see later on that it can prove essential in debugging programs when we are editing mutable (changeable) types like lists.
Note that two objects can have the same id if one object has been removed from memory (Python automatically cleans up objects that are no longer used). Essentially the unique id is being re-used.
=== TASK ===
This should be relatively simple, consider it a freebie.
Copy the following code into a new Python file.
pi = 3.14
radius = 6
circumference = 2 * pi * radius
print(f"The circle with radius = {radius} has circumference = {circumference}")
# Reassign radius to the value 15
# Reassign circumference to the circumference of the circule with radius 15
# In addition to the existing print statement, print out the new circumference.
- Reassign
radius
to the value15
- Reassign
circumference
to the circumference of the circule with radius15
- In addition to the existing
print
statement, print out the new circumference.
The output of the program should be:
The circle with radius = 6 has circumference = 37.68
The circle with radius = 15 has circumference = 94.2
Statements vs Expressions
So far you have already seen examples of both statements and expressions and your Python programs will be made up of both of these.
We will give a basic definition of the two, however, the real story is much more complicated.
1. Statements
Statements instruct Python to do something, that is the Python interpreter can execute the statement. To date, you have seen two types of statements: print and assignment.
For example,
print("Hello World!")
is a statement that instructs python to print the string "Hello World!"
and,
int1 = 1
is a statement that instructs Python to assign 1
to the variable int1
.
2. Expressions
Expressions differ from statements in that they evaluate to an object. They are a combination of objects and operators.
For example,
3 + 5
combines the integers (objects of type int
) 3
and 5
with the operator +
and evaluates to the integer 8
.
This example,
True and False
combines the boolean (objects of type bool
) True
and False
with the logical operator and
and evaluates to the boolean False
.
3. Combining Statements and Expressions
Most of your code will tend to be a statement made up of expressions.
For example, you can assign the result of an expression to a variable. Consider the following statement (assignment)
a = 3 + 5
Here 3 + 5
is an expression and the result, 8
, is assigned to the variable a
. The whole line is a statement that contains an expression.
=== TASK ===
A nice simple one.
Create a new Python file.
Print Statements instruct Python to do something
Print Expressions combine objects and operators and evaluate to an object
Print Statements can include expressions
Numbers
Python has three numeric types:
int
float
complex
Unless you are doing something specifically mathematical you will only use int
and float
. So we will cover just these in this lesson.
1. ints and floats
The following table provides a summary of int
and float
.
Data Type | Description |
---|---|
int | An integer. There is no upper or lower limit to how high or low it can be (Python 3). |
float | A decimal number. A double-precision floating-point number, equivalent to double in many other programming languages. |
1.1 int
An int
(integer) is simply a whole number such as 5
or -100
. If you assign a whole number to a variable, python will automatically know that the type of object is an int
.
Type the following two lines in the terminal:
a = 10
type(a)
You will see that the output confirms that a
is of type int
.
1.2 float
An float
(floating point number) is a number containing one or more decimals, such as 5.3
or -100.43
. If you assign a decimal number to a variable, python will automatically know that the type of object is an float
.
Type the following two lines in the terminal:
a = 10.6
type(a)
You will see that the output confirms that a
is of type float
.
You can find out the minimum and maximum float using
import sys
print(sys.float_info.min)
print(sys.float_info.max)
On the Repl.it systems it is
-2.2250738585072014e+308
to 1.7976931348623157e+308
. Which is approximately -2.23*(10**308)
to 1.80*(10**308)
That is astronomically big!
1.3 Dynamic Typing
You can combine an int
and a float
. Python will internally understand to convert the int
to a float
so that you can combine two floats.
For example, type the following into the terminal,
a = 1 + 3.3
print(a)
type(a)
the first line is adding an int
and a float
which results in an object of type float
with the value 4.3
.
2. Arithmetic Operators
Number objects can be combined with the following arithmetic operators.
Operator | Name | Example |
---|---|---|
+ | Addition | x + y |
- | Subtraction | x - y |
* | Multiplication | x * y |
/ | Division | x / y |
% | Modulus | x % y |
** | Exponentiation | x ** y |
// | Floor Division | x // y |
All of +
, -
, *
and /
will be familiar to you. However, %
, **
and //
may not be.
2.0.1 Modulus (%
)
The modulus operator is very useful for testing if a whole number is divisible by another whole number. It is an implementation of modulo (clock) arithmetic in mathematics.
If a number is divisible by another the output of x % y
will be 0
(x
and y
can be positive or negative).
10 % 3 # output is 1 (not divisible)
10 % 2 # output is 0 (divisible)
14 % 4 # output is 2 (not divisible)
14 % 7 # output is 0 (divisible)
You will see this in computational mathematics, for more info look at Modulus Operator - Real Python
It can also be used to find the remainder, but be careful, this doesn't work in all circumstances. I would always use math.remainder()
.
import math # imports extra maths stuff
print(math.remainder(10, 3)) # remainder of 10/3 is 1
print(10 % 3) # output is 1. (% WORKS)
print(math.remainder(-10, 3)) # remainder of 10/3 is 1
print(-10 % 3) # output is 2. (% DOES NOT WORK)
print(math.remainder(10, -3)) # remainder of 10/3 is -1
print(10 % -3) # output is -2. (% DOES NOT WORK)
print(math.remainder(-10, -3)) # remainder of 10/3 is -1
print(-10 % -3) # output is -1. (% WORKS)
So it works when both numbers are positive or both numbers are negative.
My advice is don't use it for the remainder.
2.0.2 Exponentiation (**
)
The exponentiation operator **
takes the number x
(called the base) to the power of y
(called the exponent).
2**3 # Evaluates to 8 (i.e. 2*2*2)
2**4 # Evaluates to 16 (i.e. 2*2*2*2)
5**2 # Evaluates to 25 (i.e. 5*5)
5**3 # Evaluates to 125 (i.e. 5*5*5)
2.0.3 Floor Division (//
)
The floor division operator //
takes the number x/y
and rounds it down.
For example,
10/4
is 2.5
rounded down is 2
.
So,
10//4 # Evaluates to 2
-10/4
is -2.5
rounded down is -3
.
So,
-10//4 #Evaluates to -3
2.1 Order of Operations
When using arithmetic operators you need to be aware of the order in which Python evaluates an operator. This is known as the operator precedence.
For example,
3 + 5 * 2
is 16
right? If you enter this into the terminal you will get 13
. Well done if you spotted this.
This is because the multiplication *
operator has higher precedence than the addition +
operator. Python is doing the following
# This is not code, we are manually evaluating to see how Python works with this expression
3 + 5 * 2 # (Evaluate 5 * 2)
3 + 10 # (Evaluate 3 + 10)
13 # (Final Object)
If you want to force the 3 + 5
to be evaluated first, then you need to use parentheses.
(3 + 5) * 2
This now evaluates to 16
. Try it in the terminal.
The following table gives the arithmetic operator precedence. Higher entries have higher precedence.
Operator | Name |
---|---|
() | Parentheses |
** | Exponentiation |
* , / , % , // | Multiplication, Division, Modulus, Floor Division |
+ , - | Addition, Subtraction |
2.2 Left-to-right Evaluation
You will notice from the table that some operators have the same precedence as each.
What happens then with the following:
5 - 2 + 1
If you try this in the terminal you will get the answer 4
. However, this could have been read as either 3 + 1
or 5 - 3
depending on whether you evaluated the -
or +
first.
Python follows the left-to-right convention. That is, if two operators are of the same precedence, then Python will evaluate the leftmost first. Hence:
# This is not code, we are manually evaluating to see how Python works with this expression
5 - 2 + 1 # (Evaluate 5 - 2)
3 + 1 # (Evaluate 3 + 1)
4 # (Final Object)
3. Comparison Operators
You can also compare two numbers and they will result in a bool
object. Either True
or False
.
Operator | Name | Example |
---|---|---|
== | Equal | x == y |
!= | Not equal | x != y |
> | Greater than | x > y |
< | Less than | x < y |
>= | Greater than or equal to | x >= y |
<= | Less than or equal to | x <= y |
For example, the following expressions evaluate to:
Expression | Result |
---|---|
3 < 5 | True |
3 > 3 | False |
3 >= 3 | True |
3 == 5 | False |
3 != 5 | True |
3.1 Order of Precedence
All the comparison operators given above have lower precedence than the arithmetic operators.
Operator | Name |
---|---|
() | Parentheses |
** | Exponentiation |
* , / , % , // | Multiplication, Division, Modulus, Floor Division |
+ , - | Addition, Subtraction |
== , != , < , > , <= , >= | Comparison Operators |
Therefore something like the following expression,
3 + 5 == 3 * 5
is evaluated as follows:
# This is not code, we are manually evaluating to see how Python works with this expression
3 + 5 == 3 * 5 # Evaluate 3 + 5
8 == 3 * 5 # Evaluate 3 * 5
8 == 15 # Evaluate 8 == 15
False
Note: I would always want to convey my intention and not rely on the order of precedence, it is easy to forget. So I would rewrite the above as:
(3 + 5) == (3 * 5)
This is identical, but it tells the reader more explicitly that the stuff in the parentheses is evaluated first.
=== TASK ===
Copy the following code into a new Python file.
# DO NOT TOUCH THE FOLLOWING LINES 4, 5 and 6
# THESE ARE USED FOR THE INPUT.
a = int(input("Please enter a number: ")) # leave this line alone
b = int(input("Please enter a number: ")) # leave this line alone
c = int(input("Please enter a number: ")) # leave this line alone
# TASK 1.
# print out the type of 10.3 + 5
# TASK 2.
# print(a + b * c) # Correct this so that the ``+`` is evaluated first
# print(a**b+c) # Correct this so that the exponent is b+c
# TASK 3.
# print the output of a < b
# TASK 4.
# print the output of (a < b) == (a <= b)
This reads in three whole numbers and stores them in variables a
, b
and c
. Try running the code and it will ask you for three numbers.
-
Print the type of
10.3 + 5
-
Fix the following two lines:
print(a + b * c) # Correct this so that the ``+`` is evaluated first
print(a**b+c) # Correct this so that the exponent is b+c
-
Print the result of
a < b
-
Print the result of
(a < b) == (a <= b)
If you enter 2
, 3
and 2
into your program then a=2
, b=3
and c=2
. If you have made the above changes correctly, your program should output:
<class 'float'>
10
32
True
True
References
Precedence and Associativity of Operators in Python
Strings
This lesson introduces you to the basics of strings in Python.
Python has a built-in datatype str
known as a string, you can think of this as a piece of text.
1. Basic Printing of Strings
Strings can be printed to the terminal using the python print()
function (we will explore functions in detail later in the course).
For example:
print("hello world!")
Prints hello world!
to the terminal.
Whatever is contained between the brackets will be printed out to the terminal using double quotes ""
.
2. Quotes
In Python strings can be enclosed in either single quotes '...'
or double quotes "..."
For example:
print('hello world!')
Also prints hello world!
to the terminal as per section 1.
NOTE: Although we can use both, please stick to double quotes as most languages represent strings with double quotes!
3. Escape Characters
My first program was "Hello World!" in Python
Try the following in the terminal.
print("My first program was "Hello World!" in Python")
You will see a SyntaxError: invalid syntax
, this is the Python interpreter telling you that it does not understand the code you have entered. If you look closely you will see that it is pointing to the H
, the character after the second ""
. This is because it is illegal to put a double quote character "
within a string.
To fix this we need to use an escape character, in Python, this is the \
(backslash) followed by a particular character.
Amend the previous line of code to include backslashes for the quotes in the string.
print("My first program was \"Hello World!\" in Python")
If you run this you will now get the desired output My first program was "Hello World!" in Python
.
There are a number of special escape characters in Python, you can look them up. We will just look at the following:
Name | Escape Character |
---|---|
tab | \t |
new line | \n |
backslash | \\ |
single quotation mark | \' (You only need to use this when the string in enclosed is single quotation marks.) |
double quotation mark | \" (You only need to use this when the string in enclosed is double quotation marks.) |
For example,
print("My first program was \"Hello World!\" in Python\n\nPython is cool!")
prints out :
My first program was "Hello World!" in Python
Python is cool!
4. Multiline Strings
Multiline strings let us write multiple lines in a string without escape characters or multiple print statements. In Python, they start with '''
or """
and end the same way.
numbers = '''3
5
6
7
'''
print(numbers)
The above will print out the following to the terminal.
3
5
6
7
If you have an escape character in your multiline string and you want this to appear in the terminal you should escape it with another \
.
For example,
print('''This is a multiline string.
The tab escape character in python is \t.
''')
will result in the output:
This is a multiline string.
print('''This is a multiline string.
The tab escape character in python is .
''')
i.e. it will put a tab into the output. To get \t
to show up you need to escape it with another \
as follows:
print('''This is a multiline string.
The tab escape character in python is \\t.
''')
NOTE: This is the same in a normal string. To output an escape character as text you should escape it!
5. Concatenating Strings
To concatenate, or combine, strings you can use the +
operator.
For example,
str1 = "Hello"
str2 = "World!"
print(str1 + str2)
results in the output:
HelloWorld!
To get the standard Hello World!
we could do the following.
str1 = "Hello"
str2 = "World!"
print(str1 + " " + str2)
Note that you can also just concatenate strings within the print function:
print("Hello" + " " + "World")
6. Formatting Strings
We can also combine strings using the format()
method.
The format()
method takes the passed arguments and puts them in the string where the placeholders {}
are:
For example,
str1 = "string 1"
str2 = "string 2"
print("Hello {} and hello {}.".format(str1, str2))
results in the output:
Hello string 1 and hello string 2.
We can also achieve this using index numbers starting at {0}
str1 = "string 1"
str2 = "string 2"
print("Hello {0} and hello {1}.".format(str1, str2))
This is especially useful if you wish to repeat strings stored in variables.
For example,
str1 = "string 1"
str2 = "string 2"
print("Hello {0} and hello {1}.\nBye {0} and bye {1}.".format(str1, str2))
results in the output:
Hello string 1 and hello string 2.
Bye string 1 and bye string 2.
7. f-Strings
Since Python 3.6 you can use f-Strings (formatted string literals), we will use these throughout the course, but you should understand how to use the others in case someone else has written code using them.
We can achieve the previous output by doing the following:
str1 = "string 1"
str2 = "string 2"
print(f"Hello {str1} and hello {st2}.\nBye {str1} and bye {str2}.")
which again results in the output:
Hello string 1 and hello string 2.
Bye string 1 and bye string 2.
Note that in front of the string we put an f
. This tells python that anything in curly braces should be evaluated. So in the example above, the values of str1
and str2
are substituted into the string.
=== TASK ===
Create a new Python file.
-
Print
Programming in Python
to the terminal. -
Print
Programming in Python with single quotes
to the terminal using single quotes''
. -
Print the following to the terminal.
I know how to put "quotes" into a string.
And put in new lines!
- Using a multiline string print
I am using a multiline string to:
- print multiple lines
- without the use of the escape character \n
to the terminal.
- Create the following strings and store them in the variables
str1
andstr2
str1 = "string 1"
str2 = "string 2"
Print Hi, I am string 1 and I am string 2!
to the terminal using string concatenation.
- Print
string 1 is before string 2 and string 2 is after string 1
to the terminal using f-strings.
The entire output of the program should be as follows:
Programming in Python
Programming in Python with single quotes
I know how to put "quotes" into a string.
And put in new lines!
I am using a multiline string to:
- print multiple lines
- without the use of the escape character \n
Hi, I am string 1 and I am string 2!
string 1 is before string 2 and string 2 is after string 1
Casting
It is very common that you will need to convert one data type to another.
Type the following into the terminal:
print("The meaning of life is " + 42)
You will the following TypeError
:
TypeError: can only concatenate str (not "int") to str
This is because you are trying to add a str
and an int
. Python does not know how to do this. To correct this you need to do something called casting.
The following code casts (converts) the int
to a str
using the constructor function str()
:
print("The meaning of life is " + str(42))
If you try this you will see that it works.
Type str(42)
into the terminal on its own and you will see that it returns the str
'42'
(note it uses single quotes, but that is the same as double quotes in Python!).
Note that we could have done the following:
print(f"The meaning of life is {42}")
This is because the python f-string knows how to convert the number 42 to play nicely with the string. Try it in the terminal.
1. How to Cast
Casting in python is therefore done using constructor functions:
Function | Description |
---|---|
int() | Constructs an integer number from an integer, a float (by removing all decimals), or a string (providing the string represents a whole number) |
float() | Constructs a float number from an integer, a float, or a string (providing the string represents a float or an integer) |
str() | Constructs a string from a wide variety of data types, including strings, integers, floats, and booleans |
bool() | Constructs a boolean from a wide variety of data types, including strings, integers, floats, and booleans |
1.1 Examples
Try these in the terminal:
int(1) # Creates an int with value 1
int("2") # Creates an int with value 2
int(4.3) # Creates an int with value 4
int(True) # Creates an int with value 1
int(False) # Creates an int with value 0
float(1) # Creates a float with value 1.0
float("2") # Creates a float with value 2.0
float("3.142") # Creates a float with value 3.142
float(4.3) # Creates a float with value 4.3
float(True) # Creates a float with value 1.0
float(False) # Creates a float with value 0.0
str(1) # Creates a str with value '1'
str("3.142") # Creates a str with value '3.142'
str(4.3) # Creates a str with value '4.3'
str(True) # Creates a str with value 'True'
str(False) # Creates a str with value 'False'
bool()
can throw up some unexpected results unless you understand what it is doing.
You might think that bool("0")
would result in False
, it doesn't!
Python treats everything as True
other than False
, 0
, an empty string ""
and some other things we have yet to encounter, empty lists, dictionaries, tuples, and the None
keyword which represents no value at all (more on that later).
bool(1) # Creates a bool with value True
bool("3.142") # Creates a bool with value True
bool(4.3) # Creates a bool with value True
bool("0") # Creates a bool with value True
bool(True) # Creates a bool with value True
bool(False) # Creates a bool with value False
bool(0) # Creates a bool with value False
bool("") # Creates a bool with value False
bool([]) # Creates a bool with value False
bool({}) # Creates a bool with value False
bool(()) # Creates a bool with value False
bool(None) # Creates a bool with value False
=== TASK ===
Create a new Python file and write a program that outputs the following to the terminal for a given X and Y.
The result of multiplying X by Y is X*Y
For example, for 2.1 and 3:
Please enter a number:
2.1
Please enter another number:
3
The result of multiplying 2.1 by 3.0 is 6.3
For example, for 5.2 and 3.4:
Please enter a number:
5.2
Please enter another number:
3.4
The result of multiplying 5.2 by 3.4 is 17.68
References
The What's My Age Again (in Hours)? Program
The following program is named after the pop-punk classic by Blink 182 - What's My Age Again off the album Enema of the State.
The program asks the user for the hour, day, month, and year of their birth.
It then outputs the person's age in hours.
Here is a plan for our program.
In pseudocode:
Ask the user for the hour, day, month and, year of birth
Compute the difference between the current date and time and the user's birth
Convert the difference into hours
Print out the user's age in hours
Please try to understand how this program works and match it with the pseudocode above.
Copy and paste this code into a new file to play around with it.
1. The Complete Program
import datetime
hour = int(input("What hour (24hr) were you born?\n"))
day = int(input("What day (number) of the month were you born?\n"))
month = int(input("What number month were you born?\n"))
year = int(input("What year were you born?\n"))
# create a new datetime object with user input
birth_datetime = datetime.datetime(year, month, day, hour)
# get current datetime
current_datetime = datetime.datetime.now()
# compute the difference
diff = current_datetime - birth_datetime
# convert days (*24) and seconds (/3600) to hours and sum
hours = diff.days*24 + diff.seconds/3600
print(f"Your age in hours is {round(hours)}.")
How would you estimate that the output is actually correct?
You should try and do a calculation to convince yourself that this works properly.
String Methods
Python has a number of built-in methods that you can use on strings to do simple transformations.
You can see a comprehensive list via the link in the References.
1. What is a Method?
A method is something you can call on an object that does something with that object. This will make more sense when we get to object-orientation later in the course.
For now, if we have a string we can call a method using the .
notation and we will get back a new string.
For example, type the following into the terminal,
"hello world".upper()
returns the new string:
"HELLO WORLD"
Here we called the upper()
method on the str
object "hello world"
and it gave us back the str
"HELLO WORLD"
.
1.1 Be Careful
String methods return new strings, consider the following code:
a = "hello world" # (assign "hello world" to variable a)
a.upper() # (call upper() on variable a)
print(a) # (print a)
will output
hello world
This is because it returns a new string, it does NOT change the original string. So a
is left as "hello world"
.
We would either have to do the following to print out the upper case version,
a = "hello world" # (assign "hello world" to variable a)
print(a.upper()) # (print out the result of calling upper() on variable a)
or,
a = "hello world" # (assign "hello world" to variable a)
b = a.upper() # (call upper() on variable a and assign result to variable b)
print(b) # (print b)
1.2 Familiarise Yourself
Take a look at the following link - W3Schools - Python String Methods and try some of these out. You will need some of them for the TASK.
=== TASK ===
Copy the following into a new Python file.
sentence = input("Please enter a sentence:\n")
print(sentence)
Amend the code so that the inputted sentence is then printed out as
- upper case
- lower case
- first character of each word is upper case
For example,
Please enter a sentence:
The quick brown fox jumps over the lazy dog
THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG
the quick brown fox jumps over the lazy dog
The Quick Brown Fox Jumps Over The Lazy Dog
References
W3Schools - Python String Methods
String Indexing and Slicing
A string is a sequence of characters representing Unicode characters.
For example the string "Helloworld"
can be thought of like this.
Unlike many programming languages, Python does not have a character type and a character is just a string (str
) of length 1
.
1. String Indexing
You can access elements of the string using square brackets []
and the index of the position you wish to access.
Note indexing starts at 0
. So the first character has an index of 0
.
For example,
str1 = "String Indexing"
print(str1[0]) # prints the first character "S"
print(str1[4]) # prints the 5th character "n"
1.1 IndexError
If you try the following:
str1 = "String Indexing"
print(str1[15]) # IndexError
You will get an exception as there is no 16th character (index 15 tries to access the 16th character).
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: string index out of range
1.2 Negative Indexing
We can also access the characters of the string from the end.
For example,
str1 = "String Indexing"
print(str1[-1]) # prints the last character "g"
print(str1[-2]) # prints the second to last character "n"
1.3 Length of a String
The len()
function will return the length of the string.
str1 = "String Indexing"
print(len("String Indexing")) # prints 15
2. String Slicing
We can easily get substrings from a given string by using string slicing.
For example, we can get "Index"
from the string "String Indexing"
.
The following gets the characters from position 7 to position 11 (12 not included)
str1 = "String Indexing"
print(str1[7:12]) # prints Index
Basically you specify the start index and the end index (not included), separated by a colon, to return a part of the string.
2.1 Start to a Given Position
You can get all characters from the start to a given position by:
str1 = "String Indexing"
print(str1[0:6]) # prints String
## OR
print(str1[:6]) # prints String
The second way is very common and is shorthand for the start index 0
.
2.2 A Given Position to the End
You can get all characters from a given position to the end of the string using.
str1 = "String Indexing"
print(str1[4:15]) # prints ng Indexing
## OR
print(str1[4:]) # prints ng Indexing
The second way is quite common and it means we don't need to know the length of the string!
Otherwise, in general you would need to do the following
str1 = "String Indexing"
print(str1[4:len(str1)]) # same as str1[4:15] or str1[4:]
2.3 Negative Slicing
You can also slice using negative indexing.
str1 = "String Indexing"
print(str1[-3:]) # prints out the last 3 characters - "ing"
print(str1[-4:-2]) # prints out character 11 and 12. Same as str1[11:13]
=== TASK===
Copy the following into a new Python file.
# DO NOT EDIT THESE TWO LINES
firstname = input("Please enter a first name: \n") # leave this alone!
surname = input("Please enter a surname: \n") # leave this alone!
# -------------------------------------------------------
# Edit the following line so that it prints out the first character of the first name
print(f"The first character of the first name is {firstname}")
# Edit the following line so that it prints out the last character of the surname (negative indexing)
print(f"The last character of the surname is {surname}")
# Edit the following line so that it prints the initials of the person. e.g. Mary Smith would result in M.S
print(f"The person's initials are {firstname}.{surname}")
# Edit the following line so that it prints the first 3 characters of the first name. For example Mary would print out Mar
print(f"The first 3 characters of the first name are {firstname}")
# Edit the following line so that it prints the last 4 characters of the surname
print(f"The last 4 characters of the surname are {surname}")
- Edit the following line so that it prints out the first character of the first name
print(f"The first character of the first name is {firstname}")
- Edit the following line so that it prints out the last character of the surname (negative indexing)
print(f"The last character of the surname is {surname}")
- Edit the following line so that it prints the initials of the person. e.g. Mary Smith would result in M.S
print(f"The person's initials are {firstname}.{surname}")
- Edit the following line so that it prints the first 3 characters of the first name. For example, Mary would print out Mar
print(f"The first 3 characters of the first name are {firstname}")
- Edit the following line so that it prints the last 4 characters of the surname (note we assume for simplicity that the surname contains at least 4 characters, what happens if it is less than 4?)
print(f"The last 4 characters of the surname are {surname}")
An example of the correct program for the input Mary Smith
is given below.
Please enter a first name:
Mary
Please enter a surname:
Smith
The first character of the first name is M
The last character of the surname is h
The person's initials are M.S
The first 3 characters of the first name are Mar
The last 4 characters of the surname are mith
Unit 3 - Branching and Decisions
So far in this course, we have written straight-line programs. These execute one statement after another.
These aren't particularly interesting programs.
Branching programs provide more complexity to our programs and make them more interesting.
Generally, we will look to use a conditional statement which has three parts:
- boolean test - an expression that evaluates to
True
orFalse
True
block - a block of code that is executed if the test evaluates toTrue
False
block - a block of code that is executed if the test evaluates toFalse
The following image demonstrates the program flow of a basic branching statement.
You can see that the code enters the conditional statement (dotted box) and encounters the Test, based on whether it evaluates to True
or False
it executes either the True
block or the False
block.
Image reproduced from Chapter 2: Introduction to Computation and Programming Using Python.
If ... Else Statement
As stated in the overview of this unit. A conditional statement has three parts:
- Boolean test - an expression that evaluates to True or False
- True block - a block of code that is executed if the test evaluates to True
- False block - a block of code that is executed if the test evaluates to False
1. Structure of a conditional statement
In Python, a conditional if
statement has the following form:
if <boolean expression>:
# If the boolean expression is True, run this block of code
else:
# If the boolean expression is False, run this block of code
You can see how this mirrors the diagram from the overview of this unit, if the boolean expression is True
, execute the True
block, else it is False
, execute the False
block.
Example 1.1 - Just an if statement
The following example tests if a variable x
is less than5
and only prints out if x
is less than 5
print("code before if statement")
# store 4 in the variable x
x = 4
# Test the variable using the boolean expression x < 5?
if x < 5:
print(f"{x} is less than 5")
print("code after if statement")
The whole output of the program is as follows:
code before if statement
4 is less than 5
code after if statement
If we change x = 10
then the program will not execute the statement print(f"{x} is less than 5")
and the output will be:
code before if statement
code after if statement
Copy and paste the following program into a Python file and experiment by entering different numbers.
# get some input from the user and store as an int
input_string = input("Please enter a whole number\n")
x = int(input_string)
print("code before if statement")
if x < 5:
print(f"{x} is less than 5")
print("code after if statement")
Example 1.2 - if ... else statement
The following example tests if a variable x
is less than 5
and prints out information for both cases.
print("code before if statement")
# Test the variable using the boolean expression x < 5?
if x < 5:
print(f"{x} is less than 5")
else:
print(f"{x} is greater than or equal to 5")
print("code after if statement")
In this example x = 7
which is greater than 5
, therefore x < 5
evaluates to False
and the program prints 7 is greater than or equal to 5
(the else
block)
The whole output of the program is as follows:
code before if statement
7 is greater than or equal to 5
code after if statement
Try copying the code below into a Python file and running the program. Think about the following:
- What happens with different
int
values ofx
? - What happens if
x
is not anint
, for example of typestr
?
# get some input from the user and store as an int
input_string = input("Please enter a whole number\n")
x = int(input_string)
print("code before if statement")
# Test using the boolean expression x < 5?
if x < 5:
# run this code if boolean expression is True
print(f"{x} is less than 5")
else:
# run this code if boolean expression is False
print(f"{x} is greater than or equal to 5")
print("code after if statement")
2. Indentation
You will notice that the code is indented, this is how Python determines blocks of code. Indentation is determined by the programmer, I would stick to 2 or 4 spaces. Repl.it defaults to 2 spaces.
It can be done using a different number of spaces or the tab character, but you should be consistent in your program. Try the code without the indentation. What happens?
Example 2.1 - No indentation
print("code before if statement")
x = 4
if x < 5:
print(f"{x} is less than 5")
else:
print(f"{x} is greater than or equal to 5")
print("code after if statement")
You will see that you get the following error - IndentationError: expected an indented block
.
3. Compound Boolean Expressions
The conditional test for the if
statement can be a more complicated expression as long as it evaluates to True
or False
.
For example, the following program tests to see if a number is between 1 and 10:
Example 3.1 - Logical And
# get some input from the user and store as an int
x = int(input("Enter a whole number:\n"))
if (x > 0) and (x < 11):
print("Number is between 1 and 10")
else:
print("Number is not between 1 and 10")
Here the boolean expression (test) is (x > 0) and (x < 11)
which requires that both (x > 0)
and (x < 11)
need to be True
for the whole expression to be True
.
Example 3.2 - Logical Not
# ask the user for a number and cast it to an int
x = int(input("Enter a whole number:\n"))
if not x < 10 :
print("Number is above 10")
Here the boolean expression (test) is not x < 0
. This will first evaluate x < 0
and then take a not
of the result. e.g. if x = 11
then x < 10
is False
, therefore not x < 10
is True
.
Try out these programs and make sure you understand what they do.
=== TASK ===
You can test if a number is even or odd using the modulus operator %
.
For example, 4 % 2 = 0
evaluates to 0
because 2
divides 4
with no remainder.
However, 7 % 2 = 1
evaluates to 1
because 2
divides 7
with remainder 1
.
We can use this to evaluate if a number is odd or even. The expression x % 2 == 0
evaluates to True
if x
is even and False
if it is odd.
The expression x % 2 == 0
compares the left side x % 2
to the right side 0
to see if they are equal.
For example,
x | x % 2 | x % 2 == 0 |
---|---|---|
4 | 0 | True |
7 | 1 | False |
I suggest you try some even and odd examples out in the terminal if you don't understand this.
E.g. Try:
# test 4 to see if it is even
4 % 2 == 0 # will print out True as 4 % 2 evaluates to 0
# test 7 to see if it is even
7 % 2 == 0 # will print out False as 7 % 2 evaluates to 1
Write a program that asks a user for a number and then prints out whether it is even or odd.
Your program should work as follows:
Please enter a whole number:
7
Your number is odd!
Please enter a whole number:
4
Your number is even!
Note that to pass the tests you must have exactly the output above, apart from the numbers which will differ depending on what the user inputs.
MP: The Really Rubbish Password Program
This is an example mini-program to demonstrate the use of an if
... else
statement.
The aim is to ask the user for a password and if the password matches the secret password then the user gets into the system, if not the user is denied access.
Feel free to play around with the program as much as you like.
Planning our program in pseudocode.
Set the secret password
Ask the user to enter their password
if the input matches the secret password
grant access
else
do not grant access
1. The Complete Program
The complete program is given below and then explained in the subsequent sections.
# The Really Rubbish Password Program
# This is the stored password for the user
secret_password = "secret"
print("Welcome to NOSA Inc.")
print("Did you know that the Moon is an average of 238,855 miles away from Earth\n")
password = input("Please enter your password:\n")
if password == "secret":
print("\nAccess Granted!")
else:
print("\nAccess Denied!")
input("\n\nPress the enter key to exit.")
2. Breaking Down the Program
The following section explains the two main parts of the program.
2.1 Getting the User Input.
The first part of the code prompts the user for their password and stores it in the variable password
.
# The Really Rubbish Password Program
# This is the stored password for the user
secret_password = "secret"
print("Welcome to NOSA Inc.")
print("Did you know that the Moon is an average of 238,855 miles away from Earth\n")
password = input("Please enter your password:\n")
2.2 Testing the Password
The second part of the code then uses the variable password
to check if the password is correct. If the condition password == "secret"
is True
then access is granted otherwise (else
) access is denied.
if password == "secret":
print("\nAccess Granted!")
else:
print("\nAccess Denied!")
input("\n\nPress the enter key to exit.")
References
Dawson, M. (2010). Python programming for the absolute beginner, third edition (3rd ed.). Delmar Cengage Learning.
Nested if Statements
It is possible to nest if statements within other if statements. This can be useful for testing multiple conditions but allowing us to run code for each conditional.
1. Nested if Statement
We can nest if statements as follows:
1.1 Example - Nested if Statement
x = 101
if x > 100:
print("Above 100, ", end="")
if x > 150:
print("and also above 150!")
else:
print("but not above 150!")
As x = 101
the first program will first execute the statement print("Above 100, ", end="")
and then execute the statement print("but not above 150!")
. Try different values of x by pasting the code above into a Python file.
If we compare this with:
x = 101
if (x > 100) and (x > 150):
print("Above 100, and also above 150!")
The second program cannot do this and only prints out "Above 100, and also above 150!
if x
is greater than 150
.
If x = 99
or x = 130
the program will print nothing.
You should make sure you understand the program flow. The two program flows are depicted in the diagram below.
2. Indentation
You should note that in the example given in 1.1, the blocks of code are given by indentation. If we just examine the structure of the example it looks as follows:
# main block of code, anything aligned with this is in this block
if x > 100:
# True block of code
print("Above ten,")
if x > 150:
# True block of code
print("and also above 150!")
else:
# False block of code
print("but not above 150!")
This is also illustrated by the diagram below. You should note that the indentation defines each of the blocks of code.
=== TASK ===
Using nested if statements, write a program that asks the user for a whole number. Your program should do the following:
- If the number is divisible by 3 and 5 it should print out
Your number is divisible by 3 and 5.
- If the number is divisible by 3 and NOT 5 it should print out
Your number is divisible by 3 and NOT by 5.
- If the number is NOT divisible by and by 5 it should print out
Your number is NOT divisible by 3 and is divisible by 5.
- If the number is NOT divisible by and by NOT 5 it should print out
Your number is NOT divisible by 3 and 5.
Your program should match the examples below. I have given examples for each output.
Please enter a number:
15
Your number is divisible by 3 and 5.
Please enter a number:
12
Your number is divisible by 3 and NOT by 5.
Please enter a number:
20
Your number is NOT divisible by 3 and is divisible by 5.
Please enter a number:
22
Your number is NOT divisible by 3 and 5.
HINT: We learned how you can test if a number is divisible by 2 in the Lesson: If ... Else Statement earlier in this unit.
Elif Statement
Sometimes we may wish to use an alternative test should our previous test evaluate as False
.
elif
which is short for else if, lets us do exactly that.
1. Using the Elif Statement
We can test another condition after the first condition as follows:
1.1 Example - if ... elif
Remember from Nested If Statements that we can do the following:
# change these to experiment with the if..elif block
x = 4
y = 5
if x < y:
# block of code
print("x is less than y")
elif x > y:
# block of code
print("x is greater than y")
Here depending on the values of x
and y
one (and only one) of the print
statements is executed.
If x
is less than y
then x < y
is True
and the first block is executed.
If x
is greater than y
then x < y
is False
and the elif
part is checked. As x > y
is True
, the second block is executed.
What about if x
and y
are equal?
Copy the code above into a Python file and experiment with different values of x
and y
.
1.2 Example - if ... elif ..else
We can also include an else
statement, now our program will output when x
and y
are equal.
# change these to experiment with the if..elif..else block
x = 4
y = 5
if x < y:
# block of code
print("x is less than y")
elif x > y:
# block of code
print("x is greater than y")
else:
# block of code
print("x is equal to y")
You should think about this and realise that there are three possibilities.
x
is less thany
x
is greater thany
x
is equal toy
If the first two are False
, then it must be that x
is equal to y
. We don't need a test, we can just the else
statement.
1.3 The Connection with if ... else
The program from the last section:
# change these to experiment with the if..elif..else block
x = 4
y = 5
if x < y:
# block of code
print("x is less than y")
elif x > y:
# block of code
print("x is greater than y")
else:
# block of code
print("x is equal to y")
Can be rewritten in terms of just if
and else
statements:
# change these to experiment with the if..elif..else block
x = 4
y = 5
if x < y:
# block of code
print("x is less than y")
else: # You can see from these two lines where elif gets it's name, it is doing the same as else if!
if x > y:
# block of code
print("x is greater than y")
else:
# block of code
print("x is equal to y")
1.4 Example - Multiple elif statements
Another example is testing to see if the first letter of someone's name begins with a vowel.
input_name = input("Please enter your name:\n")
# convert the name to lowercase
name = input_name.lower()
if name[0] == "a":
print("The name begins with an a")
elif name[0] == "e":
print("The name begins with an e")
elif name[0] == "i":
print("The name begins with an i")
elif name[0] == "o":
print("The name begins with an o")
elif name[0] == "u":
print("The name begins with an u")
else:
print("The name does not begin with a vowel")
I suggest trying each of these programs out in Python.
Note that this is not the most efficient way to do this, we can either use the newer match statement available since Python 3.10 (version). We could also do a similar thing using lists
=== TASK ===
Write a program that outputs whether a number is positive, negative, or zero. Your program should accept numbers of type float
.
- If the number is positive it should output
Your number is positive!
- If the number is negative it should output
Your number is negative!
- If the number is zero it should output
Your number is zero!
For example, your program should output the following given these inputs:
Please enter a number:
2.3
Your number is positive!
Please enter a number:
-3.3
Your number is negative!
Please enter a number:
0
Your number is zero!
The Mood Face Program
This is an example mini-program to demonstrate the use of an elif
statement.
The program generates a random number between 1 and 3 and then prints out a given mood face.
Work through the three sections.
1. The Random Mood Program
# Mood Computer
# Demonstrates the elif clause
import random
print("Right now I am feeling...")
mood = random.randint(1, 3)
if mood == 1:
# happy
print( \
"""
-----------
| |
| O O |
| < |
| |
| . . |
| `...` |
-----------
""")
elif mood == 2:
# neutral
print( \
"""
-----------
| |
| O O |
| < |
| |
| ------- |
| |
-----------
""")
elif mood == 3:
# sad
print( \
"""
-----------
| |
| O O |
| < |
| |
| .'. |
| ' ' |
-----------
""")
input("\n\nPress the enter key to exit.")
You'll note that because the multiline strings do not have indentation, the lines,
print( \
"""
-----------
| |
| O O |
| < |
| |
| . . |
| `...` |
-----------
""")
are actually really one line of code, but the multiline string is split across the 10 lines.
2. Refactoring the Program
If you look carefully you will notice that all the faces have the same first 5 lines. and the same last line.
-----------
| |
| O O |
| < |
| |
-----------
Therefore we can just print this out once and then print out the bottom 3 lines depending on the mood.
# Mood Computer
# Demonstrates the elif clause
import random
print("Right now I am feeling...")
# print the first five lines of the face
print("""-----------
| |
| O O |
| < |
| |""")
mood = random.randint(1, 3)
# use elif to print mood lines of the face
if mood == 1:
# happy
print("| . . |")
print("| `...` |")
elif mood == 2:
# neutral
print("| ------- |")
print("| |")
elif mood == 3:
# sad
print("| .'. |")
print("| ' ' |")
# print the bottom line of the face
print("-----------")
input("\n\nPress the enter key to exit.")
This is exactly the same program but slightly shorter.
3. Accept User Input
We make one final change to the program to ask the user for a number between 1
and 3
instead of randomly generating the number.
# Mood Computer
# Demonstrates the elif clause
mood = int(input("Please enter a number between 1 and 3:\n"))
# use elif to print mood lines of the face
if (mood < 1) or (mood > 3):
print("Illegal mood value!")
else:
print("Right now I am feeling...\n")
# print the first five lines of the face
print("""-----------
| |
| O O |
| < |
| |""")
if mood == 1:
# happy
print("| . . |")
print("| `...` |")
elif mood == 2:
# neutral
print("| ------- |")
print("| |")
elif mood == 3:
# sad
print("| .'. |")
print("| ' ' |")
# print the bottom line of the face
print("-----------")
input("\n\nPress the enter key to exit.")
Note now if you enter a number that isn't 1,2
or 3
you will get back,
Illegal mood value!
References
Dawson, M. (2010). Python programming for the absolute beginner, third edition (3rd ed.). Delmar Cengage Learning.
Booleans
This lesson introduces you to the built-in data type bool
. Booleans are essential in computer science and take on either a value of True
or False
.
True
and False
are keywords in Python and are used to represent their respective boolean values. You can assign them to variables, for example:
my_bool = True
type(my_bool)
The above code assigns the value True
to my_bool
and then checks the type. Try this in the terminal and you will see that my_bool
is now an object of type <class 'bool'>
.
1. Boolean Expressions
You can compare two objects of the same type using the comparison operators listed in the table below. These expressions will return either True
or False
and are known as boolean expressions.
1.1 Comparison Operators
Operator | Name | Example |
---|---|---|
== | Equal | x == y |
!= | Not equal | x != y |
> | Greater than | x > y |
< | Less than | x < y |
>= | Greater than or equal to | x >= y |
<= | Less than or equal to | x <= y |
1.2 Comparing Numbers
For example, the following expressions compare int
objects and evaluate to:
Expression | Result |
---|---|
3 < 5 | True |
3 > 3 | False |
6 >= 6 | True |
3 == 5 | False |
3 != 5 | True |
1.3 Comparing Strings
Interestingly you can also compare other objects such as str
. The following,
"held" < "helm"
evaluates to True
and,
"help" < "helm"
evaluates to False
.
Python knows how to compare the order of strings! It looks at each character in turn and compares their order in the alphabet. Try experimenting. For example, what do the following two expressions return?
"hel" < "helm"
"hello" < "helm"
Try to reason about the answers that you get.
1.4 Comparing Booleans
Believe it or not, you can also compare the order of Booleans.
True < False # (Evaluates to False)
False < True # (Evaluates to True)
This is because Python also represents True
as the bit value 1
and False
as the bit value 0
. Now the above should make sense!
1.5 Comparing Different Types
Do you know how to order the str
"hello"
and the int
10
? I don't and neither does Python.
"hello" < 10
results in the following exception:
TypeError: '<' not supported between instances of 'str' and 'int'
Two objects you can mix are numbers (int
and float
),
4 < 5.6 # (Evaluates to True)
and numbers and booleans (bool
),
4 < True # (Evaluates to False)
because True
is also represented by 1
.
2. Logical Operators
Boolean expressions can be combined with logical operators to create larger boolean expressions:
Operator | Description | Example |
---|---|---|
and | Returns True if both statements are True | x < 5 and x < 10 |
or | Returns True if one of the statements is True | x < 5 or x < 10 |
not | Reverse the result, returns False if the result is True | not(x < 5 and x < 10) |
2.1 Order of Precedence and Left-to-Right
All the logical operators given above have lower precedence than the arithmetic operators and comparison operators.
You will also notice that the order of precedence (high to low) for the logical operators is not
, and
and then or
. This means you evaluate not
first, then and
, then or
.
You also evaluate left-to-right.
Operator | Name |
---|---|
() | Parentheses |
** | Exponentiation |
* , / , % , // | Multiplication, Division, Modulus, Floor Division |
+ , - | Addition, Subtraction |
== , != , < , > , <= , >= | Comparison Operators |
not | Logical NOT |
and | Logical AND |
or | Logical OR |
2.1 Evaluating a Complicated Boolean Expression
The table gives quite a complicated expression for the not
example.
not(x < 5 and x < 10)
Let's look at this for x = 4
,
# This is not code, we are manually evaluating to see how Python works with this expression
not(x < 5 and x < 10) # (Substitute x = 4)
not(4 < 5 and 4 < 10) # (Evaluate 4 < 5)
not(True and 4 < 10) # (Evaluate 4 < 10)
not(True and True) # (Evaluate True and True)
not(True) # (Evaluate not True)
False
and for x = 6
,
# This is not code, we are manually evaluating to see how Python works with this expression
not(x < 5 and x < 10) # (Substitute x = 4)
not(6 < 5 and 6 < 10) # (Evaluate 6 < 5)
not(False and 6 < 10) # (Evaluate False and ....)
not(False) # (Evaluate not False)
True
False and 6 < 10
evaluated to False
because and
requires both expressions to be True
. As the first is False
, why bother evaluating the second?
This is known as short-circuit evaluation or McCarthy evaluation. Named after the great John McCarthy.
2.2 Truth Tables
We can consider the result of combining two bool
variables p
and q
. The following are known as truth tables and display the result for different combinations of p
and q
using and
and or
.
2.2.1 Truth Table for and
p | q | p and q |
---|---|---|
True | True | True |
True | False | False |
False | True | False |
False | False | False |
2.2.2 Truth Table for or
p | q | p or q |
---|---|---|
True | True | True |
True | False | True |
False | True | True |
False | False | False |
=== TASK ===
For this program you should fix the single line instructed.
Copy the following code into a new Python file.
# DO NOT TOUCH THESE LINES. THEY ARE USED FOR THE INPUT
is_raining = bool(int(input()))
no_hat = bool(int(input()))
#######################################################
# You should fix this line to by forming an expression using is_raining and no_hat to produce the correct result for takes_umbrella.
# e.g takes_umbrella = is_raining or no_hat
# Note you only have to fix this line. No if statements etc.. required!
takes_umbrella = True
print(takes_umbrella)
Sam doesn't like getting his hair wet and sometimes wears a hat.
- On days that it is raining and Sam is not wearing a hat, Sam takes his umbrella.
- On days that it is raining and Sam is wearing a hat, Sam does not take an umbrella.
- If it is not raining Sam does not take an umbrella.
We use two variables is_raining
and no_hat
to represent whether it is raining and if Sam is wearing a hat.
- If it is raining
is_raining = True
- If Sam is NOT wearing a hat
no_hat = True
.
Using a third variable takes_umbrella
determine if Sam should take his umbrella by combining is_raining
and no_hat
.
For example, on days that it is raining and Sam is not wearing a hat the variables will have the following values:
is_raining = True
no_hat = True
takes_umbrella = True
is_raining
and no_hat
have been set up for you. Combine them with logical operators to get the correct value of takes_umbrella
.
HINT: You should consider the truth table and fill in the missing entries. This will then give you a hint to what the expression should be.
is_raining | no_hat | takes_umbrella |
---|---|---|
False | False | ? |
False | True | ? |
True | False | ? |
True | True | True |
W3Schools - Python Comparison Operators
Unit 4 - Iteration (Loops)
In the previous module, we looked at how we could write more complicated problems using conditionals (if statement, etc...).
In addition, we can make our programs repeat particular steps by looping (iteration).
Python has two basic loop statements:
while
loopsfor
loops
Iterations provide more complexity to our programs and make them more interesting.
Generally an iteration (loop) has two parts:
- Boolean test - an expression that evaluates to
True
orFalse
- Loop body - a block of code that is executed if the test evaluates to
True
The following image demonstrates the program flow of a basic iteration program.
You can see that the code enters the iteration (dotted box) and encounters the Test, if this evaluates to True
it runs the loop body and then repeats the test. If it evaluates to False
the program continues.
Image reproduced from Chapter 2: Introduction to Computation and Programming Using Python.
While Loops
A while loop lets us execute a series of statements as long as the condition (boolean expression) remains True
.
A while loop has the basic structure:
while <condition>:
# Do something until the condition is False
1. While Loop Using a Counter
A good portion of the while
loops that you write will involve a counter variable.
The following example demonstrates a basic while loop that prints out the numbers 1
- 5
:
i = 1 # Define a variable i and bind the value 1 to it
while i < 6:
print(i) # print out the value of i
i += 1 # This increments i by 1. i.e. i = i + 1
print("Program has ended!")
Note the indentation here! The indented block is the loop body (the code that is executed inside the loop).
The above code first defines a counter variable i = 1
. It then defines a while
loop that tests whether i < 6
. If this is True
it prints out the value of i
and then adds 1
to i
.
Note it is traditional to use i
for a counter variable.
The image below demonstrates how the code runs:
2. While Loop Using a Boolean Variable
Consider the following program which just keeps asking the user for a number until they enter a 0
. Then the program ends.
# loop until the user enters 0
input_string = input("Please enter a number:\n")
num = int(input_string)
while num != 0:
input_string = input("Please enter a number:\n")
num = int(input_string)
print("Program has ended!")
We can rewrite this program by using a bool
variable (program_over
) which keeps track of whether the while
loop should continue or stop.
After a number is entered, we use an if
statement to test if the number entered was 0
, if it was, then we set program_over
to True
. This will then stop the while
loop and the program will end.
# loop until the user enters 0
program_over = False
while not program_over:
input_string = input("Please enter a number:\n")
num = int(input_string)
if num == 0:
program_over = True
print("Program has ended!")
3. Non-terminating While Loop
Now consider the code if you forget to increment (add to) i
.
i = 1 # Define a variable i and bind the value 1 to it
while i < 6:
print(i) # print out the value of i
This loop will repeat forever! i
will never change its value from 1
and therefore the condition i < 6
will forever remain True
.
- What will be the output?
- Try it in Python. To stop (exit) the program in the terminal press
Ctrl + c
.
4. Nested While Loops
Just like if
statements, we can nest loops.
The following program shows an outer and an inner loop. This prints out the multiplication table.
i = 1
while i < 13:
j = 1
while j < 13:
print(f"{i} x {j} = {i*j}")
j += 1
i += 1
The outer loop has a counter variable i
and the inner loop has a counter variable j
. Below is a flow diagram of the program.
The following is of an animation of a similar program (below), but the conditions are changed to allow us to visualise it quicker.
i = 1
while i < 3:
j = 1
while j < 3:
print(f"{i} x {j} = {i*j}")
j += 1
i += 1
=== TASK ===
Use a while loop to print out the even numbers starting at 2 and up to and including 100.
2
4
6
.
.
.
100
Note that the dots represent numbers between 6 and 100. It saves us writing it all out!
HINT: You only need a single while loop.
The Number Guessing Game
This is an example mini-program to demonstrate the use of while
loops and if
.. else
statements.
The basic aim is a simple game to ask the user to guess a number between 1 and 10.
Feel free to play around with the program as much as you like.
Note: You will need to set the secret_guess
to 4
.
1. Number Guessing Game
Planning the program is important. The basic pseudocode for this program is:
pick a random number
while the player hasn’t guessed the number or chosen to exit
let the player guess
if player guessed correct
congratulate the player
The program in Python is given below.
# The Number Guessing Game
import random
# randomly generate a number between 1 and 10
secret_guess = random.randint(1,10)
# to pass the test uncomment this line
# secret_guess = 4
print("Welcome to the Number Guessing Game!")
print("You need to try and guess the number between 1 and 10...")
print("If you wish to exit the game enter 0..")
guess = int(input("Please enter a guess:\n"))
while guess != secret_guess and guess != 0:
print("That is not correct, please try again.")
guess = int(input("Please enter a guess:\n"))
if guess != 0:
print(f"Well done the correct answer is {secret_guess}")
This program generates a random number and then asks the user to input a guess.
The guess is then tested in the while
condition and if it is not the correct number and not 0
we repeat the step. If it is the correct number we exit the while
.
2. Adding Better Feedback
If you try the program above you will get pretty frustrated by the lack of feedback.
To improve the game we can let the user know if their guess is too low or too high.
This is the subject of exercise 4.5.
The Less Rubbish Password Program
This is an example mini-program to demonstrate the use of an if
... else
statement and a while
loop.
We previously created a rubbish password program using the following code.
# The Really Rubbish Password Program
# This is the stored password for the user
secret_password = "secret"
print("Welcome to NOSA Inc.")
print("Did you know that the Moon is an average of 238,855 miles away from Earth\n")
password = input("Please enter your password:\n")
if password == secret_password:
print("\nAccess Granted!")
else:
print("\nAccess Denied!")
input("\n\nPress the enter key to exit.")
Aside from this being very insecure, a major issue is that the user only gets one guess at entering their password.
1. Improving with a Loop
To improve our program we can add a while
loop that repeats the block of code.
password = input("Please enter your password:\n")
if password == "secret":
print("\nAccess Granted!")
else:
print("\nAccess Denied!")
To do this we add a boolean variable named access
which will track whether the user has access to the system. We then set this to True
within the if
statement when access is granted.
1.1. The Complete Program
# The Less Rubbish Password Program
# This is the stored password for the user
secret_password = "secret"
access = False
print("Welcome to NOSA Inc.")
print("Did you know that the Moon is an average of 238,855 miles away from Earth")
while not access:
password = input("\nPlease enter your password:\n")
if password == "secret":
print("\nAccess Granted!")
access = True
else:
print("\nAccess Denied!")
input("\n\nPress the enter key to exit.")
Ranges and Lists
Before we come onto for
loops we must understand ranges and lists.
1. List
A list
in python is used to store multiple values in a single variable.
For example:
list1 = [1,2,3]
list2 = ["Citreon", "Ford", "Audi", "Mercedes"]
list3 = ["Citreon", "Ford", 67, True]
You can access an element in a list
using indexing. Each member of the list
has an index number starting from 0
.
For example the code,
list3 = ["Citreon", "Ford", 67, True]
print(list3[0])
print(list3[2])
results in the output:
Citreon
67
Try creating your own list
in the and printing some of its elements.
For now, we will not discuss more about lists until later in the course. It suffices to know that they store multiple values and are heterogeneous (a fancy way of saying they can store different types).
2. Ranges
A range
allows us to create a sequence of numbers using the range()
function.
range(10) # This will create a range of numbers 0,1,2,3,4,5,6,7,8,9
range(3, 10) # This will create a range of numbers 3,4,5,6,7,8,9
range(2, 10, 2) # This will create a range of numbers 2,4,6,8
Ranges can take either:
- an end value
range(end)
- a start and end value
range(start, end)
- a start, end, and step value
range(start, end, step)
In some ways a range
behaves like a list
, however, to save space, Python doesn't create the list
.
If you type the following into the terminal, you will see that the object type of a range
is a range
!
a = range(10)
print(type(a)) # prints <class 'range'>
So think of it as a convenient way to store a list
of numbers without actually storing them!
By default start
is 0
and step
is 1
.
So range(10)
is the same as both range(0,10)
and range(0,10,1)
.
2.1 Convert a Range to a List
If you try and print a range
you won't get the numbers. Try the following in the terminal.
a = range(10)
print(a)
and you will get the output:
range(0, 10)
You can convert the range to a list using the list()
function.
a = list(range(2, 10)) # converts the range to a list [2,3,4,5,6,7,8,9]
print(a)
If you run the following code in the terminal or a Python file you will get the output:
[2,3,4,5,6,7,8,9]
The list()
function is forcing Python to build the list in memory.
To prove this to ourselves let's check the size of range(10**6)
. Note 10**6 = 1000000
import sys
a = range(10**6)
print(sys.getsizeof(a))
This returns 48
which is 48 bytes of memory. Try a range
of any size and you will always get 48
.
Now let's convert it to a list
and print the size:
import sys
a = list(range(10**6))
print(sys.getsizeof(a))
This returns a whopping 8000056
(8 million) bytes, which is approximately 166667
times as large!
=== TASK ===
Using the range()
and list()
functions, print out the odd numbers from 1
up to and including 99
.
Your output should look like this:
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99]
HINT: Make sure you have read Section 2.1
For Loops
The other main way of looping Python is by using a for
loop.
1. Structure of a For Loop
A for
loop in python is structured as follows:
for x in <iterable>:
## do something
Both ranges and lists are iterables which means we can loop over them.
1.1 For Loop with a List
A list is a collection of objects. They don't have to be of the same type.
The following 3 examples show a for loop that loops over each item in the list and prints out that item.
Example 1.1.1 - List of numbers
for x in [1,6,-23]:
print(x)
1
6
-23
The following animation shows how this works.
Example 1.1.2 - List of strings
The following loops over a list of strings,
for x in ["Citreon", "Ford", "Audi", "Mercedes"]:
print(x)
and outputs.
Citreon
Ford
Audi
Mercedes
We can also do this by first assigning the list to a variable and then using this in our for loop.
cars = ["citreon", "ford", "audi", "jaguar"]
for car in cars:
print(car)
Example 1.1.3 - List of different types
The following loops over a list of different types of objects,
for x in ["Citreon", "Ford", 67, True]:
print(x)
and outputs.
Citreon
Ford
67
True
1.2 For Loop with a Range
It is common to use the range()
function to automatically generate a sequence of numbers. Do you really want to type out the entire list?
It is also faster and more efficient because it does not have to build the list in memory.
Example 1.2.1
Here range(10)
generates the numbers 0,1,2,3,4,5,6,7,8,9
. You can think of it like the list [0,1,2,3,4,5,6,7,8,9]
.
The program then prints out double of each item in the list.
for i in range(10):
print(2*i)
0
2
4
6
8
10
12
14
16
18
Example 1.2.2
Here range(3,10)
generates the numbers 3,4,5,6,7,8,9
. You can think of it like the list [3,4,5,6,7,8,9]
.
The program then prints out each item in the list.
for i in range(3,10):
print(i)
3
4
5
6
7
8
9
Example 1.2.3
Here range(3,20,2)
generates numbers using a step size of 2 - 3,5,7,9,11,13,15,17,19
. You can think of it like the list [3,5,7,9,11,13,15,17,19]
.
The program then prints out each item in the list.
for i in range(3,20,2):
print(i)
3
5
7
9
11
13
15
17
19
1.3 For Loop with Underscore
If you are not using the counter variable in the for
loop, it is convention to use an _
(underscore).
for _ in range(10):
print("Hello")
The above will print out Hello
10
times.
2. Nested For Loops
Just as we did with while
loops, we can nest for loops. Here we use an outer for
loop and an inner for
loop to print out the multiplication tables.
for i in range(1,13):
for j in range(1,13):
print(f"{i} x {j} = {i*j}")
The animation below demonstrates how this program works, note that instead of printing out the whole table we have limited this to just 1
and 2
.
for i in range(1,3):
for j in range(1,3):
print(f"{i} x {j} = {i*j}")
If we compare this to the same program using a while
loop, you can see that the for
loop version is less verbose and easier to read.
i = 1
while i < 13:
j = 1
while j < 13:
print(f"{i} x {j} = {i*j}")
j += 1
i += 1
=== TASK ===
You can find the length of a string by using the len()
function.
For example:
string1 = "for loop"
len_string = len(string1)
print(len_string)
# note you could do this in one line print(len(string1))
This will output the length of the str
"for loop"
. That is, 8
.
Copy the following into a new Python file:
# Do not touch this line - It is just there to set up the string list from the input
# string_list will contain a list of strings that have been entered.
string_list = input("Please enter a list of numbers separated by a comma.\ne.g. Citreon,Ford,Audi,Mercedes\n").split(',')
print(string_list)
This code will accept a list of comma-separated strings. e.g. Citreon,Ford,Audi,Mercedes
.
If you try running the program and you enter a list of comma-separated strings they will be printed back to the terminal.
['Citreon','Ford','Audi','Mercedes']
The strings inputted are stored in the list
named string_list
.
Amend the program so that it prints out the following for each inputted string.
The length of the string {string} is {length of string}.
For example, for the string "Ford"
the program would print:
The length of the string Ford is 4.
For the input one,two,three,four,five
, the whole program acts as follows:
Please enter a list of numbers separated by a comma.
e.g. Citreon,Ford,Audi,Mercedes
one,two,three,four,five
The length of the string one is 3.
The length of the string two is 3.
The length of the string three is 5.
The length of the string four is 4.
The length of the string five is 4.
For the input Citreon,Ford,Audi,Mercedes
, the whole program acts as follows:
Please enter a list of numbers separated by a comma.
e.g. Citreon,Ford,Audi,Mercedes
Citreon,Ford,Audi,Mercedes
The length of the string Citreon is 7.
The length of the string Ford is 4.
The length of the string Audi is 4.
The length of the string Mercedes is 8.
Note that to pass the tests you must have exactly the output above, apart from the input which will differ depending on what the user inputs.
Break and Continue
break
and continue
are keywords in python that allow you to stop the loop or skip the current iteration.
Many times programmers use break
and continue
when they don't need to. Generally break
and continue
can lead to funny flows in your program and bugs. If you can avoid using them then do!
1. Break
The break
keyword allows us to exit ("break") out of a loop early. We can do this with both while
and for
loops.
Example 1.1
The following program will stop short of printing the numbers 0,1,2,3,4,5,6,7,8,9
and only print 0,1,2,3,4
.
If you need to convince yourself, copy, paste and run this code in Python.
i = 0
while i < 10:
if i == 5:
break
print(i)
i += 1
The program tests each value of i
and if it is equal to 5
the program exits the loop.
We can write this without the break
.
i = 0
while i < 5:
print(i)
i += 1
Example 1.2
Here is the same program with a for
loop
for i in range(0,10):
if i == 5:
break
print(i)
We can write this without the break
.
for i in range(0,5):
if i < 5:
print(i)
2. Continue
The continue
keyword allows us to skip the current iteration of a loop. We can do this with both while
and for
loops.
Example 2.1
The following program will not print out the number 5
, but all other numbers.
So the output will be the numbers 0,1,2,3,4,6,7,8,9
. Notice that 5
is missing.
If you need to convince yourself, copy, paste and run this in Python.
i = -1
while i < 9:
i += 1
if i == 5:
continue
print(i)
The program tests each value of i
and if it is equal to 5
the program exits the loop.
We can write this without the continue
.
i = 0
while i < 10:
if not i == 5:
print(i)
i += 1
Example 2.2
Here is the same program with a for
loop
for i in range(0,10):
if i == 5:
continue
print(i)
We can write this without continue
.
for i in range(0,10):
if not i == 5:
print(i)
3. No Do...While
To date in Python, we have seen a while loop that will check a condition and execute a code block until the condition becomes False.
while <condition>
# Do something
In many languages it is possible to do the following:
do
run a block of code
while <condition>
This guarantees that the code block runs at least once as the while
One of the major reasons Python does not support such a statement is that it breaks the indentation rule that a code block should be indented. Here it isn't!
3.1. Try Not To Use Break
It is quite common to see the following in Python.
while True:
# do something
if <condition>:
break
The following essentially emulates a do...while loop. Here is an example.
# The Number Guessing Game
import random
# randomly generate a number between 1 and 10
secret_guess = random.randint(1,10)
print("Welcome to the Number Guessing Game!")
print("You need to try and guess the number between 1 and 10...")
while True:
guess = int(input("Please enter a guess:\n"))
print("That is not correct, please try again.")
if guess == secret_guess:
break
print(f"Well done the correct answer is {secret_guess}")
However, the use of a break
statement here relies on the person reading this to implicitly understand that the programmer is intending to use a do...while.
There are also other reasons we would not want to do this in other languages which are compiled to do with the fetch-decode-execute cycle.
We can easily rewrite this with an additional boolean.
# The Number Guessing Game
import random
# randomly generate a number between 1 and 10
secret_guess = random.randint(1,10)
print("Welcome to the Number Guessing Game!")
print("You need to try and guess the number between 1 and 10...")
game_over = False
while not game_over:
guess = int(input("Please enter a guess:\n"))
print("That is not correct, please try again.")
if guess == secret_guess:
game_over = True
print(f"Well done the correct answer is {secret_guess}")
Now, these are almost identical, however, it is now more clear that the while
will end when gameover
is set to True
. This conveys the programmers' intention.
=== TASK ===
Print out the numbers 1
to 100
but leave out the meaning of life 42
.
Use a continue
statement.
The Sum Numbers Program
This is an example mini-program.
The aim is to ask the user for a series of numbers. If the user enters 0
, the program then outputs the sum of the inputted numbers
Feel free to play around with the program as much as you like.
The first program uses a break
to exit the while
loop. Notice that the while
condition is always True
. So the break
is essential to exit.
# The Sum Numbers Program
print("Welcome to The Sum Number Program")
total = 0
while True:
input_string = input("Please enter a number. Enter 0 to stop\n")
if input_string == "0":
break
x = float(input_string)
total += x # note this is shorthand for: total = total + x
print(f"Sum of numbers entered is {total}")
We can also do this without a break
by using a bool
named program_over
. You will notice that this is a little more verbose.
print("Welcome to The Sum Number Program")
program_over = False
total = 0
while not program_over:
input_string = input("Please enter a number. Enter 0 to stop\n")
if input_string == "0":
program_over = True
else:
x = float(input_string)
total += x
print(f"Sum of numbers entered is {total}")
NOTE: Please avoid using break
if you can. It can lead to code that becomes hard to reason about or follow.
While this might seem irrelavant to this program, imagine a much larger program with a more complicated flow and the use of lots of breaks and continues. This can become hard to reason about.
The Check Valid Input Program
The following mini-program demonstrates the use of try
..except
.
We will look at exceptions in more detail later on; however, it is important to see try
and except
early on.
1. A Program That Fails
Consider the following program.
num_string = input("Please enter a number:\n")
# try to convert the number using float()
# this will fail if the user doesn't enter anything valid. e.g. hello
x = float(num_string)
print(f"{x} + 5 = {x+5}")
If you enter something which is not a number, Python will throw an exception, in particular, a ValueError
.
2. Validating the User Input
We can fix this issue by catching this exception using the try
...except
statement.
input_valid = False
while not input_valid:
num_string = input("Please enter a number:\n")
try:
num = float(num_string)
input_valid = True
except ValueError:
print("Input is not a valid number.\n")
print(f"{num} + 5 = {num+5}")
Here the program attempts to convert num_string
to a float
.
If this throws an exception, then we trigger the except
block and print out Input is not a valid number.
. Note that the line input_valid = True
is not executed and thus input_valid
remains set to False
. Therefore, we ask the user for another number.
This will continue until the user enters input that does not trigger the exception and input_valid
is set to True
.
Unit 5 - Functions
Functions are such a huge part of programming that we will be spending two weeks (weeks 5 and 7) on this subject.
In this unit we will be discussing:
- What a function is
- Why do we use functions
- Arguments vs Parameters
- Return Values
- Variable Scope
- Passing by Assignment
A function consists of three parts:
- The input(s) to the function
- The function itself
- The output from the function
You can think of a function as a machine (black box) that takes some input(s), does something, and then produces an output.
Once you define a function, you can reuse it. This is the principal reason for their existence. Don't Repeat Yourself! (DRY)
I have included 5 examples of functions. For now, I want you to understand the idea as a concept. We will see examples 1, 2, 4, and 5 in Python very soon!
1. Example Function - Is a Number Odd?
The first function takes in 1 input (an int
) and produces 1 output (a bool
).
This function is a machine that tells us whether the input is odd.
For now, we don't care how it works, we just assume it does.
The following image depicts the function on the left and then two examples of it being used on the right.
2. Example Function - Add Two Numbers
The second function takes two inputs which are numbers (float
) and outputs the sum of those two numbers.
Key point, a function can take multiple inputs!
3. Example Function - What Colour is a Shape?
The third function might look odd as in python we do not have a Shape
type (yet!). Later in the course, we will make one.
It serves to demonstrate that a function is just a machine that does something to the input and produces an output.
4. Example Function: Print Hello Name
The final two examples are quite common. The first has an input, but no output. It takes in a person's name (e.g "Emily"
) and prints "Hello Emily"
to the terminal.
We will talk about this and its relationship to Python's None
.
5. Example Function: Print Hello World
The final example has no input and no output. It is just a machine that runs a piece of code.
In this case, it just prints "Hello World"
.
Passing Unit Tests
This is very important
To date, we have been using input/output tests. From now on we will be mainly using unit tests that import functions from your files.
These allow us to test individual functions. We will discuss this properly later on in the module.
There are two main points here:
- You should only write code in either a function block. e.g. the
add()
function. - You should write all other code that runs your program and calls the functions in the
if __name__ == "__main__"
block.
Any code outside of this can cause problems with the tests!
Function Basics
As we mentioned in the overview a function consists of three parts:
- The input(s) to the function
- The function itself
- The output from the function
You can think of a function as a machine (black box) that takes some input(s), does something, and then produces an output.
Once you define a function, you can reuse it. This is the principal reason for their existence. Don't Repeat Yourself! (DRY)
In essence it is a way of packaging up bits of code for reuse or to make your program more readable.
1. Defining a Function in Python
We define a function using the keyword def
, a function name, and the function parameters. We then write the code the function performs in the function body.
def function_name(<formal parameters>):
<function body>
The easiest way to demonstrate a function is by an example.
Our first function will just add 5 to the input
def add_five(x):
return x + 5
Note that the return
keyword is how you return (output) something from a function. Here the function add_five()
returns the result of adding 5
to the input x
.
2. Calling a Function in Python
add_five()
is now a function that you can call with different values of x
. e.g. add_five(10)
Try running the following code,
def add_five(x):
return x + 5
result = add_five(10) # call function with 10 and bind result
print(result) # this prints the value of result
and you will get
15
as the output.
You can visualise this yourself on Python Tutor.
Note that we could have called this function twice.
def add_five(x):
return x + 5
result_one = add_five(10) # call function with 10 and bind result
result_two = add_five(7) # call function with 7 and bind result
print(result_one) # this prints the value in result_one
print(result_two) # this prints the value in result_two
and you will get
15
12
as the output.
2.1 Define a Function Before You Call It
Now try the following,
result = add_five(10)
print(result)
def add_five(x):
return x + 5
and you will get NameError
. This is because the function has not yet been defined, i.e. it is below the function call. Python does not know about that name.
2.2 Order of Precedence
We can also do more interesting things like:
def add_five(x):
return x + 5
result = add_five(10)*add_five(7) # same as 15*12
print(result) # prints 180
Here Python will evaluate add_five(10)
and then evaluate add_five(7)
, then multiply them together.
This is because other than parentheses ()
, function calls have higher precedence than everything else in Python.
2.3 Multiple Return Statements
Consider the following function.
def greater_than_five(x):
if x > 5:
return True
else:
return False
if __name__ = "__main__":
print(greater_than_five(3)) # prints False
print(greater_than_five(7)) # prints True
Here we use an if
statement to test whether x
is greater than 5
, if it is the function returns True
, otherwise it returns False
.
This is a key point, although your function can only return one output, it can have more than one return
statement in your function.
Note that we could have done this with one return
statement by returning the result of the boolean expression.
def greater_than_five(x):
return x > 5
if __name__ = "__main__":
print(greater_than_five(3)) # prints False
print(greater_than_five(7)) # prints True
Again which one you use will be your preference and which one you are more comfortable with. Both ways are fine. If the second one isn't for you, then use the first one.
3. if __name__ = "__main__":
You will have noticed in the last couple examples used the if __name__ = "__main__":
block. This is not needed to call your functions, but as stated in the 5.1 - MP: Passing Unit Tests you should write any code and calls to your functions inside this block.
The reason is that the unit tests import your code and anything outside the if __name__ = "__main__":
will get run due to the import. This can mess up the unit tests!
=== TASK ===
Create a function that tests whether a number is odd or even.
Copy the following into a new Python file.
def odd_test(x):
# delete pass and replace with code to implement the function
pass
if __name__ == "__main__":
print(odd_test(3)) # should output True
print(odd_test(8)) # should output False
Your function odd_test
and take in one input.
It should return a True
or False
depending on the number.
For example:
odd_test(3)
should return True
and odd_test(8)
should return False
.
More About Functions
HINT: I would copy and past examples into a Python interpreter to make sure you understand them.
This is one of the most important lessons on functions. Please make sure you understand all 3 of the following.
In this lesson we will cover the following:
- Different types of functions
- Parameters vs arguments
- Function composition
All of these are important concepts and will help you understand later material.
1. Types of Function
1.1 Function with Multiple Parameters
We are not limited to just one parameter, we can have as many as we like. The following two functions take two parameters.
The first returns the sum of the two inputs.
def add(x, y):
return x + y
if __name__ == "__main__":
result = add(3, 13)
print(result) # prints 16
The seconds returns the maximum of the two inputs.
def maximum(x, y):
if x > y:
return x
else:
return y
if __name__ == "__main__":
result = maximum(3, 5)
print(result) # prints 5
You will note that this function has two uses of return
for the two possible cases.
1.2. Function with No Return Value
Here we define a function with a single parameter name
(the input of the function).
def print_hello(name):
print(f"Hello {name}")
This is interesting as it takes an input name
and does something with the input. However, it does not have a return
statement.
Be careful though, this function does return something, it returns the None
keyword which represents no value.
Run the following code and you will see this for .
def print_hello(name):
print(f"Hello {name}")
if __name__ == "__main__":
# call the function and bind the return value to a
a = print_hello("Jimi")
print(a)
You will notice that it outputs,
Hello Jimi
None
because a
stores the return value of print_hello()
which is None
.
1.3. Function with No Input and No Return Value
The final function has no input and no return
statement. Its sole purpose is to print two lines.
def print_hello_world():
print("Hello World")
print("Welcome to another day on planet earth!")
if __name__ == "__main__":
print_hello_world()
print_hello_world()
The code above will output,
Hello World
Welcome to another day on planet earth!
Hello World
Welcome to another day on planet earth!
as the function is called twice.
1.4. Some Familiar Functions
You have already met a bunch of Python functions.
Function | Input | Returns | Purpose | Example |
---|---|---|---|---|
print() | str to output to the terminal. | None | Prints the input to the terminal. | print("Hello World") |
input() | str to output to the terminal. | str entered by the user. | Prints the input to the terminal and returns what the user inputted as a string | input("Please enter your name:\n") |
int() | str representation of a whole number, e.g."5" | int conversion of input. e.g. 5 | Converts a str to an int if possible. If not throws ValueError | int("5") |
1.5 The NotImplementedError
Sometimes it is convenient to define a function, but not write the code yet. This is especially useful when planning a program. It is also useful to make sure that if this function is called it doesn't result in a silent bug.
To stop this, we can define a function that raises a NotImplementedError
. This as the name suggests means that you have not implemented the function.
Here we set up a program for computing the total and length of a list. The length()
function has not been implemented and raises a NotImplementedError
.
def total(num_list):
""" compute the total of the numbers in the list"""
tot = 0
for x in num_list:
tot += x
return tot
def length(num_list):
""" count the number of numbers in the list"""
raise NotImplementedError("The length function has not been implemented yet!")
if __name__ == "__main__":
num_list = [4,3,5,7,4]
print(f"The total of the list is {total(num_list)}") # This line will run
print(f"The number of numbers in the list is {length(num_list)}") # This line will fail
If you try this in a Python file, e.g. one named main.py you will get the output.
The total of the list is 23
Traceback (most recent call last):
File "main.py", line 14, in <module>
print(f"The number of numbers in the list is {length(num_list)}") # This line will fail
File "main.py", line 10, in length
raise NotImplementedError("The length function has not been implemented yet!")
NotImplementedError: The length function has not been implemented yet!
This is because when we call the length()
function it raises the NotImplementedError
with the message The length function has not been implemented yet!
.
Note thought that Python actually ran the code before it and printed out the list total.
1.6 The Pass Statement
Another way to do this is by using the pass
statement. When a programmer doesn't know what to write or is creating a skeleton program they will sometimes put pass
. pass
will mean the code is valid and can therefore be run.
def total(num_list):
""" compute the total of the numbers in the list"""
tot = 0
for x in num_list:
tot += x
return tot
def length(num_list):
""" count the number of numbers in the list"""
pass
if __name__ == "__main__":
num_list = [4,3,5,7,4]
print(f"The total of the list is {total(num_list)}") # This line will run
print(f"The number of numbers in the list is {length(num_list)}") # This line will run but prints - The number of numbers in the list is None
The length()
function has a pass statement and will now return None
.
Try this in a Python Tutor
2. Parameters vs Arguments
It is important to understand the difference between the parameters of a function and arguments. These words are often confused or used interchangeably.
Parameters
Parameters are the names that are in the function definition in between the parentheses ()
.
Arguments
Arguments are the names that are used in the function call.
The following image shows both the function definition and the function call.
3. Function Composition
Function composition is the act of applying a function to the result of another function.
The name is taken from the mathematical concept of function composition.
Consider the following code.
def add5(x):
return x + 5
def mul2(x):
return 2*x
if __name__ == "__main__":
x = 10 # binds 10 to x
x = add5(x) # binds 15 to x, i.e. 10 + 5 = 15
x = mul2(x) # binds 30 to x, i.e. 2*15 = 30
print(x) # prints 30
We can write this using function composition, note that we evaluate the innermost function first. So work inner to outer.
The example below first evaluates add5()
and then evaluates mul2()
with the result of add5()
.
x = 10 # binds 10 to x
x = mul2(add5(x)) # first computes add5(10) = 15, then computes 2*15 = 30 and binds to x
print(x) # prints 30
We can go further and compose three functions. Now we pass the result of the first two to the print()
function.
x = 10
print(mul2(add5(x))) # first computes add5(10) = 15, then computes 2*15 = 30 and then prints 30
You could even just do the following.
print(mul2(add5(10))) # does the whole lot in one line!
OK great, we did it in one line!
However, take a look at the readability of the code. The examples which use function composition here are not exactly very readable, in fact, it obfuscates things.
The first example is clear. Assign 10
to x
, then add 5
to x
, then multiply x
by 2
and finally print x
.
There are many examples of complex programs that we can write in python with one-liners. This is fine if it is just for your eyes only.
I would suggest for readability you avoid it unless it is necessary or makes sense and is readable.
3.1 Function Composition Isn't Commutative
It is also important to know that function composition isn't commutative, that is just a fancy way of saying that if you change the order of evaluation it doesn't have the same result.
For example,
3 * 5 == 5 * 3 # True, because multiplication doesn't care about the order (commutative)
3 / 5 == 5 / 3 # False, division cares about the order (not commutative)
The following example shows the add5()
and mul2()
functions composed in the two possible ways give two different results.
def add5(x):
return x + 5
def mul2(x):
return 2*x
if __name__ == "__main__":
x = 10
y = mul2(add5(x))
z = add5(mul2(x))
print(y) # prints 30
print(z) # prints 25
You can try this in Python Tutor and see that y
and z
are different values because the order of composition was changed.
=== TASK ===
Copy the following into a new Python file. You will need to remove the raise NotImplementedError
and replace it with your own code. Make sure you read the whole of task.
def reverse(str_to_reverse):
raise NotImplementedError("You have not implemented the reverse function")
def get_character(string_one, i):
raise NotImplementedError("You have not implemented the get_character function")
if __name__ == "__main__":
print(reverse("This is a string")) # prints "gnirts a si sihT"
print(reverse("Hello World")) # prints "dlroW olleH"
print(get_character("This is a string", 3)) # prints "i"
print(get_character("Hello World", 5)) # prints "o"
The first function is called reverse()
and has a single parameter, a string. The function should output the reverse of the string.
You should do this using a loop. There is a simple way to do this in Python, but it is good practice to write your own reverse()
method.
The second function is called get_character()
and has a string and a number as parameters. It should return the ith character of the string.
The table below gives the return values of the functions for different calls.
Function call | Expected return value |
---|---|
reverse("This is a string") | "gnirts a si sihT" |
reverse("Hello World") | "dlroW olleH" |
get_character("This is a string", 3) | "i" |
get_character("Hello World", 5) | "o" |
I suggest you test the above work correctly in the terminal, you should not rely on the tests as the feedback may not be useful.
The Maximum of a List Program
We are now going to recycle our maximum()
function.
def maximum(x, y):
if x > y:
return x
else:
return y
1. Planning the Program
Here is an outline of the program in pseudocode.
Given a list
Store the first value of the list as current_max
for each element x in the list (not including the first)
current_max = max(current_max, x)
print out current_max as the result
2. Program - Maximum of a list
Let's now implement this and use our maximum()
function in a program to loop through a list of numbers and print out the maximum.
def maximum(x, y):
if x > y:
return x
else:
return y
if __name__ == "__main__":
num_list = [3,6,2,1,8,4,4,2,7]
# set the current max to the first element in the list
current_max = num_list[0]
# loop through the list (start at the second element)
for x in num_list[1:]:
# store result of current_max and x
current_max = maximum(current_max, x)
print(f"Maximum number is {current_max}")
Hopefully, you can see that we didn't really care how maximum()
worked, we just knew that maximum()
takes in an x
and y
and returns the larger.
3. Program - Maximum of a list (Function)
I now want to take this opportunity to rewrite this. Let's create a function that can take in a list of numbers and outputs the maximum.
def maximum(x, y):
if x > y:
return x
else:
return y
def max_list(num_list):
current_max = num_list[0]
for x in num_list[1:]:
current_max = maximum(current_max, x)
return current_max
if __name__ == "__main__":
list_one = [3,6,2,1,8,4,4,2,7]
list_two = [30,16,4,45,27,84]
list_three = [-10,-4,-3,-2]
print(f"Maximum of list 1 is {max_list(list_one)}")
print(f"Maximum of list 2 is {max_list(list_two)}")
print(f"Maximum of list 3 is {max_list(list_three)}")
Congratulations, you have just written a function max_list()
that takes in a list
of numbers and returns the maximum.
This is a reusable function that we don't really care how it works, we just know it works! This is the essence of what we call decomposition and abstraction which we will look at in unit 7.
Interestingly Python provides such a function for us. Try typing this into the Python Interactive Shell.
max([5,2,4,7])
Again let me stress, max()
is a built-in function that we do not know how it works. We just know it takes list
and returns the maximum.
You can type the following into the Python Interactive Shell to get help information about the max()
function.
help(max)
Scope
This can be a tough one to get your head around, so I suggest you speak to the instructors in the labs if it doesn't make sense.
When you define a function you define a new namespace also called a scope.
Any variable defined within the function is said to be a local variable and can only be accessed inside the function (the scope of the function).
We use function parameters and the return value to exchange information between parts of our program.
NOTE: I do not use the if __name__ == "__main__":
block here, it is NOT required, but normally it is very good practice to use it.
1. Example 1
Let's illustrate this using the example from Guttag, J.V. (2021).
def f(x):
# variables defined here are only available to f() - local
# we also do not have access to variables created outside of f()
y = 1
x = x + y
print(f"x = {x}")
return x
# main body of code starts here
# note we have left out if __name__ == "__main__" here. It isn't needed, but is recommended.
x = 3
y = 3
z = f(x)
print(f"z = {z}")
print(f"x = {x}")
print(f"y = {y}")
When a function is called, it creates a new stack frame and any variable defined in the function scope exists in this stack frame and is local to this stack frame.
Once the function has finished, the stack frame is removed and the local variables are also removed.
Here x
and y
are first defined on the global frame. Then we call f(x)
which creates a new stack frame and new variables x
and y
. We say that information is passed to f()
These two x
's and y
s' are different! I cannot stress the importance of this. The same name, but different scope. They are not the same variable!
I have created an animation from Python Tutor which shows the stack frame that is created when the function is called and the other local variable x
.
I would also copy the example into Python Tutor and follow the program line by line.
2. Example 2
The following code defines two functions f()
and g()
and calls both of them one after another.
def f(x):
""" adds 1 to x and prints result"""
y = 1
x = x + y
print(f"x = {x}")
return x
def g(x):
""" adds 2 to x and prints result"""
y = 2
x = x + y
print(f"x = {x}")
return x
# main body of code starts here
x = 3
y = 3
z = f(x)
t = g(x)
print(f"z = {z}")
print(f"t = {t}")
print(f"x = {x}")
print(f"y = {y}")
This has the effect of creating a stack frame for f()
. When f()
is finished the stack frame is removed.
Next a stack frame for g()
is created. When g()
is finished the stack frame is removed.
The program now drops back to the global stack frame and completes.
The animation below illustrates this visually.
Again, I would also copy the example into Python Tutor and follow the program line by line.
3. Example 3
Each call to a function creates a new stack frame.
So if we call a function from within a function, we actually create a new stack frame on top of the previous stack frame.
The reason we call them stack frames is because they get stacked upon each other.
Here f()
has been updated to actually call g()
from within its function body.
def f(x):
""" adds g(x) to x and prints result"""
y = g(x)
x = x + y
print(f"x = {x}")
return x
def g(x):
""" adds 2 to x and prints result"""
y = 2
x = x + y
print(f"x = {x}")
return x
# main body of code starts here
x = 3
y = 3
z = f(x)
print(f"z = {z}")
print(f"x = {x}")
print(f"y = {y}")
You will see from the animation or, by experimenting on Python Tutor or using the debugger, that we end up with the following stack frames all at once.
- Global stack frame
- f - stack frame for
f()
- g - stack frame for
g()
Once g()
finishes it is popped off (removed) from the stack and we go back to f()
. Once f()
finishes we go back to the global frame.
Note that Python Tutor displays them underneath each other, if it helps think of them stacked on top of each other.
=== TASK ===
Don't skip reading the above lesson, in the long term it won't help you!
This one is a bit of a freebie.
Print out the following about scope, namespaces, local variables, and stack frames
print("When you define a function, you define a new namespace also called a scope.\n")
print("Any variable defined within the function is said to be a local variable and can only be accessed inside the function (the scope of the function).\n")
print("Calling a function creates a new stack frame and any variable defined in the function scope exists on this stack frame and is local to this stack frame.")
References
Guttag, J.V. (2021). Introduction to Computation and Programming Using Python, Third Edition (3rd ed.). MIT Press.
Keyword Arguments and Default Values
1. Parameters and Arguments
To date, our functions have just had parameters such as:
def print_name_age(name, age):
print(f"Hello {name}, your age is {age}")
This function takes two parameters; name
and age
. When we call the function we can provide arguments (values) to the function that are bound to the parameters.
So print_name_age("Jimi", 27)
will bind name = Jimi
and age = 27
. These are then used in the print
statement.
Formally these are known as positional parameters because you call the function and pass the arguments in the correct position.
2. Keyword Arguments and Default Values
Python provides another way of defining the parameters of a function called keyword arguments.
You can think of these as optional arguments, you will though need to give them a default value.
Let's look at an example from Guttag, J.V. (2021).
def print_name(first_name, last_name, reverse = False):
if reverse:
print(f"{last_name}, {first_name}")
else:
print(f"{first_name}, {last_name}")
print_name()
is now a function whereby the third parameter is a keyword argument with a default value of False
.
This means we can leave out the keyword argument. We can just call using the two positional parameters first_name
and last_name
. Inside the function reverse
will take on the default value of False
.
print_name("Jimi", "Hendrix") # prints out Jimi, Hendrix
We can also choose to call the function and overwrite the value of the keyword argument, for example we can tell the function to set reverse
to False
.
print_name("Jimi", "Hendrix", reverse = True) # prints out Hendrix, Jimi
which prints out Hendrix, Jimi
because reverse is True
.
There is also nothing stopping you setting reverse
to False
, this is perfectly legal, but not necessary.
print_name("Jimi", "Hendrix", reverse = False) # prints out Jimi, Hendrix
2.1 Order matters.
You can't put keyword arguments before positional parameters.
The following is illegal in Python:
def print_name(reverse = False, first_name, last_name):
if reverse:
print(f"{last_name}, {first_name}")
else:
print(f"{first_name}, {last_name}")
2.2 Another Simple Example
Let's say you want a price()
function that calculates discounted price.
This has an obvious default, that is, no discount.
Here is a function that reflects this. Note that discount is a percentage between 0
and 100
.
def calculate_price(price, discount=0):
""" returns new price with discount applied"""
return price * (1 - discount/100)
if __name__ == "__main__":
print(calculate_price(30)) # will just return 30. No discount
print(calculate_price(30, discount=50)) # will just return 15. 50% discount on the price
We could extend this to add an optional tax argument.
def calculate_price(price, discount=0, tax=20):
""" returns new price with discount applied and tax"""
total_before_tax = price * (1 - discount/100)
total_after_tax = total_before_tax * (1 + tax/100)
return total_after_tax
if __name__ == "__main__":
print(calculate_price(30)) # will just return 36. No discount, tax=20
print(calculate_price(30, discount=50)) # will return 18. 50% discount on the price, tax=20
print(calculate_price(30, discount=50, tax=25)) # will return 18.75. 50% discount on the price, tax=25
3. A Few More Keyword Arguments
Just to complete the picture. Here is a function with two keyword arguments. You can have any number of keyword arguments.
def print_person(first_name, last_name, reverse=True, age=None):
if reverse:
print(f"{last_name}, {first_name}")
else:
print(f"{first_name}, {last_name}")
if age:
print(f"Aged {age}.")
We can call this function a number of different ways.
print("Jimi", "Hendrix") # Prints Jimi, Hendrix
print("Jimi", "Hendrix", age = 27)
# Prints
# Jimi, Hendrix
# Aged 27.
print("Jimi", "Hendrix", age = 27, reverse = True)
# Prints
# Hendrix, Jimi
# Aged 27.
Some of you may have spotted in the last example that age
and reverse
are given in a different order to the function definition.
This is deliberate, once you have given the positional arguments, you can give the keyword arguments in any order.
4. Mutable Keyword Arguments (DO NOT USE)
Consider the following code:
def test_keyword_mutability(list_one=[]):
list_one.append("sam")
return list_one
if __name__ == "__main__":
test_keyword_mutability()
test_keyword_mutability()
We would expect this to print out:
['sam']
['sam']
Instead it will print out:
['sam']
['sam', 'sam']
This wasn't the intention, this is because the keyword argument list_one=[]
is mutable and Python stores it the when the function is defined, not when it is called.
We can correct this by doing the following:
def test_keyword_mutability(list_one=None):
if list_one is None:
list_one = []
list_one.append("sam")
return list_one
if __name__ == "__main__":
test_keyword_mutability()
test_keyword_mutability()
['sam']
['sam']
Much better!
For more information you can see this link:
Python Mutable Defaults Are The Source of All Evil
=== TASK ===
Write a function called print_list
that prints the multiple of each item in the list
For example print_list([4,1,6,7], multiple = 2)
8
2
12
14
For example print_list([4,1,6,7], multiple = 3)
12
3
18
21
By default your function when called without the keyword argument multiple
, i.e. print_list([4,1,6,7])
should output:
4
1
6
7
Copy the following into a new Python file to get started.
def print_list():
pass
if __name__ == "__main__":
print_list([4,1,6,7], multiple = 2)
print_list([4,1,6,7], multiple = 3)
print_list([4,1,6,7])
Note: Due to the way that the tests are written for this task, an incorrect solution may result in the tests looking as though they are "hanging". Refresh the page to reattempt the test after considering why your solution may be incorrect.
References
Guttag, J.V. (2021). Introduction to Computation and Programming Using Python, Third Edition (3rd ed.). MIT Press.
Specification and Docstrings
When defining functions it is important to inform the person using your function of three things
- What does the function do?
- What are the parameters of the function and what type should they be?
- What does the function return?
Sometimes a function won't take parameters or return anything, so not all of these need documenting.
The standard way of doing these in Python is docstrings. A docstring uses triple quotes """
.
1. Creating a Docstring
It is easiest to look at a simple example.
The following function is the simple add function we have seen before:
def add(x, y):
return x + y
The answers to the 3 questions above for this function are
- Adds up two numbers and return the result.
x
(int
orfloat
),y
(int
orfloat
). These are the numbers to add.- Returns the sum of
x
andy
.
The following code snippet shows how we add these with a docstring.
def add(x, y):
"""
Adds up two numbers and returns the result.
Parameters
----------
x: int or float
First number to add.
y: int or float
Second number to add.
Returns
-------
int or float
Sum of x and y.
"""
return x + y
Our basic template is:
"""
Function description
Parameters
----------
param_name: type
Description of parameter
param_name: type
Description of parameter
.
.
.
Returns
-------
type
Description of return value
"""
We will be using the NumPy standard in this course that works with the documentation generator library Sphinx. We are not looking at this in detail in this course but is important to know about it.
If we are missing either the parameters or return statement then we just leave these out of the docstring.
def print_hello():
"""
Prints Hello.
"""
print("Hello")
def print_sum(x, y):
"""
Prints the sum of the two numbers.
Parameters
----------
x: int or float
First number to add.
y: int or float
Second number to add.
"""
print(x + y)
And even though you would rarely see a function like the following, for completeness.
def five():
"""
Returns 5
Returns
-------
int
Always returns 5
"""
return 5
2. Python's help() Function
Python has many built-in functions. You can find an extensive list below.
One of these functions is help()
which will display the docstring of a function.
Try the following in the terminal:
help(abs)
This gives you the docstring for the built-in function abs()
.
3. When To Document
Generally, when you are hacking away at your own code, documentation probably will be furthest from your mind. I would still suggest putting in a short description.
Every programmer has written some complicated code and then come back to it at a later date and not had a clue what it does. This then requires time, some hints will help you.
Clearly when working with others and on a shared codebase documenting is important. It is also important if people will be using your code and they would like some hints by using the help()
function. If you don't write a docstring, there won't be any help!
=== TASK ===
Copy the following into a new Python file. You won't have to understand this code to pass the task, but you will need to read on.
def add(x, y):
"""
Adds up two numbers and returns the result.
Parameters
----------
x: int or float
First number to add.
y: int or float
Second number to add.
Returns
-------
int or float
Sum of x and y.
"""
return x + y
# Example from Guttang (2021)
def find_root(x, power, epsilon=0.01):
"""
Find a y such that y**power = x (within epsilon x).
e.g. For x = 27 and power = 3 then y = 3 as 3**3 = 27.
i.e. the cubed root of 27 is 3.
Parameters
----------
x :int or float
Number we want the root of.
power: int
Root number e.g. square root, power = 2.
epsilon: int or float, default 0.01
Error tolerance for the answer.
Returns
-------
float or None
"""
# if x is negative and power even. No root exists
if x < 0 and power % 2 == 0:
return None
low = min(-1.0, x)
high = max(1.0, x)
ans = (high + low) / 2.0
# check if the answer is close enough
while abs(ans**power - x) >= epsilon:
if ans**power < x:
low = ans
else:
high = ans
ans = (high + low) / 2.0
return ans
if __name__ == "__main__":
# add the line of code here
pass
The built-in help()
function allows us to print out the docstring of a function.
You will also see a more complicated function called find_root
.
For now, it doesn't matter how it works, but the docstring does tell us what it does and requires a longer description.
Add a line of code to the bottom that prints out the docstring for find_root
by using the help()
function. Make sure you don't use any indentation so that the code isn't inside a function.
Think carefully, help()
is a function that prints out to the terminal and returns None
.
The Receive and Return Program
This is an example program from:
Dawson, M. (2010). Python programming for the absolute beginner, third edition (3rd ed.). Delmar Cengage Learning.
It demonstrates three variations of functions.
display()
receives one parameter message
, prints it out, and then does not have a return
statement. It will therefore return None
.
give_me_five()
receives no parameters and it returns the value 5
.
ask_yes_no()
receives one parameter question
and asks the user to answer "y"
or "n"
. It returns the user's response.
1. Complete Program
# Receive and Return
# Demonstrates parameters and return values
def display(message):
print(message)
def give_me_five():
return 5
def ask_yes_no(question):
"""Ask a yes or no question."""
print(question)
print("\nPlease enter 'y' or 'n': ")
response = None
while response not in ("y", "n"):
response = input().lower()
return response
if __name__ == "__main__":
# main
display("Here's a message for you.\n")
number = give_me_five()
print("Here's what I got from give_me_five():", number)
answer = ask_yes_no("\nDo you like Python? ")
print("\nThanks for entering:", answer)
input("\n\nPress the enter key to exit.")
2. The Function Specifications
# Receive and Return
# Demonstrates parameters and return values
def display(message):
"""Prints out the message.
Parameters
----------
message: str
Message to display.
"""
print(message)
def give_me_five():
"""Returns the value 5.
Returns
-------
int
Always returns 5
"""
return 5
def ask_yes_no(question):
"""Ask a yes or no question to the user and return the answer "y" or "n".
Parameters
----------
question: str
The question to ask the user.
Returns
-------
str
The response of the user. Either "y" or "n".
"""
print(question)
print("\nPlease enter 'y' or 'n': ")
response = None
while response not in ("y", "n"):
response = input().lower()
return response
OK, so our code now looks more verbose, but it is much clearer to someone else what the function does.
Sometimes functions are complicated and giving the person reading it a good specification is important.
How this is done can differ between projects and programmers, but in larger codebases, it is very important as programmers may come and go or hundreds or thousands of programmers may work on a single codebase.
Passing by Assignment
WARNING: This is really fundamental and can cause major bugs and errors if you don't understand it.
I strongly suggest that you paste the examples in Python Tutor and work through them.
If you are struggling to follow this then please speak to the instructors in the practicals.
Python differs from other programming languages in that everything is an object.
Some of these objects are immutable (cannot be changed) and some are mutable (can be changed).
Immutable and mutable types are common in most languages and how these are dealt with differs.
We are specifically talking about what Python does.
1. Immutable vs Mutable Types
1.2 Immutable Type
An immutable type is something that once created cannot be changed. Immutable types you have seen in Python are str
, float
, int
and bool
.
Consider the following example which uses int
. Here we also use the is
keyword to see if x
and y
are the same object in memory.
x = 1
y = x
print(x is y) # prints True. They are the same object in memory
y += 1
print(x) # prints 1
print(y) # prints 2
print(x is y) # prints False. They are now different objects in memory
The first print(x is y)
prints True
because x
and y
are both attached to the same immutable object 1
.
The reason the second print(x is y)
prints False
is because to start with x
and y
are attached to the immutable object 1
; however, when we add 1
to y
Python creates a new object and reassigns y
to 2
.
Now x
points to the object 1
in memory and y
points to the object 2
in memory. Different objects!
1.2 Mutable Type
Lists are mutable. If we do something similar to the above we get a very different result.
Consider the following example which uses list
. Here we also use the is
keyword to see if list_one
and list_two
are the same object in memory.
list_one = [1,2,3]
list_two = list_one
list_two.append(4)
print(list_one) # prints [1,2,3,4]
print(list_two) # prints [1,2,3,4]
print(list_one is list_two) # prints True. They are the same object in memory
When we assign list_two = list_one
both names are pointing to the same object [1,2,3]
in memory.
When we call the append method, we add 4
to the end of the list [1,2,3]
. Both list_one
and list_two
still point to the same list which is now [1,2,3,4]
. Hence print(list_one is list_two)
prints True
.
We can stop this from happening by using a copy.
Here we assign list_two
to a copy of the object [1,2,3]
in memory. This now means that list_one
points to one object [1,2,3]
and list_two
points to a separate object [1,2,3]
.
list_one = [1,2,3]
list_two = list_one.copy() # now we create a copy
list_two.append(4)
print(list_one) # prints [1,2,3]
print(list_two) # prints [1,2,3,4]
print(list_one is list_two) # prints False. They are now different objects in memory
The append()
method is now only called on the object attached to list_two
and therefore the object attached to list_one
is not updated. Hence print(list_one is list_two)
prints False
.
2. Passing by Assignment
When we pass arguments to functions, what we are actually doing is binding objects to the parameters of the function.
2.1 Passing an Immutable Type
n Python, a variable is just a name (label) that can be attached to an object. When we pass an object to a function, the function parameter is attached to the passed object
In the example below x
will be attached to whatever is passed into the function add_one()
. If you pass in an immutable object, as soon as you try to update the object it will create a new object to reflect the changes.
Remember immutable objects can't be changed.
The following example illustrates this. We pass in a number that is attached to num
and then add 1
. Because we are trying to change an immutable object, we have to create a new object. However, we don't return anything and reassign, so the original variable num_one
is not changed.
def add_one(num):
num += 1 # add one to the local variable x
if __name__ == "__name__":
num_one = 1
print(num_one) # prints 1
add_one(num_one)
print(num_one) # prints 1
We can reflect the changes by returning the result and reassigning it to num_one
.
def add_one(num):
num += 1
return num # return the new object created by adding 1
if __name__ == "__name__":
num_one = 1
print(num_one) # prints 1
num_one = add_one(num_one) # reassign x1 to the result of add_one()
print(num_one) # prints 2
2.2 Passing a Mutable Type
When you pass a mutable object to a function and change it you are updating the original object. That is doing something to the object passed to the function doesn't create a new copy, it operates on the same object!
Here is a simple example that adds 4
to the end of a list.
def func_one(items):
""" append 4 to the list. """
# append adds to the end of the list
items.append(4)
if __name__ == "__main__":
list_one = [1, 2, 3]
func_one(list_one) # call func_one with list_one
print(list_one) # prints [1,2,3,4] - this has been updated by the function call
The object attached to variable list_one
is first passed to func_one()
and the variable items
is attached to the object.
The function then adds 4
to the end of the list using the append()
method. What happens here is both list_one
and items
point to the mutable list. When it is updated, both are updated as they point to the same object.
Below are some more examples that illustrate passing a list to a function.
def func_one(items):
""" append 4 to the list. """
# append adds to the end of the list
items.append(4)
def func_two(items):
""" add the list and [4] """
# this creates a new object by adding the lists items and [4]
items = items + [4]
def func_three(items):
""" add the list and [4] """
# again this creates a new object by adding the lists items and [4]
items = items + [4]
# however this time we return the new object
return items
if __name__ == "__main__":
list_one = [1, 2, 3]
func_one(list_one) # call func_one with list_one
print(list_one) # prints [1,2,3,4] - this has been updated by the function call
list_one = [1, 2, 3]
func_two(list_one) # call func_two
print(list_one) # prints [1,2,3] - this has not been updated by the function call
list_one = [1, 2, 3]
list_two = func_three(list_one) # call func_three and bind to the variable list_two
print(list_two) # prints [1,2,3,4] - the new object created by the function call
func_one()
operates on the original list, this means you don't need to return anything. You could choose to return the list here, but there is no point.
func_two()
adds the original list and the list [4]
. This creates a new list but we do not return it and therefore we lose the new list.
func_three()
adds the original list and the list [4]
. This creates a new list and we return it. This means we can use it outside of our function.
I would experiment with these in Python Tutor to make sure you understand what is going on.
3.3 To Return or Not To Return
Generally, if you are operating on a mutable object and you don't need to keep a copy of the original then there is no need to return anything.
If you need the original list intact then you need to copy the original list before you operate on it and return the modified copy. See the next section.
3.4 Copy and Deep Copy
We will talk about these more in unit 6, but we can stop Python from updating the original list by using a copy of the list.
For now, we will only need the copy()
function as we will demonstrate on a list of numbers. Things get more complicated if we operate on a list that contains other mutable data types like a list of lists.
import copy
def func_four(items):
""" append 4 to a copy of the list. """
# create a copy of the list
new_list = copy.copy(items)
# append to the copy
new_list.append(4)
# return the copy
return new_list
if __name__ == "__main__":
list_one = [1,2,3]
list_two = func_four(list_one)
print(list_one) # prints [1,2,3]
print(list_two) # prints [1,2,3,4]
Note, we could have overwritten the original list by reassigning the return value. e.g.
list_one = [1,2,3]
print(list_one) # prints [1,2,3]
list_one = func_four(list_one)
print(list_one) # prints [1,2,3,4]
=== TASK ===
Create two functions. The first function update_list_item_one()
should take a list and a number and update the first item in the list with the number. It should operate on the original list. It does not require a return statement.
For example,
list_one = [1,2,3,4]
update_list_item_one(list_one, 0)
print(list_one) # prints out [0,2,3,4]
The second function new_list_item_one()
should create a copy of the list that is passed into the function (this will be a new object), update the first item in the new list and then return the new list.
For example,
list_one = [1,2,3,4]
new_list_item_one(list_one, 0)
print(list_one) # prints out [1,2,3,4]
does not update the original list, however,
list_one = [1,2,3,4]
list_one = new_list_item_one(list_one, 0) # bind the new object to list_one
print(list_one) # prints out [0,2,3,4]
reflects the changes made because the new object returned by new_list_item_one()
was reassigned to list_one
.
To get you started copy the following into a new Python file.
import copy
def update_list_item_one(items, x):
pass
def new_list_item_one(items, x):
pass
if __name__ == "__main__":
list_one = [1,2,3,4]
update_list_item_one(list_one, 0)
print(list_one) # should print out [0,2,3,4]
list_one = [1,2,3,4]
new_list_item_one(list_one, 0)
print(list_one) # should print out [1,2,3,4]
list_one = [1,2,3,4]
list_one = new_list_item_one(list_one, 0) # bind the new object to l
print(list_one) # should print out [0,2,3,4]