Lesson 5
Lesson 5
Page Content
Part 1: Branching
Part 1 Exercises
Tutorial 2: While Loop
Part 2 Exercise
Part 1: Branching
Video link
We’re going to use the lesson_4_pt_2.py file. You can either save it as lesson_5_pt_1a.py or
download and use the lesson_5_pt_1a.py file.
1 import turtle
2
3
4 def draw_poly(length, sides):
5 for i in range(sides):
6 my_ttl.forward(length)
7 my_ttl.right(360 / sides)
8
9
10 # setup window
11 screen = 500
12 window = turtle.Screen()
13 window.setup(screen, screen)
14
15 # create instance of turtle
16 my_ttl = turtle.Turtle()
17 my_ttl.shape("turtle")
18
19 num_sides = int(input("How many sides?> "))
20 size = int(input("Length of sides?> "))
21
22 draw_poly(size, num_sides)
Run the program, and at the prompt, instead of providing a number, provide a word, for
example dog.
This error occurs because in line 19 we are trying to convert the literal (string) dog into an
integer. Since dog is not a whole number, it causes an error.
What we need to do is check that user has entered a whole number before converting it into an
integer.
Create a new file in, enter the code below then save it as lesson_5_pt_1b.py.
PRIMM:
Predict what you think will happen when you run the code twice:
first time enter the value 10
second time enter the value dog
Run the code. Did it follow your predictions?
Let’s investigate that code.
Remember that Python inputs are strings. Strings have special operations called methods. One
of those is the isdigit method. isdigit returns the Boolean value of True if all the characters in
a string are digits.
String Methods
Python has many useful string methods. If you want to explore them W3Schools’
Python String Methods is a good place to start.
We can tell if the user’s input is a number or not. Now we need to tell the computer how to
respond to this information.
The if statement
Adjusts your lesson_5_pt_1b.py code so it is the same as the code below.
PRIMM
Predict what you think will happen when you run the code twice:
first time enter the value 10
second time enter the value dog
Run the code. Did it follow your prediction?
Let’s investigate that code.
Flowcharts
Flowcharts are great at demonstrating how selection works. We have already used the condition
symbol (diamond) in our for loops. They are also used for the conditions in if statements.
Code flowchart:
Code breakdown:
Line 3: if user_value.isdigit():
This defines the if statement.
The if tells Python that this is an if statement.
The next part is called a conditional.
Conditionals are operations that return a Boolean value (True or False).
This specific conditional is user_value.isdigit()
We already know the results from our previous work:
10 → True
dog → False
Ends with :
This has the same use as for loops and functions. It indicates that an indented
code block follows.
The indented code block, will only run if the condition returns True. In our example:
10 → user_value.isdigit() returns True → run indented code block
dog → user_value.isdigit() returns False → don’t run indented code block
Line 4: print("That's a number")
This is the indented code block that will run if user_value.isdigit() is True
We can now respond to a digit being entered. But what if we want to provide a different
response when user_value.isdigit() is False?
PRIMM
Predict what you think will happen when you run the code twice:
first time enter the value 10
second time enter the value dog
Run the code. Did it follow your prediction?
Let’s investigate that code.
Code flowchart:
Code breakdown:
To check out what is happening in detail stepping through the code with the debugger. Use the
inputs of 10 and dog.
1 import turtle
2
3
4 def draw_poly(length, sides):
5 for i in range(sides):
6 my_ttl.forward(length)
7 my_ttl.right(360 / sides)
8
9
10 # setup window
11 screen = 500
12 window = turtle.Screen()
13 window.setup(screen, screen)
14
15 # create instance of turtle
16 my_ttl = turtle.Turtle()
17 my_ttl.shape("turtle")
18
19 # get user input
20 num_sides = input("How many sides?> ")
21 if num_sides.isdigit():
22 num_sides = int(num_sides)
23 else:
24 print("Invalid input")
25 quit()
26
27 size = input("Length of sides?> ")
28 if size.isdigit():
29 size = int(size)
30 else:
31 print("Invalid input")
32 quit()
33
34 draw_poly(size, num_sides)
PRIMM
Predict what you think will happen when you run the code in the following scenarios:
valid sides value and valid size value
valid sides value and invalid size value
invalid sides value and valid size value
invalid sides value and invalid size value
Run the code. Did it follow your prediction?
Let’s investigate that code.
When testing branching code you need to test all possible paths.
Test if statements for both True conditions and False conditions.
This code had four possible branches so we needed to test all four of them
Code flowchart:
Code breakdown:
Line 19: # get user input → a comment used to structure the code
Line 20: num_sides = input("How many sides?> ") → accepts user input and assigns it to
num_sides
Line 21: if num_sides.isdigit(): → tests if num_sides only contains numbers
if num_sides.isdigit() is True then run the code block from line 20
Line 22: num_sides = int(size) takes the value assigned to num_sides converts it to an
integer, then reassigns it to num_sides
Line 23: else: → if num_sides is not all numbers execute following code block (lines 22 to
23)
Line 24: print("Invalid input") → informs the user of their mistake
Line 25: quit() → exits the program
Line 27: size = input("Length of sides?> ") → accepts user input and assigns it to size
Line 28: if size.isdigit(): → tests if size only contains numbers
If size.isdigit() is True then run the code block from line 27
Line 29: size = int(size) takes the value assigned to size converts it to an integer, then
reassigns it to size
Line 30: else: → if size is not all numbers execute following code block (lines 29 to 30)
Line 31: print("Invalid input") → informs the user of their mistake
Line 32: quit() → exits the program
During all this, the only parts of the code that differs are:
This looks like a prefect opportunity to refactor the code using a function.
What is refactoring?
Refactoring is changing your code without changing the way it works. This is
normally done to make code more efficient or more maintainable.
Efficient code uses less computing resources (processing power, storage, internet
bandwidth etc.).
Maintainable code is easier for programmers to understand, fix, update and
enhance.
To refactor our code we need to add the following function at line 10 of your code:
def get_number(prompt):
num = input(prompt)
if num.isdigit():
return int(num)
else:
print("Invalid input")
quit()
Then remove the code under # get user input from lines 19 to 32, and replace it with two calls
to the function:
In the end your code should look like the code below:
1 import turtle
2
3
4 def draw_poly(length, sides):
5 for i in range(sides):
6 my_ttl.forward(length)
7 my_ttl.right(360 / sides)
8
9
10 def get_number(prompt):
11 num = input(prompt)
12 if num.isdigit():
13 return int(num)
14 else:
15 print("Invalid input")
16 quit()
17
18
19 # setup window
20 screen = 500
21 window = turtle.Screen()
22 window.setup(screen, screen)
23
24 # create instance of turtle
25 my_ttl = turtle.Turtle()
26 my_ttl.shape("turtle")
27
28 # get user input
29 num_sides = get_number("How many sides?> ")
30 size = get_number("Length of sides?> ")
31
32 draw_poly(size, num_sides)
When you refactor code, it is important to ensure the code still works the same. So run the code
to ensure that it still works the same way.
If your code still works the same, let’s investigate the code we added.
Code flowchart:
Code breakdown:
The get_number function:
def get_number(prompt): → defines our new function with one argument prompt:
we observed that the prompt was one of the differences between our two
blocks of similar code
using this argument means we can provide a different prompt each time we
call the function
num = input(prompt) → uses the prompt argument and assigns the user input to num
if num.isdigit(): → checks if num only contains numbers
return int(num) → converts the value assigned to num then sends it to the main
program:
return is new
return sends a value back to the main program and then ends the function.
else: → if num does not contain only numbers, run the following code block
print("Invalid input") → informs the user their input is not correct
quit() → exits the program
num_sides = get_number("How many sides?> ") → calls the get_number function
get_number() → calls the function
"How many sides?> " → provides the prompt string to the function
num_sides = takes the value returned by the function and assigns it to num_sides
size = get_number("Length of sides?> ") → calls the get_number function
get_number() → calls the function
"Length of sides?> " → provides the prompt string to the function
size = takes the value returned by the function and assigns it to size
Like most programming languages, Python uses US spelling. Using Australian spelling
(eg. colour) will generate an error.
Line 5
Line 6
Line 35
1 import turtle
2
3
4 def draw_poly(length, sides, color):
5 my_ttl.color("black", color)
6 my_ttl.begin_fill()
7 for i in range(sides):
8 my_ttl.forward(length)
9 my_ttl.right(360 / sides)
10 my_ttl.end_fill()
11
12
13 def get_number(prompt):
14 num = input(prompt)
15 if num.isdigit():
16 return int(num)
17 else:
18 print("Invalid input")
19 quit()
20
21
22 # setup window
23 screen = 500
24 window = turtle.Screen()
25 window.setup(screen, screen)
26
27 # create instance of turtle
28 my_ttl = turtle.Turtle()
29 my_ttl.shape("turtle")
30
31 # get user input
32 num_sides = get_number("How many sides?> ")
33 size = get_number("Length of sides?> ")
34
35 draw_poly(size, num_sides, "red")
PRIMM
Predict what you think will happen when you run the code:
Run the code. Did it follow your prediction?
Let’s investigate that code.
Code breakdown:
Turtle colours
Turtle allows the use of named colours. It also allows RBG and Hexadecimal colours,
but named colours are enough for our needs.
Now that we can change colour, can we let the user choose between red, blue and green for the
fill colour?
We will need to capture the error when the user enters anything other than "red", "blue" or
"green". That means using an if statement, but the if … else statement only allows two
branches. We need to have four.
To choose between three or more branches we need to learn about the last part of the if
statement: elif.
Lines 22 to 32
Line 47
Line 49
1 import turtle
2
3
4 def draw_poly(length, sides, color):
5 my_ttl.color("black", color)
6 my_ttl.begin_fill()
7 for i in range(sides):
8 my_ttl.forward(length)
9 my_ttl.right(360 / sides)
10 my_ttl.end_fill()
11
12
13 def get_number(prompt):
14 num = input(prompt)
15 if num.isdigit():
16 return int(num)
17 else:
18 print("Invalid input")
19 quit()
20
21
22 def get_color():
23 color = input("Fill colour (red, blue, green)?> ").lower()
24 if color == "red":
25 return color
26 elif color == "blue":
27 return color
28 elif color == "green":
29 return color
30 else:
31 print("Invalid input")
32 quit()
33
34
35 # setup window
36 screen = 500
37 window = turtle.Screen()
38 window.setup(screen, screen)
39
40 # create instance of turtle
41 my_ttl = turtle.Turtle()
42 my_ttl.shape("turtle")
43
44 # get user input
45 num_sides = get_number("How many sides?> ")
46 size = get_number("Length of sides?> ")
47 fill = get_color()
48
49 draw_poly(size, num_sides, fill)
PRIMM
Predict what you think will happen when you run the code:
Run the code. Did it follow your prediction?
Let’s investigate that code.
Line 23: color = input("Fill colour (red, blue, green)?> ").lower() → lower() is new
lower() is another string method
it converts all the letters in a string to their lowercase version
Line 24: if color == "red": → tests if the user inputted "red"
Line 25: return color
sends the value of color (in this case "red" back to the main program)
ends the function
Line 26: elif color == "blue":
is only executed when the condition in line 21 is False
checks if the value of color is "blue"
Line 27: return color
sends the value of color (in this case "blue" back to the main program)
ends the function
Line 28: elif color == "green":
is only executed if the conditions in line 21 and line 23 are both False
checks if the value of color is "green"
Line 29: return color
sends the value of color (in this case "green" back to the main program)
ends the function
Line 30: else:
is only executed if the conditions in line 21, line 23 and line 24 are all False
Line 31 and line 32 are the same as the get_number function
Code flowchart:
The if … elif … else statement is very useful and flexible. You will use it in various
configurations, so let look at it’s rules.
if…elif…else structure
the if component
always at the beginning of an if … elif … else statement
the only compulsory component
there can only be one if per if … elif … else statement
the elif component
must come after the if statement and before the else statement
is optional
there can be as many elif components as needed
it is only used when all the conditions before it are False
the else component
must be at the end of an an if … elif … else statement
it is optional
there can only be one per if … elif … else statement
it is only used when all the conditions before it are False
Part 1 Exercises
In this course, the exercises are the make component of the PRIMM model. So work through
the following exercises and make your own code.
Exercise 1
Download lesson_5_ex_1.py file and save it to your lesson folder. Below is its code.
Follow the instructions in the comments and use your Python knowledge to create a password
checker. Remember to apply the DRY principle
Exercise 2
Download lesson_5_ex_2.py file and save it to your lesson folder. Below is its code.
Follow the instructions in the comments and use your Python knowledge to create an enhanced
password checker. Remember to apply the DRY principle
Exercise 3
Download lesson_5_ex_3.py file and save it to your lesson folder. Below is its code.
1 import turtle
2
3 #####################################################
4 ## Adjust the code below to allow the user to ##
5 ## choose the coordinates where the shape is drawn ##
6 #####################################################
7
8
9 def draw_poly(length, sides, color):
10 my_ttl.color("black", color)
11 my_ttl.begin_fill()
12 for i in range(sides):
13 my_ttl.forward(length)
14 my_ttl.right(360 / sides)
15 my_ttl.end_fill()
16
17
18 def get_number(prompt):
19 num = input(prompt)
20 if num.lstrip("-").isdigit():
21 return int(num)
22 else:
23 print("Invalid input")
24 quit()
25
26
27 def get_color():
28 color = input("Fill colour (red, blue, green)?> ").lower()
29 if color == "red":
30 return color
31 elif color == "blue":
32 return color
33 elif color == "green":
34 return color
35 else:
36 print("Invalid input")
37 quit()
38
39
40 def move_pen():
41 # write your code here to get coordinates from user #
42
43
44 # setup window
45 screen = 500
46 window = turtle.Screen()
47 window.setup(screen, screen)
48
49 # create instance of turtle
50 my_ttl = turtle.Turtle()
51 my_ttl.shape("turtle")
52
53 # get user input
54 num_sides = get_number("How many sides?> ")
55 size = get_number("Length of sides?> ")
56 fill = get_color()
57
58 draw_poly(size, num_sides, fill)
Follow the instructions in the comments (check line 41) and use your Python knowledge to
enhance our shape drawing code. Remember to apply the DRY principle.
In Python we have two forms of iteration. We have already looked at the for loop. In the section
we will look at the other iteration control structure, the while loop.
definite iteration
Is used when you do know how many times the loop will need to run.
definite iteration uses for loops since they loop for a set number of times.
indefinite iteration
Is used when you don’t know how many times the loop will need to run.
indefinite iteration uses while loops since they will loop as long as the condition is
True
Card dealing is a good analogy of the definite and indefinite loops distinction:
In summary:
for loop is count controlled → we know how many times to run it.
while loop is condition controlled → we don’t know how many times to run it.
1 import random
2
3
4 def get_number(prompt):
5 num = input(prompt)
6 if num.isdigit():
7 return int(num)
8 else:
9 print("Invalid input")
10 quit()
11
12
13 number = random.randint(1, 100)
14
15 guess = get_number("Guess a number between 1 and 100> ")
16
17 if guess == number:
18 print("Correct!")
19 else:
20 print("Incorrect. The number was", number)
PRIMM
Predict what you think will happen when you run the code:
Run the code. Did it follow your prediction?
Let’s investigate that code.
The random module gives us access to a range of functions that produce random
results.
To see all the commands, you can go the W3Schools Python Random Module page.
Code breakdown:
Comparison operators
A comparison operator compares two values and returns either True or False.
Operator Meaning
!= checks if two values are not the same (not equal to)
> checks if the left value is greater than the right value
< checks if the left value is less than the right value
>= checks if the left value is greater than or equal to the right value
<= checks if the left value is less than or equal to the right value
So, we’ve made a simple game, but it is not a good one. A one-in-one-hundred chance of
guessing a number is not going to keep the user entertained for too long. How about we adjust
the code to allow the user to have ten guesses?
Now that sounds like iteration, but what kind? Since we know how many times this will need to
loop (10), it’s definite iteration. Definite iteration requires a for loop.
PRIMM
Predict what you think will happen when you run the code:
Run the code. Did it follow your prediction?
We won’t worry about investigating as there is nothing new in this code.
This is better, but still isn’t great. There is a one-in-ten chance of getting the right number. Each
guess is a stab in the dark with no knowledge gained from the previous guesses. How about we
give the user hints and let them know that their guess is too high or too low?
Change the if … else statement into the if … elif … else statement on lines 20 to 25 in the
code below:
1 import random
2
3
4 def get_number(prompt):
5 num = input(prompt)
6 if num.isdigit():
7 return int(num)
8 else:
9 print("Invalid input")
10 quit()
11
12
13 number = random.randint(1, 100)
14
15 print("You have 10 turns to guess a number between 1 and 100")
16
17 for turn in range(10):
18 guess = get_number("Guess a number between 1 and 100> ")
19
20 if guess > number:
21 print("Guess is too high")
22 elif guess < number:
23 print("Guess is too low")
24 else:
25 print("Correct!")
26
27 print("The number was", number)
We’ve done a fair bit of coding without any serious testing. So this time lets keep running our
code until we cover all four branches:
PRIMM
Predict what you think will happen when you run the code:
Run the code. Did it follow your predictions?
We won’t worry about investigating as there is nothing new in this code.
Did you identify a problem when the user guesses the number before using all ten guesses? The
game prints Correct! but then continues to ask them to guess numbers. This is because we
created a definite iteration using for which is set to always give ten guesses.
What we want is an indefinite iteration that loops until the user guesses the number. To do this
we will use a while loop.
1 import random
2
3
4 def get_number(prompt):
5 num = input(prompt)
6 if num.isdigit():
7 return int(num)
8 else:
9 print("Invalid input")
10 quit()
11
12
13 number = random.randint(1, 100)
14
15 guess = 0
16
17 while guess != number:
18 guess = get_number("Guess a number between 1 and 100> ")
19
20 if guess > number:
21 print("Guess is too high")
22 elif guess < number:
23 print("Guess is too low")
24 else:
25 print("Correct!")
26
27 print("The number was", number)
Again you want to run this code enough time that you have covered all four possible branches:
PRIMM
Predict what you think will happen when you run the code:
Run the code. Did it follow your predictions?
Let’s investigating the new code to see how a while loop works.
Code breakdown:
Code flowchart:
At the moment, if the user provides an input which isn’t an integer, the game ends. This is a bit
harsh, especially if they have already made three or four guesses.
Adjust your get_number function so that it is the same as in the code below.
1 import random
2
3
4 def get_number(prompt):
5 while True:
6 num = input(prompt)
7 if num.isdigit():
8 return int(num)
9 else:
10 print("Invalid input")
11
12
13 number = random.randint(1, 100)
14
15 guess = 0
16
17 while guess != number:
18 guess = get_number("Guess a number between 1 and 100> ")
19
20 if guess > number:
21 print("Guess is too high")
22 elif guess < number:
23 print("Guess is too low")
24 else:
25 print("Correct!")
26
27 print("The number was", number)
Again you want to run this code enough time that you have covered all four possible branches:
PRIMM
Predict what you think will happen when you run the code:
Run the code. Did it follow your predictions?
Let’s investigating the new code to see how this use of a while loop works
Code breakdown:
Code flowchart:
The end effect of these changes is the program that will ask the user for a number until the user
enters an integer.
Part 2 Exercise
In this course, the exercises are the make component of the PRIMM model. So work through
the following exercises and make your own code.
Exercise 4
Download lesson_5_ex_4.py file and save it to your lesson folder. Below is its code.
1 import turtle
2
3
4 def draw_poly(length, sides, color):
5 my_ttl.color("black", color)
6 my_ttl.begin_fill()
7 for i in range(sides):
8 my_ttl.forward(length)
9 my_ttl.right(360 / sides)
10 my_ttl.end_fill()
11
12
13 ############################################
14 ## adjust the get_number code so it loops ##
15 ## until the user provides a valid input ##
16 ############################################
17
18
19 def get_number(prompt):
20 num = input(prompt)
21 if num.lstrip("-").isdigit():
22 return int(num)
23 else:
24 print("Invalid input")
25 quit()
26
27
28 ###########################################
29 ## adjust the get_color code so it loops ##
30 ## until the user provides a valid input ##
31 ###########################################
32
33
34 def get_color():
35 color = input("Fill colour (red, blue, green)?> ").lower()
36 if color == "red":
37 return color
38 elif color == "blue":
39 return color
40 elif color == "green":
41 return color
42 else:
43 print("Invalid input")
44 quit()
45
46
47 def move_pen():
48 x_val = get_number("x axis position?> ")
49 y_val = get_number("y axis position?> ")
50 my_ttl.penup()
51 my_ttl.goto(x_val, y_val)
52 my_ttl.pendown()
53
54
55 # setup window
56 screen = 500
57 window = turtle.Screen()
58 window.setup(screen, screen)
59
60 # create instance of turtle
61 my_ttl = turtle.Turtle()
62 my_ttl.shape("turtle")
63
64 # get user input
65 num_sides = get_number("How many sides?> ")
66 size = get_number("Length of sides?> ")
67 fill = get_color()
68
69 move_pen()
70 draw_poly(size, num_sides, fill)
Follow the instructions in comments and make changes to the get_number and get_colour
functions so they capture user input errors.
By Damien Murtagh
© Copyright 2023, Damien Murtagh.