G3 Computing Textbook Chapter 04
G3 Computing Textbook Chapter 04
04 Programming
87
88
4.1 Introduction to Algorithms and Programming
To process data, computers follow a set of instructions. In this chapter, we will learn about how such
instructions may be provided using algorithms and programming.
4.1.1 Algorithms
algorithms. 2 1 7 4
6 2 8
8 7 6 4 3
ERMS
KEY T Rubik’s cube Sudoku puzzle
Algorithm
A set of step-by-step instructions for solving a Figure 4.1 Examples of games and
problem or performing a calculation applications involving algorithms
We use algorithms every day, although we may not see them explicitly written out. For example, the
following steps can be used to add two positive whole numbers:
Step 2: Let the current column be the right-most column of digits in the ones place.
Add all the digits (including any “carry-over” digits) in the current column.
Step 3:
If a digit is missing, treat it as 0. There is no “carry-over” digit for the right-most column.
89
If the result is greater than 9, write “1” at the top of the column immediately to the left
Step 4: of the current column as a “carry-over” digit.
Step 5: Write the last digit of the result at the bottom of the current column, below the line.
3982
row of digits below the line.
Final answer
The advantage of writing out an algorithm step by step is that these steps can be followed precisely by
anyone who can add digits to obtain the correct answer. This algorithm is also useful because it is general
and works for adding any two positive whole numbers.
In some secondary schools, the Mean Subject Grade (MSG) is used to measure a student’s performance
in school. The MSG is a number between 1 (best) and 9 (worst) calculated from the student’s rounded
percentage scores in each subject.
75–100 A1 1
Convert the rounded percentage
Step 1: scores into grade points using 70–74 A2 2
the table to the right.
65–69 B3 3
55–59 C5 5
50–54 C6 6
Step 3: Divide the result by 7.
45–49 D7 7
40–44 E8 8
Step 4: Round the result to two decimal
places. This is the student’s MSG.
Less than 40 F9 9
90
For instance, if the rounded percentage scores for the student’s seven subjects are as shown in the
illustration below, the corresponding MSG would be 3.14.
This algorithm is useful as anyone can follow the steps precisely to obtain the MSG of any student (as long
as the student takes exactly seven subjects).
63 74 73 56
ENGLISH MATH ADD MATH PHYSICS
80 50 72
BIOLOGY CHEMISTRY HISTORY
4.1.2 Programs
91
C Pascal
Scratch
R
Lisp
U
DID YO
O W ? In general, a computer’s processor cannot run source code directly. Instead,
QUICK
EC K 4. 1
CH
1. The following is an algorithm that can be performed on any digit from 1 to 9.
2. The following is an algorithm that can be performed on any positive whole number. (Make sure to use the
denary number system.)
Step 1: Calculate the difference between the input number and the input number with its digits reversed.
Step 2: Add up all the digits of the result (ignore the sign).
Step 3: If the result has only 1 digit, this is the output. Otherwise, go back to Step 2.
92
QUICK
EC K 4.1
CH
3. The following is an algorithm used for decode secret messages. It requires a secret message and a positive
whole number to be provided as inputs.
Step 1: Extract the first N letters of the secret message where N is the provided number.
Step 2: Append the extracted letters to the remaining letters of the secret message. This is the output.
For Step 1, the extracted 4 letters are MPLE. For Step 2, the extracted letters MPLE are appended to the remaining
letters EXA to obtain the output EXAMPLE.
LEARNING OUTCOMES
2.1.1 For a given problem, identify and remove unnecessary details to specify the
• inputs and the requirements for valid inputs
• outputs and the requirements for correct outputs
93
Input Processes Output
Similarly, the inputs to the problem of calculating the MSG for a student taking seven subjects can be
specified by treating each subject’s rounded percentage score as a separate input:
Input
63
ENGLISH • Score 1: rounded percentage score for subject #1; must be a whole
number between 0 and 100 inclusive
74
MATH • Score 2: rounded percentage score for subject #2; must be a whole
number between 0 and 100 inclusive
94
73
ADD MATH • Score 3: rounded percentage score for subject #3; must be a whole
number between 0 and 100 inclusive
56
PHYSICS • Score 4: rounded percentage score for subject #4; must be a whole
number between 0 and 100 inclusive
80
BIOLOGY • Score 5: rounded percentage score for subject #5; must be a whole
number between 0 and 100 inclusive
50
CHEMISTRY • Score 6: rounded percentage score for subject #6; must be a whole
number between 0 and 100 inclusive
72
HISTORY • Score 7: rounded percentage score for subject #7; must be a whole
number between 0 and 100 inclusive
Table 4.3 Specifying the inputs to the problem of calculating the MSG for seven subjects
However, this way of specifying the inputs can be quite repetitive. Since the requirements for each rounded
percentage score are the same, we can shorten the specification as follows:
80
673256 Input
BIOLOGY
IG ISRHY
STLO
EHNPHYSICS • Score: rounded percentage score for subject; must be a whole
number between 0 and 100 inclusive (provided seven times, once for
each subject)
Table 4.4 Simplified way of specifying the inputs to the problem of calculating the MSG
for seven subjects
95
4.2.2 Specifying the Outputs
Similarly, we can specify the output of the However, if we require the MSG values to be
problem of calculating the MSG for a student rounded to two decimal places, we can revise
taking seven subjects in this manner: the problem of calculating the MSG for seven
subjects accordingly:
OUTPUT OUTPUT
• MSG for the seven subjects • MSG for the seven subjects, rounded to
two decimal places
Table 4.6 Specifying the output of the problem of Table 4.7 Specifying the output of the problem of
calculating the MSG for seven subjects (original) calculating the MSG for seven subjects (rounded)
QUICK
E CK 4 .2
CH
1. Modify the input specifications for the addition problem in Table 4.2 so that both positive and negative whole
numbers are valid inputs. Determine whether the original addition algorithm is a solution to this modified
problem, and if not, explain why.
2. Alex wants to find the weight of the heaviest apple on display at the market.
Specify the input and output requirements for Alex’s problem.
3. Table 4.8 shows a possible set of input and output requirements for calculating the average score of a class test.
Suggest how it can be improved and why.
Table 4.8 Input and output requirements for the average-score problem
Input Output
• Title: name of the class test • The average score of the class test
• Scores: list of class test scores with each student’s
score included once
96
4.3 Installing Python
The programming language that you will learn in this textbook is Python. It can be downloaded for free
online.
ER MS
1 Graphical user interface
Python can be used with programs such
KEY T
as JupyterLab Desktop with menus and Command line interface
syntax highlighting to facilitate the writing A means of interacting with a program such
and running of Python source code. Many that commands are given as lines of text
alternatives to JupyterLab Desktop are
also available for download online. Such Graphical user interface
graphical user interfaces are most useful if A means of interacting with a program
the programmer wishes to use an “all-in-one” such that commands are given using visual
program to edit, run and test his or her source elements such as windows, icons, menus
code. and mouse pointers
2 Command line interface Syntax highlighting
Python can also be run using a command Displaying different parts of source code
line interface where commands are given differently (such as using different colours
as lines of text. This interface is most useful and fonts) in a text editor based on the
for combining the use of Python with other rules of the programming language to
programs or for automating the use of Phyton. make reading easier
In fact, graphical user interfaces for Python
typically make use of the command line
interface in the background.
Figure 4.8 shows what JupyterLab Desktop looks like when it is initially run on Windows:
97
Unlike traditional programming tools, programs in JupyterLab Desktop are written in files called notebooks
where executable code is embedded in formatted documents that may contain text, images and other
media. Figure 4.9 shows JupyterLab Desktop editing a notebook after the “New Notebook…” option is
selected.
ER MS
KEY T
By default, selecting “New Notebook…” creates a
new file named Untitled.ipynb. You can rename Kernel (Jupyter)
it by right-clicking on the notebook’s tab at the A program that runs the code embedded in
top or in the file browser on the left. You can also a notebook
create a new notebook using the File menu or the
plus sign on the tab bar. However, if you create a Notebook (Jupyter)
new notebook in this way, you will need to select A file where executable code is embedded
a kernel that runs the code you write. For this in formatted documents that may contain
textbook, any available Python kernel can be used. text, images and other media
Each notebook is made up of cells, and each cell may contain either executable code or formatted content
(e.g., text, images, media). The first cell in a new notebook is usually a code cell where you can enter source
code. For instance, try entering the following line of Python source code in the cell and pressing Shift-Enter:
Note that for the code examples given in this textbook, anything that you may need to type will be shown in
a cell with a gray background. To run the code, you will usually need to press Shift-Enter or click the “play”
button on the toolbar.
98
If there are no errors, you should see the phrase “Hello, World!” displayed on the screen immediately below
the code cell:
If there is an error, Python will display an error message, such as the example in Figure 4.12.
U
Note that the Python programming language
DID YO
is case-sensitive and that “Print” (with an
O W ?
upper-case “P”) is not the same as “print”
(with a lower-case “p”), as shown by the error K N
in Figure 4.12. It is typical to encounter such
JupyterLab Desktop is an example of an “integrated
errors when learning a new programming
development environment” (IDE) that makes it easier
language. You will learn more in Chapter 6
to use Python by providing an “all-in-one” program for
on what to do if a program does not work as
editing, running and testing source code.
intended.
Notebooks in Jupyter Desktop are stored in ipynb
You may occasionally run Python code that
format. The ipynb extension stands for “Interactive
that “hangs” JupyterLab so it refuses to
Python Notebook” and notebooks stored in this
run any other code. If this happens, select
format are compatible with web-based tools such as
“Restart Kernel…” from the Kernel menu and
Google Colab, Anaconda Cloud and JupyterLite that
click on “Restart” to confirm.
are freely available to use online.
Some programs in this textbook take up
Python programs may also be written in plain text
multiple lines. When entering such programs,
files with the py extension. To run such programs
take note of the difference between pressing
in Jupyter Desktop, you can either copy the py file’s
Enter and pressing Shift-Enter. Pressing Enter
contents into a code cell of a notebook and press Shift-
just inserts a new line without running the
Enter, or enter the following “magic” command in a
code. To run the code in a code cell, you need
cell and press Shift-Enter, replacing “example.py” with
to press Shift-Enter.
the name of the py file:
%run example.py
99
JupyterLab Desktop also has an option to show line numbers under the View menu. The following is an
example of a Python program with multiple lines of code:
1
2
U
DID YO
KNOW?
Instead of navigating menus to reach commands, you can activate
JupyterLab Desktop’s “command palette” by pressing Ctrl-Shift-C to search
for commands instead. For example, by pressing Ctrl-Shift-C and typing
“line”, you can toggle line numbers without needing to memorise where “Show line numbers” is found
in the menus.
JupyterLab Desktop will also attempt to auto-complete any partially written code when you press the
Tab key. This can be convenient for long names or when you cannot recall the exact name of something
in Python.
While JupyterLab Desktop can be convenient, a graphical user interface usually requires the use of a mouse,
making tasks difficult to automate without human intervention. A command line interface, on the other
hand, can be automated easily as commands are given using only lines of text. Use of Python’s command
line interface, however, is not covered in this textbook.
4.4 Comments
When you save a Python program in a file, you should also add comments to your program. Comments
make the source code easier for other people to understand but will be ignored by the Python interpreter.
In Python, comments start with the hash symbol (#) and finish when the line ends. Any text from the
hash symbol until the end of the line will be ignored by the interpreter. For example, Figure 4.15 shows
hello_world.ipynb with some additional comments:
100
Even with the addition of comments, this program still behaves
exactly like the original program, as shown in Figure 4.16:
ER MS
KEY T
Comment
A piece of source code that
explains the program but is
ignored by Python
LEARNING OUTCOMES
2.3.2 Use literals to represent values directly in code without using a variable.
Computers are designed to store and read values quickly and reliably. To work with values in Python, we
use literals and variables.
4.5.1 Literals
ER MS
KEY T
A literal is a value that is represented directly in
source code. For instance, the number 2024 can be
represented by writing out its digits in source code:
Literal
A value that is represented directly in
source code
2024
Syntax
Figure 4.17 Example of a literal
Rules that determine how the words
and symbols in a valid instruction are
arranged Different types of values have different rules or syntax
for how literals must be written. For instance, text
literals must be enclosed in either single or double
quotes:
101
4.5.2 Variables
Literals are useful for representing values that are relatively short and do not need to be changed by users
of the program. Literal values also do not have names, so their purpose may not be obvious and they cannot
be easily reused or manipulated by other parts of the program.
In contrast, variables allow us to associate meaningful names to values. Literals are often used to provide
the initial values for variables, after which they can be changed using the variable while the program is
running.
A variable is initialised when a value is first stored in it. It is usually a good practice to initialise a variable
as soon as there is a value to be assigned to it.
• Can contain the upper-case and lower-case letters A through Z and the underscore (_)
• Can contain the digits 0 through 9 but not as the first character
• Cannot contain spaces or special symbols (e.g., “!”, “@”, “#”, “$”)
• Cannot be any of the reserved words (or keywords) in Figure 4.20 that have special meanings in
Python
• Are case-sensitive
102
ER MS
KEY T Valid Invalid
Explanation
identifiers identifiers
Identifier
A sequence of characters that sum_of_scores sum-of-scores Identifiers cannot contain the
follows certain rules and can be hyphen character “-”
used as a variable name
my_class class class is a reserved word
Initialise
BOX_SIZE size_correct? Identifiers cannot contain the
To store or assign a value to a
question mark character “?”
variable for the first time
test_score_3 3rd_test_score The first character of an identifier
Keyword cannot be any of the digits “0”
A word that has a special to “9”
meaning in a programming
language and cannot be used as oneword two words Identifiers cannot contain any
an identifier spaces
year = 2024
46547440
48 -12
Figure 4.24 Example of using the id() function
56 2025
Of course, the memory address that is printed out
64 1965 will vary from computer to computer depending
on which memory addresses are available to
72 “Hello, World!” Python. Do not be alarmed if your computer does
not produce the same output as your friend’s!
103
We call the name, year, a variable because we
can change (i.e., vary) the value that is assigned
ER MS
to it while the program is running. KEY T
year = 2024 Variable
year = 2025 A name with an associated value that can be changed
year = 2026 while the program is running
year = 2027
Expression
Figure 4.24 Changing the value assigned to the Code that produces a value
variable year
72 “Hello, World!”
total = 1 + 2
Figure 4.25 Changing the assigned value of the Figure 4.26 Assigning the result of a calculation
variable year from 2024 to 2027 to the variable total
104
If we take year_1 and assign it to year_2, we can Addresses Memory contents
imagine the sticky note for year_2 being moved
over to the location of the sticky note for year_1, 24 2027
so both year_1 and year_2 now refer to the same
value. This is illustrated in Figure 4.29 and Figure 32 2026
4.30:
_2
year year_1 40 2024
year_2 = year_1 48 -12
56 2025
Figure 4.29 year_1 and year_2 now refer to the
same value
64 1965
72 “Hello, World!”
Figure 4.30 year_1 and year_2 now refer to the same value
We can retrieve or “read” the value assigned to a variable by using the variable’s name. If a value was
previously assigned to a variable with the given name, Python will use the value that was assigned in place
of the variable name.
However, if the name year does not correspond to any variable that was used or defined previously, Python
will produce an error message instead:
105
4.5.2.3 Assigning and Reading Values Together
CODE: OUTPUT:
As mentioned previously, we can take the value
from one variable, manipulate it, and then assign
it to another variable. For example, the variable year = 2024 2024
next_year will be assigned a value of 2025 after the next_year = year + 1
source code in Figure 4.34 is run: print(year)
print(next_year) 2025
CODE: OUTPUT:
We can also take the value from a variable, manipulate
it, and then assign it back to the same variable. This year = 2024 2025
is because Python will always fully calculate the year = year + 1
value of the right-hand side before assigning it to the print(year)
variable on the left-hand side:
Sometimes we need to keep track of the fact that a value is missing or that there is no suitable value to be
assigned to a variable. For instance, some problems may have optional inputs. Since Python will produce
an error message if we try to retrieve a variable that has not been defined, we may still wish to create the
variable but assign it a value that indicates the value is missing. For such cases, Python provides a special
None value that can be used instead:
CODE: OUTPUT:
106
4.5.2.5 Deleting Variables
Instead of using None, it is occasionally useful to delete a variable completely so that using the variable
name produces an error. This can be done using the del keyword like this:
QUICK 5
C K 4.
CHE
1. Examine the following program:
hours = 6
hours = hours + 12
print(hours)
When Python runs the program, it performs the following steps, but not in the order presented:
A) Displays the value assigned to hours on the screen
B) Calculates the sum of hours and 12 to obtain 6 + 12 = 18
C) Assigns 6 to hours
D) Assigns 18 to hours
107
QUICK 5
C K 4.
CHE
2. For each of the following programs, identify all the variables and predict the output:
a) num_people = 6
num_chairs = 3
shortage = num_people - num_chairs
print(shortage)
b) week_length = 7
today = 4
today = today + week_length
print(today)
c) cost = 10
cost = cost + cost
savings = 250
savings = savings - cost
print(savings)
4.6.1 Functions
108
MS U
KEY T
ER DID YO
N O W ?
Argument
An additional value that can be
K You may be familiar with
“argument” to mean “having a
supplied as input to a function call verbal disagreement” or “reasons
for supporting an idea”, but in mathematics the word also
Function means “quantity from which another quantity can be
A set of instructions assigned to a deduced or calculated”. The use of “argument” here is based
name that can be used again later on this mathematical definition.
Function call
The process of assigning arguments
to new variables and running the
instructions assigned to a function
ER MS
KEY T
result = max(3, -2, 1, 5, 0)
print(result)
5
Nested function call
Figure 4.42 Using the max() function Using the return value of one function call as
an argument for another function call
In this example, the return value of max() is the
largest argument 5. Hence, the entire function Return value
call “max(3, -2, 1, 5, 0)” is treated as the The output of a function, or the value that a
number 5, and the number 5 is in turn assigned to function call is treated as, after the instructions
the variable result. assigned to the function are completed
109
U
DID YO
N O W ? Besides identifying arguments based on position (first argument, second
K argument, etc.), arguments can also be identified by name. These are called
“keyword arguments”. For example, the print() function accepts multiple
arguments and outputs the arguments separated using spaces by default:
print(1, 2, 3, 4)
1 2 3 4
Figure 4.44 The print() function separates arguments using spaces by default
However, you can specify a different separator using the sep keyword argument:
print(1, 2, 3, 4, sep=',')
1,2,3,4
Figure 4.45 The print() function accepts a sep keyword argument to specify a different separator
Some features of Python, such as using a different separator as shown above, can only be accessed using
keyword arguments.
To learn about the arguments (including keyword arguments) that a function accepts, enter a question
mark “?” before or after the function name without any parentheses and press Shift-Enter. For example,
to learn more about print(), you can enter “?print” into a code cell and press Shift-Enter.
4.6.2 Methods
A method is a function that is associated with a value. To use a method, type a value that is associated with
the method, followed by a period (.), followed by the name of the method, and followed by zero or more
comma-separated arguments enclosed within parentheses (see Figure 4.46). This is sometimes called
“dot syntax”. Just like in calling functions, arguments are assigned to new variables before running the
method’s instructions.
ER MS
Syntax 4.4 Method Call KEY T
value.name_of_method() Method
A function that is associated with a value
value.name_of_method(argument_1)
value.name_of_method(argument_1, Object
A value with associated data and/or methods
argument_2)
110
Python provides some additional functions through
methods. For instance, in the following program, message = "Hello, world!"
the upper() method associated with the message new_message = message.upper()
variable returns a copy of the message converted print(new_message)
to uppercase:
HELLO, WORLD!
4.6.3 Operators
You have already seen operators used in many of Similarly, whenever we assign a value to a variable, we
our previous examples. For instance, the addition are actually using the assignment operator (=), which
operator (+) takes two values, one to the left and one takes the value calculated on the right and stores it in
to the right, then returns the sum of the two values. the variable on the left:
3
Figure 4.48 Using the addition operator Figure 4.49 Using the assignment operator
Some operators take in only one operand on the right, Binary operator
such as the negation operator (-) shown in Figure 4.50. An operator that takes in exactly two values
These are called unary operators.
Operand
A value that is manipulated by an operator
value = 65
Operator
A symbol for performing instructions like a
print(-value)
function but using a different syntax
-65
Unary operator
Figure 4.50 Using the negation operator An operator that takes in exactly one value
111
QUICK
E CK 4 .6
CH
1. Identify whether each of the following code snippets shows a function call, a method call or the use of an
operator:
a) print(name, contact)
b) len(description)
c) numbers.append(3)
d) cost / count
e) f.readline()
f) -result
print(abs(min(-4, 0, 1))
State the order in which the abs(), min() and print() function calls are performed in the program.
(Hint: an inner function call must be completed first so its return value can be used as an argument for the outer
function call.)
print(abs(min(-4, 0, 1) + 1))
abs(), min() and print() are built-in Python functions. The + operator is used to perform an addition
operation.
State the order in which the abs(), min() and print() function calls and the addition operation are performed in
the program.
112
4.7 Data Types
Recall that in a computer data is stored as bits and bytes. The same sequence of bytes can represent different
types of data such as numbers or text, depending on how the bytes are interpreted. If data is interpreted wrongly,
such as treating numbers as text or vice versa, it can lead to unintended behaviour.
To ensure that correct interpretations are used, Python associates every value with a data type. Some built-in
data types in Python are shown in Table 4.12:
Note that there are two different data types for numbers in Python, namely int and float, depending on
whether it is necessary to represent non-whole numbers (i.e., real numbers). In addition, the data type for text
in Python is called str.
These data types are discussed in further detail from sections 4.9 to 4.13.
113
4.8 Input and Output
LEARNING OUTCOMES
2.3.3 Use the built-in functions: input() and print(), to perform interactive input/output using
the keyboard and screen.
Note how input() outputs the given prompt “Enter your name: ” then waits for the user to provide some
text using the keyboard. The program will continue to wait until the user hits the enter key, then the entire
input() function call is treated as the text that was entered just before pressing the enter key.
Table 4.13 summarises the input() and print() functions that are used to perform interactive input/
output using the keyboard and screen.
s = input('Enter s:')
print(s)
Enter s: Example
Example
114
Function Argument(s) Description Examples
Note that input() returns what the user entered using the text data type str. If you need to perform
calculations using the input, an additional step is needed to convert the str into a number data type (i.e.,
an int or float).
4.8.2 Files
The input() and print() functions have some disadvantages. For instance, the input() function typically
requires the user to re-enter their responses each time it is called, which can be time-consuming. Similarly, the
output of print() is saved in the notebook but can be inconvenient to extract for use in other programs, such
as a spreadsheet. To address these disadvantages, an alternative approach is to use files on your computer for
input and output.
We will cover how to use files for input and output when we discuss the with statement later in section 4.16.
QUICK
EC K 4. 8
CH
1. Write a program that uses input() and print() to solve the following problem:
Input Output
• name: name of the user • name displayed 3 times on 3
separate lines
Table 4.14 Input and output requirements for
multiple-line name printing problem
2. Write a program that uses input() and print() to solve the following problem:
Input Output
• name: name of the user • name displayed 3 times on a
single line, separated by spaces
115
4.9 Booleans
LEARNING OUTCOMES
2.3.6 Use Boolean values with the operators: or, and, not.
A Boolean (or bool) is a special data type used to represent logic-related data. Unlike other data types,
there are only two valid bool values: True and False.
The literals for bool are also written as True and False:
seawater_is_salty = True
peanuts_are_nuts = False
print(seawater_is_salty)
print(peanuts_are_nuts)
True
False
Figure 4.52 Examples of valid bool literals
Note that both literals are case-sensitive and that lowercase true and false will produce errors instead:
116
4.9.2 Boolean Operators
4.9.2.1 Equality
Python produces a bool value when we use the following equality operators to compare whether any two
values are equal:
print(2024 == '2024')
False
print(2024 != '2024')
True
117
4.9.2.2 Logical
print(not False)
True
118
The statement on Line 3 translates very naturally into English as “we can leave home if it is not raining or if it is
raining and we have an umbrella”. Lines 1 and 2 specify the specific scenario where is_raining is True and
have_umbrella is False. The output is thus False as leaving home in this scenario would probably get us
drenched!
U
DID YO
N O W ?
K We can use what we learned in Chapter 3 on the properties of AND and OR to
simplify Line 3. Converting the Python code into a Boolean statement, we get:
QUICK
EC K 4.89
CH
1. State whether each of the expressions below evaluate to True or False.
a) not True b) not False c) 1 == 1 d) 1 == 1.0 e) 1 == '1' f) 0 != 0 g) 0 != 1
2. At a water treatment plant, an alarm rings if the water level in a tank is full and the drainage pipe is closed,
unless an override switch is turned on.
It is given that tank_full is a bool equal to whether the tank is full, drainage_open is a bool equal to whether the
drainage pipe is open and override_on is a bool equal to whether the override switch is turned on.
Which of the following Boolean expressions is equal to whether the alarm is ringing?
a) (tank_full and drainage_open) or not override_on
b) (tank_full and drainage_open) and override_on
c) (tank_full and not drainage_open) and not override_on
d) (tank_full and not drainage_open) or override_on
3. A programmer wishes to obtain a Boolean that is True only if an int variable, num, is either -1 or any value other
than 10 or 20.
Fill in the blanks for the required Boolean expression below using “and” and “or” only.
119
4.10 Integers and Floating-Point Numbers
LEARNING OUTCOMES
2.3.7 Use integer and floating-point values with appropriate operators and built-in functions
(limited to those mentioned in the Quick Reference Guide) to perform:
• Addition, subtraction, multiplication, division, modulo and exponentiation
• Rounding (normal, up, down, towards zero)
• Calculation of square roots
• Generation of ranges
• Generation of random integers / floats
• Conversion to and from strings
2.3.5 Use the import command to load and make additional variables and functions available
for use.
Integers in Python have no limit, positive or negative, in the whole numbers they can represent.
2024.0
-19.65
6.02E23
-2.03e3
120
Due to the format in which they are stored, floats
ERMS
are not especially accurate, as shown in Figure 4.59:
KEY T
print(3 * 0.1)
Floating-point number (float)
0.30000000000000004
A data type to represent real numbers that
Figure 4.59 Example of an inaccurate may contain a fractional part
floating-point calculation
Integer (int)
This inaccuracy may cause surprising results when A data type representing whole numbers
testing for equality, as mentioned previously in
section 4.9.2.1.
U
DID YO
KNOW? The most positive and most negative limits that can be represented using a
float are approximately 1.79 × 10308 and -1.79 × 10308 respectively. Beyond
these limits, the float will get converted into the special value of infinity (represented by inf or -inf).
print(1.79e308)
1.79e+308
print(1.80e308)
inf
print(-1.79e308)
-1.79e+308
print(-1.80e308)
-inf
There is also a limit for how small a positive non-zero number can be. If a number gets too close to 0, it
will get converted to 0. This is called “underflow”.
print(1e-323)
1e-323
print(1e-324)
0.0
You do not need to memorise these limits. However, it is useful to know they exist in case you run into
them.
121
4.10.3 Mathematical Operators
Mathematical operators are commonly used to operate on int and float data types.
4.10.3.1 Comparison
Python produces a bool value when we use any of the following comparison operators:
< Less than Two numbers Returns True if the print(2024 < 2030)
number on the left is
less than the number True
on the right and False print(2024 < 2024)
otherwise.
False
<= Less than or Two numbers Returns True if the print(2024 <= 2030)
equal to number on the left is
less than or equal to the True
number on the right and print(2024 <= 2024)
False otherwise.
False
> Greater Two numbers Returns True if the print(2030 > 2024)
than number on the left
is greater than the True
number on the right and print(2024 > 2024)
False otherwise.
False
>= Greater Two numbers Returns True if the print(2030 >= 2024)
than or number on the left is
equal to greater than or equal to True
the number on the right print(2024 >= 2024)
and False otherwise.
True
122
Note that ints can be compared to floats and vice versa using these operators as they both represent
numbers. Otherwise, in general these comparison operators require both operands to have the same data
type.
4.10.3.2 Arithmetic
You have already seen some of the operators used for performing calculations or arithmetic, as shown in
Table 4.19:
print(9.0 + 4)
13.0
print(-(9.0 + 4))
-13.0
print(9.0 - 4)
5.0
print(9.0 * 4)
36.0
123
Operator Name Operand(s) Description Examples
print(9.0 / 4)
2.25
1.0
6561.0
124
The modulus operator (%) is particularly useful for testing whether a number is odd or even. If x % 2 is 1,
then x is odd. Otherwise, x % 2 is 0 and x must be even.
We often need to use a variable for a calculation and then update the variable with the result. For
convenience, Python provides additional or “augmented” assignment operators to help save some typing
when performing this task.
-= x -= a x = x - a
*= x *= a x = x * a
/= x /= a x = x / a
//= x //= a x = x // a
%= x %= a x = x % a
**= x **= a x = x ** a
Table 4.20 Assignment operators that work with int and float data types
Python provides a wide variety of built-in functions to perform various mathematical operations. Table
4.21 summarises the most common mathematical functions in Python.
2024
print(int(-3.9))
-3
125
Operator Name Description Examples
2.024
126
Operator Name Description Examples
-2
We will discuss more details about round() and other functions that perform rounding later in section
4.10.6.
The int() and float() functions are also important for converting strs to numbers. Figure 4.64 shows
that while "123" and int("123") both appear as 123 when printed, "123" is text and cannot be used to
perform calculations while int("123") is the actual number 123 and can be used to perform calculations.
Figure 4.64 Using the int() function to convert a str into an int
127
As mentioned in section 4.8.1, the
input() function returns a str. If we
want to the use the input as an int
or float for calculations, we need
to use either int() and float()
accordingly. For instance, Figure 4.65 Enter total mass (kg): 900.8
shows a program that accepts user
Enter number of beams: 40
input to calculate the average mass of
Average mass of beam (kg): 22.52
a steel beam given the total mass and
number of steel beams:
Figure 4.65 Converting user input into numbers using
int() and float()
U
DID YO
N O W ?
K There are many Python modules available for use, both included with Python
and downloadable online as “packages”. Some common Python packages
that you may find online are shown in Table 4.22.
128
The module name for each of these packages is usually the same as the package name. In Jupyter
Desktop, packages can be conveniently downloaded and installed using the Python package manager
pip, as shown in Figure 4.66:
Alternatively, we may create our own modules by simply assigning variables and defining functions we
want to reuse in a py file in the same folder as our notebook; its filename (without the extension) will be
used as the module name.
To access and use the variables and/or functions within the module, we use the same “dot syntax” as accessing
the methods of an object:
name_of_module.name_of_function()
name_of_module.name_of_function(argument_1)
name_of_module.name_of_function(argument_1, argument_2)
129
U
DID YO
?
KNO W
Python also comes with a module named turtle that is
useful for learning programming, drawing graphics, and
creating simple games. The module is inspired by the Logo
programming language and is meant to simulate a robotic
drawing toy called a “turtle”, as shown in Figure 4.71. The
turtle robot and Logo were both widely used for introducing
children to programming.
Figure 4.71 Example of a robot “turtle”
In the turtle module, there are commands for moving the import turtle
turtle forward and turning the turtle left or right by a given turtle.forward(100)
turtle.right(90)
number of degrees. For example, the following program turtle.forward(100)
instructs the turtle to draw a square in a separate window: turtle.right(90)
turtle.forward(100)
turtle.right(90)
turtle.forward(100)
turtle.done()
(Important: After running the cell, you must close the turtle
window to continue using JupyterLab. If you encounter a
Terminator error trying to run turtle commands after
closing the window, just try running the cell again.)
We will use the turtle module to provide some optional
examples later in this chapter.
Figure 4.73 Square drawn using the turtle module
The syntax for import statements is summarised in To do this, we can make use of the from and
Figure 4.74: import keywords. For instance, Figure 4.75 shows
how to import just the sqrt() function from the
math module. Note that when using this form of
Syntax 4.6 import Statement the import statement, we access the function or
variable name directly and it is not necessary to use
import name_of_module the “dot syntax” described previously in Figure 4.68:
import name_of_module as new_name
from math import sqrt
result = sqrt(9)
Figure 4.74 Syntax for import statement print(result)
3.0
Python modules may contain a large number of
Figure 4.75 Importing just the sqrt()
functions and variables. Sometimes, it is simpler function of the math module
to just import specific functions and variables that
need to be used, rather than the entire module.
130
Just like with the first form of the from math import sqrt as my_square_root
import statement, we can use a result = my_square_root(9)
different name for the imported print(result)
variable or function using the as
keyword, as shown in Figure 4.76: 3.0
Figure 4.76 Importing just the sqrt() function of the math module
using the name my_square_root
The syntax for this alternate form of import statements is summarised in Figure 4.77:
After importing the math module, the following functions will become available:
−2
−3
131
Using any of these functions without importing the math module will result in an error. In the example below,
Python does not understand what the name “math” means as it does not correspond to an existing variable or
loaded module:
U
DID YO
?
KN OW We can use the built-in dir() function to list all the function and variable
names in a module. For instance, Figure 4.79 shows how to list everything in
the math module:
import math
print(dir(math))
Figure 4.79 Listing all the function and variable names in the math module
In this textbook, we will focus on using modules to access functions and not variables. However, from
the list you should see that some important mathematical values such e and π are available in the math
module as math.e and math.pi respectively.
A number of functions such as math.ceil(), math.floor(), round() and math.trunc() are similar
in that they can convert floats to ints. This conversion is performed by rounding floats such that the
resulting ints are close in value to the original floats. However, each function uses a different rounding
method. Understanding the difference between these functions is important, because using the correct
function can greatly reduce the number of steps needed for a solution.
For round(), the argument is always rounded to the nearest whole number. However, an argument
that is exactly halfway between two integers (such as 0.5) may be rounded either up or down. Table 4.24
demonstrates how round() can appear to give unpredictable results – notice that 0.5 is rounded down
while 1.5 is rounded up.
132
x round(x) Explaination
−1.0 −1 Exact
0.0 0 Exact
1.0 1 Exact
Table 4.24 Behaviour of the round() function for numbers around zero
print(round(-1.0))
-1
print(round(0.5))
print(round(0.75))
print(round(1.5))
U
DID YO
?
KNO W
The round() function only appears to give unpredictable results. In reality, round() will always return the
even integer if its argument is exactly halfway between two integers. For example, round(0.5) will return
0 and round(1.5) will return 2.
133
On the other hand, math.ceil() always rounds up. Take note that this means a negative non-integer
argument will end up becoming less negative.
math.
x Explanation
ceil(x)
−1.0 −1 Exact
−0.75 0 Rounded up
−0.5 0 Rounded up
−0.25 0 Rounded up
import math
0.0 0 Exact print(math.ceil(-1.0))
-1
0.25 1 Rounded up
print(math.ceil(0.5))
0.5 1 Rounded up
1
0.75 1 Rounded up
print(math.ceil(0.75))
1.0 1 Exact
1
Table 4.25 Behaviour of the math.ceil() function for numbers Figure 4.81 Using the math.ceil() function
around zero
Conversely, math.floor() always rounds down. Take note that this means a negative non-integer
argument will end up becoming more negative.
math.
x Explanation
floor(x)
-1.0 -1 Exact
-1
0.25 0 Rounded down
print(math.floor(0.5))
0.5 0 Rounded down
0
0.75 0 Rounded down
print(math.floor(0.75))
1.0 1 Exact
0
Table 4.26 Behaviour of the math.floor() function for Figure 4.82 Using the math.floor() function
numbers around zero
134
Finally, math.trunc() drops the digits of the argument after the decimal point (i.e., truncating the
argument). This is equivalent to rounding towards zero.
math.
x Explanation
trunc(x)
-1.0 -1 Exact
0
0.25 0 Rounded down towards 0
print(math.trunc(0.75))
0.5 0 Rounded down towards 0
0
0.75 0 Rounded down towards 0
print(math.trunc(1.0))
1.0 1 Exact
1
Table 4.27 Behaviour of the math.trunc() function for Figure 4.83 Using the math.trunc() function
numbers around zero
Many computer games often require the computer to make a random choice or to generate a random
number. To do this, Python provides a module named random:
print(random.randint(2, 7))
2
Note: The output of the above
examples is random.
135
Function Argument(s) Description Example
0.06804711169732913
print(random.random())
0.5070243505930644
Note: The output of the above
examples is random.
U
DID YO
KNOW? Run the following program that uses the turtle module to draw an artwork
made up of four randomly-sized squares:
Notice that the code is rather repetitive. We will see how to shorten it later.
136
QUICK 10
C K 4.
CHE
1. State whether each of the expressions below evaluate to True or False.
a) 0 > 1 b) 1 > 0 c) 2 > 0 and 2 < 1 d) 2 > 0 or 2 < 1
product = 10.49 * -2
converted_to_int = int(product)
print(converted_to_int)
remainder = 17 % 6
print(remainder)
x = 1
x += 2
x *= 2
x -= 2
print(x)
5. For each value of x below, predict the result of the respective rounding functions. Check your answers by
printing the actual return values in Python.
math.floor(x)
round(x)
math.trunc(x)
6. When two six-sided dice are rolled, the resulting sum can range from 2 to 12 (inclusive) with 7 being the most
likely.
Write a program to simulate rolling two six-sided dice and output the resulting sum.
7. The area of a triangle with base b and height h is given by the formula A=1/2bh.
Input Output
• b: base of triangle • Area of triangle obtained by the formula
• h: height of triangle A=1/2bh
Table 4.30 Input and output requirements for triangle area calculation problem
137
QUICK . 10
K 4
C HEC
7. The hypotenuse c of a right-angled triangle with other two sides of lengths a and b is given by Pythagoras’s
formula c = √a2+b2.
Input Output
• a: length of triangle’s side • Length of triangle’s hypotenuse obtained by
• b: length of triangle’s side Pythagoras’s formula c = √a2+b2, rounded to 2
decimal places
Table 4.31 Input and output requirements for hypotenuse calculation problem
4.11 Strings
LEARNING OUTCOMES
2.3.8 Use string values with appropriate operators, built-in functions and methods (limited to
those mentioned in the Quick Reference Guide) to perform:
• Concatenation and repetition
• Extraction of characters and substrings (i.e., indexing and slicing)
• Conversion to upper and/or lower case
• Conversion of single characters to and from ASCII
• Testing of whether characters are letters only, lower-case letters only, upper-case letters
only, digits only, spaces only and/or alphanumeric
• Testing of whether the string contains a substring
• Testing of whether the string starts with and/or ends with a substring
• Searching for the location of a substring
• Splitting of string into list of substrings based on either whitespace or a given delimiter
• Calculation of length
• Output formatting
ER MS
A string (or str) is used to represent text in Python.
The name “string” is shorthand for the phrase
KEY T
“string of characters/letters”.
String (str)
A data type to represent text as a sequence
of characters or symbols
138
4.11.1 String Literals
The most common form of a str literal consists of text enclosed by matching single-quotes (') or double-
quotes ("):
Some strs may contain characters that are either difficult to type out as part of a literal or would cause a
syntax error if not treated specially. To include such characters, we need to input them using the backslash
(\) key and special escape codes.
Some common escape codes for string literals are shown below.
Excape Meaning
code
ER MS
KEY T
\\ Backslash (\)
Escape code
\' Single-quote (‘) A sequence of characters used to input
characters that are either difficult to type out
\" Double-quote (“) as part of a literal or would cause a syntax
error if not treated specially
\n Newline character
\t Tab character
139
The last escape code “\” The \n escape code breaks
print('L ine 1\nLine 2')
represents a backslash that is the the string up into two lines.
For string literals that contain a large number of newlines, Python supports “triple-quoted strings” where
the text is surrounded by matching groups of three single-quotes or double-quotes. These string literals
can have multiple lines and the newlines are preserved, as shown in Figure 4.88:
140
U
DID YO
N O W ?
K For string literals that contain a large number of backslashes, Python supports
“raw strings” that are prefixed by “r” or “R”. These string literals generally
leave escape codes unchanged and treat the backslash as a normal character,
as shown in Figure 4.89.
print(r"Escape \n codes \' do \" nothing \\ here!")
Escape \n codes \' do \" nothing \\ here!
Figure 4.89 Example of raw string
This format is especially useful for specifying file or folder locations on Windows where backslashes are
used to separate folder names (e.g., C:\Users\Example\Desktop).
Finally, we often want to create strings that seamlessly include other values. Python provides an easy way
to do this using “formatted string literals” that are prefixed by “f” or “F”. They are also called “f-strings”.
Each set of curly braces {} in the literal must contain a Python expression and is called a replacement field,
or just field for short. When Python creates a string from the literal, all the expressions are evaluated, and
each field is replaced by the value of the expression it contains. This can be used to easily create strings that
include the value of variables or simple calculations, as shown in Figure 4.90:
year = 2024
print(f'The year is {year}')
print(f"The next year is {year + 1}")
To “escape” the curly braces so that they are not treated as a field, use double braces so that it looks like
“{{“ or ”}}”.
year = 2024
print(f'Using {{year}} in a f-string produces {year}')
The syntax for replacement fields in formatted string literals is summarised in Figure 4.92:
141
ER MS
Values can also be included in strings using type
casting and string concatenation (described in
KEY T
section 4.11.2) or the str.format() method Replacement field
(described in section 4.11.5). A placeholder denoted by a set of curly braces
that will be replaced with a value
4.11.2.1 Comparison
The comparison operators in Table 4.18 for ints and floats also work with strs, as shown in Table
4.33. When comparing strings, the results are determined by comparing the ASCII codes of the first pair of
differing characters (i.e., ASCII order). If no difference is found, the comparison is based on the lengths of
the strings.
False
True
False
Note: < also works with
numbers and lists. See Table
4.18 and Table 4.41.
True
True
Note: <= also works with
numbers and lists. See Table
4.18 and Table 4.41.
142
Operator Name Operand(s) Description Example
False
False
Note: > also works with
numbers and lists. See Table
4.18 and Table 4.41.
False
True
Note: <= also works with
numbers and lists. See Table
4.18 and Table 4.41.
In programming, we often want to arrange strings in alphabetical order. However, alphabetical order does
not always correspond to ASCII order. For instance, in ASCII all upper-case letters have lower ASCII codes
than lower-case letters and thus upper-case ‘Z’ will be “less than” lower-case ‘a’ using the operators in
Table 4.33.
To achieve alphabetical ordering, we will need to process the strings beforehand to remove all non-letters
and to convert all letters to either upper or lower-case.
143
4.11.2.2 Concatenation and Repetition
Sometimes, we may want to join two strs together or repeat a str multiple times. To perform these tasks,
Python reuses the + and * arithmetic operators:
202420242024
However, note that the + and * operators are exceptions and that strs do not work with arithmetic
operators in general. For instance, trying to subtract one str from another str will result in an error.
in identifier
144
Like f-strings, we can use the + operator to produce strings that seamlessly include other values, including
non-strings such as ints and floats. However, one complication is that to perform concatenation both
operands must be strs or there will be an error:
The solution is to type cast the non-strings into strs, then use the + operator to concatenate the required
strs together, as shown in Figure 4.95:
145
If we want to extract a subset of
characters from a str (instead of just a subject_name[3:6]
If the start index is left out, it is treated as 0. If the stop index is left out, it is treated as the length of the str.
Leaving out the stop index usually has the same meaning as making sure all remaining characters of the
str are included in the return value.
Although not often used, the slicing operator can also accept an optional third int value in the form
sequence_name[a:b:c], where a and b are the start and stop indices while c is called the step. This
extracts the sequence of values or characters positioned from the start index up to but not including the
stop index in increments of the step.
subject_name[2:8.2]
+2 +2
0 1 2 3 4 5 6 7 8
As before, if the start index is left out, it is treated as 0. If the stop index is left out, it is treated as the length
of the str or list. If the step is left out, it is treated as 1.
print("Computing"[:8:2])
Cmui
print("Computing"[2::2])
muig
print("Computing"[2:8:])
mputin
Figure 4.100 Using the slicing operator with the start index,
stop index or step left out
146
The indexing and slicing operators are summarised in Table 4.35:
print(s[3])
print(s[-1])
g
Note: The indexing operator
also works with lists and
dicts. See Table 4.43 and
Table 4.46.
puting
Note: The slicing operator also
works with lists. See Table
4.43.
print(s[2:8:])
mputin
147
Using the indexing operator on the left-hand side of an assignment and trying to change the characters in
a str will result in an error. This is because (like int and float) str is an immutable type, meaning that
once a str is created, its contents cannot be changed. For instance, suppose we wish to change the first
character of “Computing” to a K so that the string becomes “Komputing”. Trying to do so directly results in
an error:
The solution is to create a new str by concatenating pieces from the original str:
subject_name = "Computing"
subject_name = 'K' + subject_name[1:]
print(subject_name)
Komputing
Figure 4.102 Creating a new str to change the first letter of subject_name
The second line in Figure 4.102 creates a new str value of “Komputing” and assigns it back to subject_name.
The previous str value of “Computing” is no longer referred to. Note that a new str has been created. This
is not the same as changing characters in an existing str, which is not allowed by Python.
Another common task in solving problems is accessing the last value in a str. If the index for an indexing
operator is negative, Python treats it as a position counting from the opposite end of the sequence, treating
the last character as position −1, the second-to-last character as position −2, and so on:
subject_name = "Computing"
letter = subject_name[-1]
print(letter)
g
Figure 4.103 Accessing the last value by using a negative index
subject_name = "Computing"
letters = subject_name[4:-2]
print(letters)
uti
Figure 4.104 Using a negative index with the slicing operator
148
4.11.2.4 Membership
False
True
149
4.11.3 String Functions
150
String Method Argument(s) Description Example
print('banana'
.find('na', 5))
-1
print('{1} {0}'
.format(6, 5))
'5 6'
(see section 5.3.3 on formatting strings)
True
151
String Method Argument(s) Description Example
True
False
152
String Method Argument(s) Description Example
['C', 'S']
We have already seen how to use f-strings as well as type casting and the concatenation operator to
produce strs that include other values and follow a desired format. For instance, suppose we wish to
report the values assigned to variables named student and year with some short descriptive text. We can
use f-strings like this:
student = "Bala"
year = 2024
print(f"student is {student}, year is {year}.")
153
We can also use type casting and the concatenation operator like this:
student = "Bala"
year = 2024
print("student is " + student + ", year is " + str(year) + ".")
Figure 4.106 Using type casting and the concatenation operator to format strs
In this section, we will cover how to produce the same output using the str.format() method like this:
student = "Bala"
year = 2024
print("student is {}, year is {}.".format(student, year))
Notice that the output of str.format() replaces each set of curly braces {} in the original str with one of
the provided arguments. As with f-strings, each set of curly braces is called a “replacement field” or “field”,
but the syntax here is different and fields for str.format() cannot contain arbitrary Python expressions.
By default, fields are replaced with arguments in order from left to right. For instance, in Figure 4.107,
the first {} is replaced with the first argument student while the second {} is replaced with the second
argument year.
To change the order of arguments used to replace each field, we specify an index for each field as follows:
student = "Bala"
year = 2024
print("year is {1}, student is {0}.".format(student, year))
Just like indices for lists and strs, indices for arguments start from 0 and not 1. Hence, the field of {0}
specifies an index of 0 and is replaced by the first argument student. The field of {1} specifies an index of
1 and is replaced by the second argument year.
154
Note that multiple fields may specify the same index and that it is not compulsory for all arguments to be
included in the output.
student = "Bala"
year = 2024
print("{0}, {0}, {0}".format(student, year))
Also note that fields with specified indices and fields without specified indices cannot be used together.
As with f-strings, to “escape” the curly braces so that they are not treated as a field, use double braces so
that it looks like “{{“ or ”}}”:
student = "Bala"
year = 2024
print("student is {}, ignore {{}}, year is {}".format(student, year))
{index}
155
QUICK . 1 1
K 4
CHEC
1. State whether each of the expressions below evaluate to True or False.
a) "b" not in "abc"
b) "c" in "abc"
c) "d" in "abc"
example = "x"
example += 'y'
example *= 2
print(example)
word1 = 'more'
word2 = 'or'
word3 = 'less'
result = (2 * word1) + (word2 + word3) * 2
print(result)
Hint: The standard order of operations and meaning of brackets in mathematics still apply.
s = "x\nx"
s = 2 * s
s = s + "\n"
s = s * 2
print(s)
5. A string satisfies the format of a postal code if has exactly 6 characters, all of which are digits.
Write a program to solve the following postal code format checking problem:
Input Output
• postal_code: a string • Whether postal_code satisfies the format of a
postal code (i.e., True or False)
Table 4.39 Input and output requirements for postal code format checking problem
For instance, if the input is “012345”, the output should be “True”. However, if the input is “12345”, the output
should be “False”.
6. A string satisfies the format of a postal code if has exactly 6 characters, all of which are digits.
Input Output
• message: a string • Text-based box made out of “*” characters
surrounding the contents of message
Table 4.40 Input and output requirements for boxed message problem
For instance, if the input is “Hello, World!”. the output should be:
***************
*Hello, World!*
***************
156
4.12 Lists
LEARNING OUTCOMES
2.3.9 Use list values with appropriate operators, built-in functions and methods (limited to those
mentioned in the Quick Reference Guide) to perform:
• Concatenation and repetition
• Extraction of single items and subset of items (i.e., indexing and slicing)
• Testing of whether an item is in the list
• Calculation of length
• Calculation of sum, minimum value and maximum value (provided the list items are all
integer or floating-point values)
lists are useful to keep and manipulate multiple values without prior knowledge of the total number of
values. This is an important advantage over using multiple variables, where the number of variables must
be determined ahead of time.
list literals are written as a comma-separated list of values enclosed by square brackets.
print([1, 2, 3])
[1, 2, 3]
print([])
[]
Figure 4.114 Examples of valid list literals
Note how a list is printed in a format like its literal form. The same format is used when a list is converted
to a string.
U
DID YO
N O W ? A simple list is sometimes called a one-dimensional “array”. Technically, an
K array requires all its items to have the same size and be arranged consecutively
in memory. Although a list may contain values of different types and sizes,
internally it tracks those values using an array of memory addresses that all have the same size, so this
description is acceptable.
Besides built-in lists, Python offers more traditional arrays that contain values of a fixed type via the
array module. Use of the array module is not covered in this textbook.
157
In particular, the list of values in the brackets can be empty and the [] literal is often used to create a new
empty list.
[value_1]
[value_1, value_2]
4.12.2.1 Comparison
Many of the same operators that work on strings also work on lists, such as the comparison operators
in Table 4.41. When comparing lists, the results are determined by comparing the first pair of differing
values. If no difference is found, the comparison is based on the lengths of the lists.
print(x < y)
True
print(x < z)
False
158
Operator Name Operand(s) Description Example
print(x <= y)
True
print(x <= z)
False
print(x > y)
False
print(x > z)
True
print(x >= y)
False
print(x >= z)
True
159
Note that since the results
are obtained by comparing
corresponding items in the
two lists, in general the
data types of items in the two
lists must be compatible for
performing comparisons or an
error will occur:
As with strings, the + and * operators can be used to join and repeat lists:
[1, 2, 1, 2, 1, 2]
160
4.12.2.3 Indexing and Slicing
print(l[3])
print(l[-1])
[6, 5, 0]
print(l[1:4:])
[9, 6, 5]
Note: The slicing with step
operator also works with strs.
See Table 4.35.
161
Unlike strs which are immutable, with lists
we can use the indexing operator on the left- scores = [85.0, 88.1, 72.9, 63.4]
hand side of an assignment statement to change scores[2] = 50.0
the value stored in a list: print(scores)
Notice how the third value (with index 2) of the [85.0, 88.1, 50.0, 63.4]
list changed from 72.9 to 50.0. Figure 4.117 Changing a value in a list using the indexing
operator
U
DID YO
N O W ?
K x = [1965]
y = x # x and y refer to the same mutable list
print(x)
print(y)
Be aware that changing the contents of a
[1965]
list may have unintended consequences [1965]
if multiple variables refer to the same list.
x[0] = 2024 # list is changed using x
Recall from section 4.5.2 that variables in print(x)
Python behave like sticky notes attached to print(y) # y reflects the change
list using x will also affect y: Figure 4.118 Changes to a mutable value can affect multiple variables that refer to it
Figure 4.119 Conceptual diagram that shows x and y referring to the same modified value
This is different from having multiple variables refer to ints, floats or strs as these values are immutable.
Suppose two variables x and y refer to the same immutable value (such as an int). If x gets replaced with
a different value, that value will be separate and have a different memory address from the original, so y
is unaffected:
x = 1965
y = x # x and y refer to the same immutable int
print(x)
print(y)
1965
1965
2024
1965
Figure 4.120 Immutable values can only be replaced and not changed
162
4.12.2.4 Membership
We can also use the membership operator “in” to test if an item appears within a list:
False
not in Non- Any value and a Returns True if the print(0 not in [2, 0, 1])
membership list value on the left cannot
be found in the list False
on the right and False print(3 not in [2, 0, 1])
otherwise.
True
163
4.12.3 List Functions
0
Note: len() also works with strs and
dicts. See Table 4.37 and Table 4.48.
164
U
DID YO
KNOW?
lists are also objects and one of the methods they have is append(), which
accepts a value and adds it to the end of the list. It is often used to build up
a list item by item:
years = []
years.append(1965)
years.append(2024)
print(years)
[1965, 2024]
Figure 4.122 Using the append() method to build up a list
Note that append() modifies the list directly and does not have a return value. Since it modifies the
list, other variables that refer to the same list will also be affected, as mentioned in section 4.12.2:
An alternative to using append() is using the concatenation operator to join the original list to a list
containing just the new item:
years = []
years = years + [1965]
years = years + [2024]
print(years)
[1965, 2024]
Note that each use of the concatenation operator creates a new list and this process is slower than using
append(). As the new list is separate from the original list, other variables that refer to the original
list are unaffected:
165
QUICK . 12
C K 4
CHE
1. State whether each statement is true or false.
a) In general, the + operator requires the values on its left and right sides to have the same type (except
mixing ints and floats are allowed because they both represent numbers).
b) The code [] + [] is valid and the result is [].
c) The code 1 + [] is valid and the result is [1].
d) The code [1] + 1 is valid and the result is [1].
e) The code "A" + "B" is valid and the result is "AB".
f) The code ["A"] + ["B"] is valid and the result is ["A", "B"].
2. It can be difficult to distinguish between list literals, indexing operators and the slicing operators as they all use
square brackets in their syntax.
For each of the following programs, identify all the line numbers where there are:
i. List literals
ii. Uses of an indexing operator
iii. Uses of a slicing operator
a)
1 choices = [1965, 2017, 2024, 2030]
2 year = choices[2]
3 print(year)
b)
1 year = [1965, 2017, 2024, 2030][2]
2 print(year)
c)
1 numbers = [1, 9, 6, 5, 0]
2 numbers = numbers + [2]
3 midpoint = len(numbers) // 2
4 print(numbers[midpoint])
5 half1 = numbers[:midpoint]
6 half2 = numbers[midpoint:]
7 print(half2 + half1)
A) [1, [2]]
B) [1, 2]
C) [3]
D) 3
166
QUICK . 12
C K 4
CHE
6. Predict the output of the following code:
scores = [92, 97, -1, 93, 96, -1]
scores[2] = 99
scores[5] = 50
print(scores)
4.13 Dictionaries
LEARNING OUTCOMES
2.3.10 Use dictionary values with appropriate operators to perform dictionary insertion,
query, lookup and deletion.
ER MS
KEY T
A dictionary (or dict) is an unordered collection
of key-value pairs.
dicts are useful for problems that involve pairs Dictionary (dict)
of values where it is useful to quickly look up the A data type to represent an unordered collection of
second value of a pair given only the first value key-value pairs
of pair, which is called the key.
Key
The name “dictionary” is based on the metaphor A unique value that is used to look up the
of physical dictionaries, which are used to look corresponding value in a dictionary
up definitions (i.e., values) of words (i.e., keys).
However, dicts are also useful for other problems that have nothing to do with words or definitions. For
instance, they can be used to look up the name of a user given their email address or to look up the price
of a item given its product code.
The keys and values of a dictionary satisfy certain requirements and properties:
1 Keys in a dictionary must be unique
167
4.13.1 Dictionary Literals
Note that like a list, a dict is printed in a format Syntax 4.11 dict Literals
like its literal form.
{}
The {} literal is often used to create a new empty
dict. {key_1: value_1}
4.13.2.1 Indexing
Looking up the value associated with a key uses the indexing operator:
print(d[6])
168
To set or change the value associated with a key, use the indexing operator on the left-hand side of an
assignment statement:
To remove a key-value pair, use a del statement with the indexing operator:
4.13.2.2 Membership
To test whether a key exists in a dictionary, use the “in” membership operator.
False
169
Operator Name Operand(s) Description Example
True
Table 4.48 summarises how the len() function can be used to determine the number of key-value pairs in
a dictionary:
print(len({}))
170
QUICK . 13
C K 4
CHE
1. If d is a dictionary, what does “d[2]” evaluate to?
A) The second item stored in d.
B) The third item stored in d.
C) The key associated with the value 2 in d.
D) The value associated with the key 2 in d.
A) 15
B) 16
C) 20
D) 22
171
4.14 Control Flow
LEARNING OUTCOMES
2.2.1 Interpret flowcharts to understand the sequence, selection and iteration constructs.
2.3.11 Use the if, elif and else keywords to implement selection constructs.
2.3.12 Use the for and while keywords to implement iteration constructs.
Control flow refers to the order in which the instructions of a program are run. In this section, we will
explore flowcharts and use them to understand the control flow commands in Python.
4.14.1 Flowcharts
The algorithm below shows how to calculate the remainder when a positive integer dividend is divided by
another positive integer divisor:
1 2 3 4
Get user to If dividend Subtract
divisor from Output dividend
input positive is less than
dividend and store as the final
integers dividend divisor, go to Step
the difference in answer.
and divisor. 4. Otherwise,
continue to Step 3. dividend, then go
back to Step 2.
This same algorithm can also be represented using the flowchart as shown in Figure 4.131.
START
No
dividend < divisor? dividend = dividend - divisor
Yes
OUTPUT dividend
STOP
As can be seen, a flowchart is a more visual way of presenting an algorithm and may be easier to understand
than a long series of steps. Each step is represented by a symbol and the order in which steps are followed
is shown using flow lines or arrows.
172
U
DID YO
O W ?
N
The earliest forms of flowcharts were introduced by engineers Frank and Lillian
K Gilbreth in 1921 to visualise processes used in industrial production, sales and
finance.
ER MS
KEY T
There are four standard symbols used in flowcharts:
Control flow
1 Terminator The order in which instructions of a
program are run
Flow line
2 Data An arrow that indicates the order in
which steps should be followed
Flowchart
3 Decision A visual presentation of an algorithm
using symbols to show the flow of a
sequence of steps
173
It represents the beginning or end of a set of steps and is usually labelled with either START or END/STOP
as shown in Figure 4.133.
START
STOP
It represents a step involving a question. The outgoing arrows represent the possible outcomes to the
question and are usually labelled “Yes” and “No”. There may be two or three outgoing arrows depending on
the number of possible outcomes. Only one of these outgoing arrows should be followed when performing
the algorithm.
174
Note that the question used in a decision
symbol may involve checking if two values
U
DID YO
are equal or not equal. We typically use
the Python operators “==” and “!=” to
?
KNO W
represent such checks. For example, to Flowcharts are mainly
check if the variables x and y are equal, we meant for humans to
would ask “Is x == y?” Conversely, to check read, so they generally
if the variables x and y are not equal, we do not need to follow strict syntax rules as long as
would ask “Is x != y?” the intended meanings are clear. Nevertheless, we
standardise on using “==” and “!=” in this textbook to
Some examples of the decision symbol are avoid any ambiguity.
shown in Figure 4.137.
It represents a step involving an action or operation. This usually involves changing the value of a variable
or performing more complex actions, as shown in Figure 4.139.
175
4.14.2 Constructs
Flowchart symbols must be connected using flow lines or arrows to indicate the order in which they should
be followed. The flow lines should also follow the sequence, selection and iteration constructs (which
will be discussed in the following sections) to avoid ending up with overly complicated flowcharts that are
difficult to read or understand. Each of these constructs are formulated to have exactly one entry point and
one exit point.
Using these constructs will also ensure that the flowcharts can be translated into Python, which is
a structured programming language that does not allow jumping to arbitrary lines of code when the
program is running.
4.14.2.1 Sequence
Third Instruction
Exit Point
Figure 4.140 Example of a sequence construct
4.14.2.2 Selection
Whenever a decision symbol is used, it always has exactly one incoming flow line that serves as an entry
point but may have two or more outgoing flow lines exiting from it. These outgoing flow lines lead to
different branches or sequences of symbols and constructs. If these separate sequences merge back into a
single flow line, this forms a selection construct with one entry point at the decision symbol and one exit
point where the multiple branches merge.
176
We call this a selection construct as the person or computer running the flowchart has to select only one
of the different branches from the decision symbol to follow. For instance, Figure 4.141 shows a selection
construct with two branches (i.e., a “Yes” branch and a “No” branch).
Entry Point
Exit Point
4.14.2.3 Iteration
Exit Point
ER MS
KEY T
Branch Iteration construct
A sequence of instructions that serves as one option A construct for repeating instructions while a
out of two or more choices particular condition is true
177
4.14.3 if-elif-else Statements
By using a selection construct, the flowchart in Figure 4.143 outputs “Yes” if the input text is the same as the
special phrase “P@55w0rd”, otherwise it outputs “No”:
START
INPUT text
Is text == “P@55w0rd”?
Yes No
STOP
178
Suppose that now we want to perform more commands in each branch of password.ipynb and for the
program to output “Goodbye” just before it ends (whether or not the input text matches the password).
The amended flowchart and Python source may look like this:
START
INPUT text
Is text == “P@55w0rd”?
Yes No
OUTPUT “Goodbye”
STOP
179
It is also possible to nest an if-else statement inside another if-else statement. For instance, we might
want to provide a separate error message if the text entered is blank.
One possible solution would be for the flowchart and Python source to look like this:
START
INPUT text
Yes
Is text == “”?
OUTPUT “Goodbye”
STOP
Figure 4.148 Password checker program with detection of blank input added
180
Nested if-else statements are common.
However, excessive indentation can make the
code difficult to read. To avoid this, Python 1
provides a way to combine nested if-else 2
statements into a single statement so that there 3
is no need to increase the level of indentation. 4
For example, password_3.ipynb can be re- 5
written as shown in Figure 4.149. 6
7
8
The elif keyword in line 4 of 9
password_3_elif.ipynb replaces the 10
else and if in lines 4 and 5 of the original
password_3.ipynb and avoids additional Figure 4.149 Password checker with additional indentation
indentation of the remaining code. When avoided by using elif
reading the source code, treat elif as an
abbreviation for “else if”. Besides these
cosmetic changes, this program otherwise
behaves exactly the same as the original
password_3.ipynb.
Some algorithms do not require any instructions to be followed for the else portion of an if-else
statement. The else keyword should then be omitted entirely. For instance, the following program tries to
censor the word “Evil” if it is entered as part of a name:
START
INPUT name
Is “Evil” in name?
Yes
name = “***CENSORED***”
No
STOP
181
In Figure 4.150, name has its contents replaced by a censorship notice if “Evil” is found in the name entered
by the user. Otherwise, the program continues normally by outputting the name with no modification to
its contents.
The syntax rules in Figure 4.152 summarise how the words and indentation for an if-elif-else statement can
be arranged:
if condition:
commands when condition is True
if condition:
commands when condition is True
else:
commands when condition is False
if condition_1:
commands when condition_1 is True
elif condition_2:
commands when condition_1 is False and condition_2 is True
else:
commands when condition_1 is False and condition_2 is False
182
U
DID YO
KNOW? Run the following program that uses the turtle module to draw the result of
rolling a 6-sided die:
import random
import turtle
roll = random.randint(1, 6)
turtle.penup()
turtle.goto(-50, -50)
turtle.color('black')
turtle.pendown()
turtle.forward(100)
turtle.left(90)
turtle.forward(100)
turtle.left(90)
turtle.forward(100)
turtle.left(90)
turtle.forward(100)
turtle.penup()
if roll == 1:
turtle.color('red')
turtle.goto(0, 0)
turtle.dot(20)
else:
turtle.color('blue')
turtle.goto(-25, -25)
turtle.dot(10)
turtle.goto(25, 25)
turtle.dot(10)
if roll % 2 == 1:
turtle.goto(0, 0)
turtle.dot(10)
if roll > 3:
turtle.goto(-25, 25)
turtle.dot(10)
turtle.goto(25, -25)
turtle.dot(10)
if roll == 6:
turtle.goto(-25, 0)
turtle.dot(10)
turtle.goto(25, 0)
turtle.dot(10)
turtle.hideturtle()
turtle.done()
Figure 4.153 Drawing the result of rolling a 6-sided die using the turtle module
Figure 4.154 Examples of rolling a 6-sided dice using the turtle module
Note how the code uses if-else statements to decide on which dots to draw.
183
4.14.4 while Loops
1
2 Yes
INPUT name Is name == “”?
3
4
No
Nadiah
OUTPUT “Hello” +
Hello Nadiah name
Like the if-elif-else statement, Python uses indentation to determine which lines of code belong to
the while statement. In the case of greeting.ipynb, only line 3 belongs to the while statement and it
is run repeatedly as long as nothing is entered into name (in other words, as long as name == "" is True).
Note that, exactly like the flowchart, Python checks the condition after the while keyword at least once
while the program is running and every time after the commands in the loop are complete. This means that
if we set name to anything other than an empty string, Python will skip the contents of the loop.
ER MS
KEY T
Loop
Instructions that are repeated
until a condition is met
Additional lines with the same indentation belong to the same while loop. For instance, the flowchart
and program in Figure 4.158 and Figure 4.159 go through the contents of a list and output each item on
a separate line.
184
START
index = 0
index = index + 1
Yes
Is index < length of items? OUTPUT items[index]
No
STOP
Note that the while loop follows the iteration construct that we learnt previously. However, sometimes we
want a convenient way for the program to go back to the start of the loop or to exit the loop early. For such
cases, Python provides the continue and break keywords.
The continue keyword causes Python to skip the remaining commands in the loop and go straight to
deciding whether the loop should run again by testing the condition after the while keyword. For example,
suppose we have a loop that (by default) outputs “C is for word” for every word in a list. However, if a
word does not start with C, we can decide that the default behaviour does not apply and that the program
should advance to the next word instead.
185
The flowchart and source code in Figure 4.161 and Figure 4.162 demonstrate how such an algorithm can be
implemented using the continue keyword:
START
index = 0
Yes
Is first letter of No
words[index] “C”? index = index + 1
No Yes
STOP
186
The break keyword, on the other hand, causes Python to immediately skip the remaining commands and
exit the loop completely. For example, suppose we once again have a loop that (by default) outputs “C is
for word” for every word in a list. However, this time we want the loop to end once a word that does not
start with C is detected.
The flowchart and source code in Figure 4.163 and Figure 4.164 demonstrate how such an algorithm can be
implemented using the break keyword:
START
index = 0
No Yes
Is first letter of
words[index] “C”?
No
Yes
STOP
187
The program in Figure 4.125 demonstrates how the continue and break keywords work by playing a
simple “Simon Says” game.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
In this game, the computer will repeat what the user enters as long as the input starts with the words
“Simon says”. This is the default behaviour that is repeated using a while loop that seems to repeat forever
as its condition is always True (line 6). However, if the input matches the special word “Quit”, the loop ends
immediately using the break keyword and the game ends with the thank-you message. On the other hand,
if the input does not start with “Simon says”, the default behaviour is skipped and we proceed to ask for
the next input instead. Try playing the game a few times and see if the new keywords behave the way you
expect them to.
U
DID YO
N O W ?
K import turtle
Note how the code uses a while loop to keep repeating the forward() and left() method calls until it
breaks out of the loop upon returning to the starting point.
188
4.14.5 for-in Loops
Previously, we saw an example of iterating through the items of a list by using an index variable:
Iterating through the items of a sequence is such a common task that Python provides a shortcut for doing
so: the for-in statement. When we use the for-in statement, each item in the sequence is assigned to a
variable automatically before the loop is run. For instance, we can simplify the code shown in Figure 4.168
to become the code in Figure 4.169:
Figure 4.169 Iterating through a list without a counter using a for-in loop
Note that this new code no longer has an int counter (index). Instead it has a variable item
that is assigned a new item from the list each time the loop repeats. The syntax for for-in statements
is shown in Figure 4.170:
189
U
DID YO
N O W ?
The for-in loop works with dicts as well. The
loop is run for each key-value pair in the dict and K
the loop variable is assigned to the key for each
We can shorten repetitive code using for-in
pair, as shown in Figure 4.175:
loops. For instance, the code for drawing
randomised artwork using the turtle
module in section 4.10.7 can be shortened to
the following with no change in behaviour:
import random
import turtle
U
DID YO
N O W ? import turtle
K
values = [8, 12, 2, 16, 4]
for value in values:
for i in range(2):
for length in [50, value * 10]:
It is not recommended to modify a Python turtle.forward(length)
list while iterating through it using a turtle.left(90)
turtle.forward(50)
for-in loop. Modifying a list during turtle.done()
iteration can lead to unexpected behaviour
and errors. This is because internally the
Figure 4.173 Drawing a column chart using the
for-in loop still keeps track of a hidden turtle module
counter variable and modifying the list
during the loop can mess up this tracking.
190
QUICK . 14
C K 4
CHE
1. For each of the following flowcharts:
a)
START
INPUT number
No
OUTPUT “Even” number % 2 == 0? OUTPUT “Odd”
STOP
B) START
No
dividend < divisor? dividend = dividend - divisor
Yes
OUTPUT dividend
STOP
C)
START
current = start
Yes
current < stop? OUTPUT current
No
191
QUICK . 14
C K 4
CHE
2. Write a program that lets the user input a list of strs by asking for each item of the list in order. Receiving an
empty str from the user would indicate the end of the list. The program would then output the list on the
screen.
Input Output
• A sequence of non-empty strs, in order, • Printed list of all input strs arranged in the
ending with an empty str correct order
Table 4.49 Input and output requirements for the list input problem
3. Write a program that lets the user input a password. To make sure that the password has been entered
correctly, the program should ask for it twice. If the two entered passwords are different, the program should
output “Invalid” and repeat the input process all over again. The program should output “Valid” and end when
the two entered passwords match.
Input Output
• password: password entered by user • “Invalid” whenever password and password_again do
• password_again: password entered not match, repeated as many times as needed; “Valid”
again by user when the two inputs match
Table 4.50 Input and output requirements for the password input problem
LEARNING OUTCOMES
2.3.13 Write and call user-defined functions that may accept parameters and/or provide a
return value.
2.3.14 Distinguish between the purpose and use of local and global variables in a program
that makes use of functions.
Besides Python’s built-in functions, you can also write your own functions. These are called user-defined
functions (UDFs).
To define a UDF, we first need to choose its name. The name must be a valid Python identifier that follows
the same rules described previously for variable names (see section 4.5.2.1). The UDF’s name should also
be different from any existing variable or function names. Otherwise, the UDF will overwrite the existing
value or function associated with that name.
For example, the program define_hello.ipynb in Figure 4.176 defines a function named hello() that
prints out the phrase “Hello, World!”. Lines 2 and 3 form the UDF’s body which is run whenever the UDF is
called. Like control flow statements, this indentation is used to determine the start and end of each UDF.
192
If we run define_hello.ipynb, nothing appears on the screen because hello() is never actually called.
In order to use a UDF, you need to call it and this can only be performed after the UDF is defined.
The program say_hello.ipynb in Figure 4.177 defines the same function hello(), then calls the
hello() function three times. In this program, the indentation after line 1 indicates that lines 2 and 3
belong to the hello() UDF’s body while lines 5 to 7 do not. Also, if you place lines 5 to 7 before lines 1 to 3,
running the program will produce an error.
1
2
3
4
5
6
7
For instance, the program quiz.ipynb in Figure 4.178 has a UDF get_answer() that expects to receive
two arguments using the parameters prompt and reply. This means that when the function is called (but
before the function body is run), the first argument will be assigned to a new variable named prompt and
the second argument is assigned to a new variable named reply.
193
1
2
3
This value is returned by the function
4 after the function call ends.
5
6
7
8
9
10
11
Figure 4.178 Defining a UDF that accepts arguments and returns a value
Line 4 of quiz.ipynb also shows that a UDF can provide a return value using the return keyword. When
Python encounters a return instruction, the function immediately ends and the original function call is
treated as the provided return value. If no return instruction is encountered before the function body
ends, then the default return value is None and the original function call is also treated as None. An example
of this is the hello() function in Figure 4.177.
From Lines 6 to 8 of quiz.ipynb, get_answer() is called three times and the return value of each call is
assigned to variables answer1, answer2 and answer3 respectively.
Figure 4.179 illustrates the process of how the arguments of a function call are assigned to new variables
with names given by the function’s signature and how the return value is assigned to answer1.
Figure 4.179 How arguments are assigned to new variables during a function call
194
Figure 4.180 summarises the syntax rules for a UDF that accepts either no arguments, one argument or two
arguments. This pattern can be extended to write UDFs that accept even more arguments.
def function_name():
commands to run when function_name is called
def function_name(parameter_1):
commands to run when function_name is called (needs 1 argument)
U
The syntax rules for return are
DID YO
summarised in Figure 4.181. The return
N O W ?
K
value after the return keyword is
optional. If it is provided, the return value
can be of any data type. If it is omitted, the
return value is treated as None. Run the following program that uses the turtle
module and allows the user to draw lines by clicking
in a window:
turtle.onscreenclick(draw_line)
return return_value turtle.done()
ERMS
that Python supports “first-class functions” where
195
For instance, the program area.ipynb in Figure 4.184 has a UDF named area_of_circle() that, when
called, will assign its argument to a variable named radius. Inside the function, a new variable named
area is also created:
1
2
3
4
5
6
7
8
9
In this case, both radius and area are local variables for area_of_circle(). This means that outside
the function body of area_of_circle(), radius and area are not defined. For example, commands
like print(radius) and print(area) would run correctly inside the function body (e.g., line 5), but the
same code would produce an error message outside the function body (e.g., line 9).
Notice that area_of_circle() uses the constant PI that is defined outside of the function body. Besides
local variables, the code inside a function body also has read access to the global variables, constants
and functions that are defined outside of the function. The different local as well as global variables and
constants found in area.ipynb are illustrated in Figure 4.70:
Global variables
and contstants
PI
Global variables
and contstants
PI
ER MS
In a typical program, constants and functions
are defined once and do not change. Hence, it is KEY T
usually predictable to use global constants and Global variable
call global functions such as input(), print() or A variable that is created outside of a UDF
other UDFs inside a function body. and is readable from the UDF’s body if its
name is not hidden by a local variable
196
Variables are different from constants and functions as they can change while a program is running. A
function that depends on global variables may produce completely different behaviour each time it is
called. To avoid this, UDFs should request for all the inputs that they need as arguments instead of using
global variables. For instance, the program add_function.ipynb in Figure 4.186 shows two ways of
writing a function to add two numbers. In general, the good_example() approach is better than the
bad_example() approach as all the inputs that can affect the return value are clearly specified in the
function’s signature and the function body does not use any global variables.
1
2
3
4
5
6
7
8
9
10
For add_function.ipynb, the variable names num1 and num2 are used in two different contexts:
1 On lines 1 and 2, they are local variables for the good_example() function.
2 On lines 5, 7 and 8, they are global variables.
While they have the same names, it is important to understand that the local variables named num1 and
num2 on lines 1 and 2 are completely separate variables from the global variables named num1 and num2
on lines 5, 7 and 8.
When good_example() is called on line 9, two new local variables are created and given the names num1
and num2 just before the function body on line 2 is run. This temporarily hides the global variables that are
also named num1 and num2 and prevents them from being accessed inside the body of good_example().
However, both global variables are merely hidden and not lost. After the call to good_example() ends,
the local versions of num1 and num2 are removed and the global variables that are named num1 and num2
become accessible again with the same values that were assigned to them previously.
Figure 4.187 illustrates the local and global variables in add_function.ipynb and how having a local
variable can hide access to global variables with the same name.
197
Local Variables Global Variables
num1 num1 (hidden)
num1 num2 (hidden)
Global Variables
num1
num2
Notice how the global variables named num1 and num2 are hidden for good_example() but are not hidden
for bad_example().
• Code that is repeated in different locations of the program can be put in UDFs. The repeated code
can then be replaced with shorter function calls.
• When designing an algorithm, the solution to each sub-problem can be placed in a separate UDF.
Each UDF will be smaller than the main program and thus can be more easily understood. (See
Chapter 7.)
• When working in a team, different programmers can be given responsibility for different UDFs so
they can all work concurrently.
• Each UDF can be tested separately and more thoroughly with different inputs compared to a single
large program with no UDFs. (See Chapter 6.)
U
DID YO
?
KNO W
In this textbook, we will limit ourselves to read access for global variables.
If you accidentally try to reassign a global variable, you may find that your
existing code no longer works. For example, let us consider a simple function
example() that prints the value of a global variable number:
198
U
DID YO
N O W ? This program works fine. However, if we add a line that tries to reassign the
K global variable number, we suddenly get an error:
1
2
3
4
5
This may be doubly surprising since the line we added (line 3) comes after where the error occurs (line 2).
The explanation is that Python code in a function definition is treated differently from top-level code that
is not in a function definition:
• Top-level code is run immediately as Python reads it and all variables are global.
• On the other hand, code in a function definition is not run immediately and there is a need to distinguish
between local and global variables. Python does this by reading all the code in the function in advance
(without running it) and identifying which variables are reassigned anywhere in the function. These
variables are then treated as local variables for the entire function body.
In this case, Python identifies that the code added on line 3 will reassign number and thus treats number on
line 2 as a local variable. This then leads to an error when the function is called because, as a local variable,
number is not initialised yet on line 2.
199
U
DID YO
KNOW?
Python allows you to override this behaviour by declaring that number must be
treated as a global variable by using the global keyword. This also grants the
function write access to reassign the specified global variable:
Although this works, it is generally a bad practice to reassign global variables inside a function and use of
the global keyword is not recommended.
QUICK . 15
C K 4
CHE
1. What is the output of the following code?
def decorate(s, symbol):
return symbol + s + symbol
message = 'HELLO'
message = decorate(message, '*')
message = decorate(message, '!')
print(message)
a) !*HELLO!*
b) !*HELLO*!
c) *!HELLO!*
d) *!HELLO*!
x = 2024
add_one(x)
print(x)
200
QUICK . 15
C K 4
CHE
b)
def add_one(x):
return x + 1
x = 2024
x = add_one(x)
print(x)
c)
def add_one(x):
x = x + 1
x = 2024
add_one(x)
print(x)
d)
def add_one(x):
x = x + 1
x = 2024
x = add_one(x)
print(x)
e)
def add_one():
return x + 1
x = 2024
x = add_one()
print(x)
f)
def add_one():
return x + 1
x = 2024
y = add_one()
y = add_one()
print(y)
201
4.16 with Statements
LEARNING OUTCOMES
2.3.4 Use the open() built-in function as well as the read(), readline(), write() and close()
methods to perform non-interactive file input/output.
In programming, we often need to make use of the computer’s limited resources. For some resources such
as memory for storing variables, Python takes care of reserving and releasing memory automatically.
However, there are other kinds of resources that require the programmer to manage reserving and releasing
them properly. Examples of such resources include network connections and files in secondary storage.
If a resource is accidentally reserved but never released, then it becomes unavailable for other programs.
This is like borrowing a book from a library without returning it or starting a phone call and never hanging
up.
To help programmers manage such resources properly, Python offers with statements. with statements
are designed to ensure that resources which are reserved when the statement starts are automatically
released when the statement ends. Specifically, we shall use with statements to perform file input/output
using Python.
The built-in function open() is used for opening files on your computer for either input and output based
on its second argument. To use a file for input, use “r” (meaning “read”) for the second argument. To use a
file for output, use “w” (meaning “write” to overwrite the file) or “a” (meaning “append” to continue from
the end of an existing file) for the second argument.
202
4.16.2 File Input/Output Methods
The file object that is returned by open() has the following methods:
File Object
Methods Argument(s) Description Example
close() None Closes the text file and # Writes str to file.txt
saves any changes; f = open('file.txt', 'w')
not necessary if file f.write('Line 1\nLine 2\n')
object is used with a f.close()
with statement
Returns None.
readlines() None Returns next line from # Prints out first line
the text file (including with open('file.txt') as f:
a newline character line = f.readline()
at the end if present) print(line)
or an empty str if
already at end of file. Line 1
The file object must
have been open for
reading.
203
4.16.3 Opening and Closing Files
To avoid losing data, it is important to always close any files you open. If a with statement is not used,
the close() method must be called as the last instruction for each file object that is returned by open().
However, if a with statement is used, the close() method is automatically called when the with
statement completes.
with expression:
commands to run while resource is reserved
The value that appears after with must meet certain requirements so Python knows how to release the
resource when the with statement ends. File objects returned by open() are one such type of value. For
file input/output, we usually need access to the file object inside the statement to call its methods, so we
would use the second form of the with statement to assign the file object a variable as well.
File objects can be used with for loops to iterate through the file’s lines of text. However, how for loops,
readline() and readlines() treat newline characters may need some explanation.
204
In JupyterLab Desktop, under the File menu, create a new text file with the contents shown in Figure 4.193.
Take care not to end line 3 with a newline character:
If we use a for loop to print out the lines directly, we will obtain extra blank lines:
Figure 4.194 Extra blank lines are output when printing the lines of a text file with a for loop
To understand why, let us call readlines() instead and print what is returned. We see that a list of
three items (one for each line) is returned as expected. However, what may not be expected is that the '\n'
character is included for lines 1 and 2.
This is different from what we are used to with input() and print(), which handle newlines automatically.
For input(), when the user presses enter to submit, the newline that is produced by pressing enter is
automatically excluded from the return value. For print(), a newline is automatically included at the end
of each call so the next output starts on a separate line.
205
U
DID YO
KNOW?
To remove or replace the automatic newline at the end of each print() call,
use the end keyword argument:
Hello,
world!
Hello, world!
Hello, !world!
Figure 4.196 The print() function accepts an end keyword argument that is
appended to the output
On the other hand, for file input/output, newlines must be handled manually. In particular, for loops,
readline() and readlines() will not automatically remove trailing newlines. Combined with the
default behaviour of print(), which automatically includes a newline at the end of each call, this explains
why there are extra blank lines in the output for Figure 4.194.
One possible solution is to test each line manually to see if it ends with '\n'. Figure 4.197 shows three
different alternatives that use this method to print the contents of example.txt correctly without extra
blank lines. The three alternatives use a for loop, readline() and readlines() respectively:
with open('example.txt') as f:
for line in f:
if line.endswith('\n'):
line = line[:-1]
print(line)
Line 1
Line 2
Line 3
Line 1 Line 1
Line 2 Line 2
Line 3 Line 3
206
Note that an if statement with
U
endswith() is needed before slicing off
DID YO
?
KNO W
the last character because the last line of
the file may not end with '\n'. Without
this, we may accidentally slice off the last
character of the file (note the missing “3”):
There are other solutions. For instance, we can disable
the automatic newline appended by print(), with
with open('example.txt') as f: the downside that subsequent output may not start
for line in f: on a separate line:
line = line[:-1]
print(line) with open('example.txt') as f:
for line in f:
print(line, end='')
Line 1 print('Goodbye')
Line 2
Line 1
Line Line 2
Line 3Goodbye
Figure 4.198 Incorrect example of slicing off the
last character of each line without testing Figure 4.200 Using the end keyword argument of
print() to avoid blank lines in the output
Alternatively, we can call read() to strs also have strip() and rstrip() methods
read the entire file as a str and use that return a new str with whitespace characters
the split() method with '\n' as the (spaces, newlines, etc.) removed. The whitespace is
delimiter. Compared to readlines(), removed from either both the start and end of the
this technique has the advantage of str for strip(), or just from the end of the str for
consistently removing '\n' characters rstrip(). If needed, both methods also accept a str
from all the extracted lines: of characters it should remove instead of whitespace
characters. For our purposes, calling rstrip() with
‘\n’ as the argument should help to remove any extra
with open('example.txt') as f: ‘\n’ character at the end of a line:
lines = f.read().split('\n')
for line in lines: with open('example.txt') as f:
print(line) for line in f:
line = line.rstrip('\n')
print(line)
Line 1
Line 2 Line 1
Line 2
Line 3 Line 3
Figure 4.199 Using split() with a newline delimiter Figure 4.201 Using the rstrip() method to avoid
blank lines in the output
Similarly, for the write() method, we must explicitly include newline characters after each line of output.
There is no option like for print() to automatically include a newline character at the end of each call to
write().
207
QUICK . 16
C K 4
CHE
1. Convert the following program to use a with statement instead of calling close():
f = open('cat.txt', 'w')
f.write(' /v\\ \n')
f.write('(o.o)\n')
f.write(' >^< \n')
f.close()
W
REVIE
E ST ION
QU
1. Alex wishes to write a program to find out which loaned books are overdue in the school library.
a) Alex studies the problem and decides to use str values in the format 'YYYY-MM-DD' to represent day DD,
month MM and year YYYY. Table 4.53 shows some examples of how dates would be represented in Alex’s
program.
Explain why Alex’s method of representation will simplify his program later when he needs to compare
dates.
b) The input and output requirements for Alex’s problem are provided in Table 4.54. Write a program that
processes the provided inputs and correctly prints out the titles of all the overdue books. In your program,
write and use a UDF that takes in the due date of a book and today’s date as input arguments, then returns
True if the due date has passed and False otherwise. Assume that the input data will always be valid.
208
W
REVIE
E ST IO N
QU
Input Output
• today: str representation of • Titles of all loaned books with
today’s date due dates earlier than today
• titles: titles of loaned books
(via text file)
• duedates: corresponding str
representation of due dates (via
text file)
Table 4.54 Input and output requirements for the problem of overdue books using lists
Figure 4.204 Input file containing corresponding str representation of due dates
2. Write a program to extract the hour, minute and second values (as ints) from a time string that is provided in
the format “HH:MM:SS”. Assume that the input data will always be valid.
209
W
REVIE
E ST I ON
QU
Input Output
• time_str: time string in the • Hour value, minute value and
format “HH:MM:SS”, where the second value, each on a separate
hour value must be from 0 to 23 line in the above order
inclusive, the minute value must
be from 0 to 59 inclusive, and
the second value must be from 0
to 59 inclusive
Table 4.55 Input and output requirements for the problem of reading a time string
3. Write a program to calculate the number of seconds between a start time and an end time, which are both
provided in the format “HH:MM:SS”. In your program, write and use a UDF that takes in a time string as an input
argument, then returns a list containing the time string’s hour value, minute value and second value in that
order. Assume that the input data will always be valid, the end time is after the start time, and both times occur
on the same day.
Table 4.56 Input and output requirements for the time interval problem
Input Output
• start_time: start time in the • Number of seconds between the
format “HH:MM:SS”, where the start time and end time
hour value must be from 0 to 23
inclusive, the minute value must
be from 0 to 59 inclusive, and
the second value must be from 0
to 59 inclusive
• end_time: end time in the
format “HH:MM:SS”, where the
hour value must be from 0 to 23
inclusive, the minute value must
be from 0 to 59 inclusive, and
the second value must be from 0
to 59 inclusive
ANSWER
Pg. 92-Quick Check 4.1
1. a) 111111111
b) 222222222
c) 777777777
d) 999999999
2. a) 9
b) 9
c) 9
d) 0
210
ANSWER
3. a) SILENT
b) REDACT
c) BYE
4. a) True.
b) False. Python can be used to write algorithms but is not an algorithm.
c) False. A processor runs instructions but is not made of instructions.
d) True.
Input Output
• x: a whole number • The sum of x and y
• y: a whole number
The addition algorithm provided previously is not a solution to this modified problem. See the counterexample
below where the algorithm is used to add -2017 to 1965:
1
2017
-1965
Incorrect answer
3982
The counterexample shows that the addition algorithm produces an incorrect answer of 3982 while the correct
answer should be –52.
2. The input and output requirements for the problem are as follows:
Input Output
• Weights: list of weights of • Weight of the heaviest apple
different apples
3. The title of the class test should be excluded as it is irrelevant and does not affect what we require for the
output.
Input Output
• Scores: list of class test scores • The average score of the class
with each student’s score test
included once
211
ANSWER
3. min() function call, addition operation, abs() function call, print() function call
name = input()
print(name, name, name, sep="\n")
2.
name = input()
print(name, name, name)
2. C
3. or, and
a) False
b) True
c) False
d) True
2. B
3. 5
4. B
212
ANSWER
5.
x -20.24 -19.65 -0.1 0.0 0.1 19.65 20.24
math.ceil(x) -20 -19 0 0 1 20 21
math.floor(x) -21 -20 -1 0 0 19 20
round(x) -20 -20 0 0 0 20 20
math.trunc(x) -20 -19 0 0 0 19 20
6.
import random
die1 = random.randint(1, 6)
die2 = random.randint(1, 6)
print(die1 + die2)
7.
b = float(input('Enter b:'))
h = float(input('Enter h:'))
print('Area of triangle:', 0.5 * b * h)
8.
import math
a = float(input('Enter a:'))
b = float(input('Enter b:'))
c = round(math.sqrt(a ** 2 + b ** 2), 2)
print('Hypotenuse:', c)
2. xyxy
3. moremoreorlessorless
4. 6
5.
postal_code = input('Enter postal code:')
print(len(postal_code) == 6 and postal_code.isdigit())
6.
message = input('Enter message:')
length = len(message)
print('*' * (length + 2))
print(f'*{message}*')
print('*' * (length + 2))
213
ANSWER
2. a) List literals: Line 1
Uses of an indexing operator: Line 2
Uses of a slicing operator: None
b) List literals: Line 1
Uses of an indexing operator: Line 1
Uses of a slicing operator: None
c) List literals: Lines 1, 2
Uses of an indexing operator: Line 4
Uses of a slicing operator: Lines 5,6
3. B
4. [44, 45]
5. [1, 4, 1, 4, 1, 4]
7. False
2. B
3. [‘Alice’, 1.55]
4. False
5. MAGIC
214
ANSWER
2. A possible program is as follows:
# Program: list_input.ipynb
1 # Input and Process
2 result = []
3 while True:
4 input_str = input("Enter item, blank to end: ")
5 if input_str == "":
6 break
7 result += [input_str]
8
9 # Output
10 print(result)
We can break down the task into two parts. The first part repeatedly asks the user for an item and adds it to our
output list. We can think of this as the “default behaviour” of the program. The second part detects when a
blank item is received and ends the program. We can think of this as an “exceptional case” to the
default behaviour of the program.
The first part of the task can be accomplished using a loop that keeps repeating without end. We do this using
a while condition that is always True on line 3. As the loop keeps repeating, we keep getting the next item from
the user (line 4) and adding it to our output list, which we call result (line 7). Of course, before we
start the loop we must initialise result to an empty list (line 2).
The second part of the task requires us to detect an exception to the default case. We do this using an if
statement immediately after getting the user’s input (line 5). If we detect that the input is blank, we break out
of the loop (line 6). The use of break helps to explain why our program does not run forever as long as a blank
input is eventually supplied by the user.
# Program: password_check.ipynb
1 # Input, Process and Output
2 while True:
3 password = input("Enter password: ")
4 password_again = input("Enter password again: ")
5 if password == password_again:
6 break
7 print("Invalid")
8 print("Valid")
Like the previous task, there is again a default behaviour that is required (i.e., repeatedly asking for two
passwords and printing “Invalid”) as well as an exceptional case (i.e., stopping when the two passwords match
and printing “Valid”).
Thus, we can adapt our program from the solution to the previous task. Instead of
prompting for an item, we now prompt for two passwords (lines 3 and 4). Instead of adding an item to a list, we
print “Invalid” (line 7). Finally, instead of detecting a blank input, we detect when the two passwords match
(line 5) before breaking out of the loop (line 6) and printing “Valid” as the final output (line 8).
2. a) 2024
Explanation: add_one() returns 2025 but the value is discarded.
b) 2025
Explanation: add_one() returns 2025 and is assigned to x.
c) 2024
Explanation: The x modified in add_one() is a local variable and the global variable x is not affected.
215
ANSWER
d) None
Explanation: add_one() has no return value so None is assigned to x.
e) 2025
Explanation: add_one() returns 2025 and is assigned to x.
f) 2025
Explanation: add_one() returns 2025 and is assigned to y repeatedly.
2.
with open('run_count.txt', 'r') as f:
run_count = int(f.read())
run_count += 1
print(run_count)
with open('run_count.txt', 'w') as f:
f.write(str(run_count))
2. The diagram below shows the different ways we can extract the hour, minute and second components from
the time string using the slice operator:
time [-2:]
time [:2] time [6:]
time [0:2] time [3:2] time [6:8]
-8 -7 -6 -5 -4 -3 -2 -1
0 1 2 3 4 5 6 7 8
time H H : M M : S S
216
ANSWER
Using this analysis, a possible solution is as follows:
217