Sie sind auf Seite 1von 62

CSC108 Introduction to

Computer Programming
Final Exam Study Guide

University of Toronto St. George Fall 2015

CSC 108 - Lecture 1- Syllabus


Course covers:
basics of programming in Python
fundamental concepts of programming
solve human problems using Python
Useful studying tips from Prof:
1. Rubber duck technique-explain your program to a rubber duck verbally
(helps you figure out solutions to problems)
2. Familiarize yourself with the course site
Inverted/Flipped Classroom
Prepare-view lecture videos and finish the exercises (Due Sundays by 10 pm online)
Rehearse-apply concepts from videos (completed in lecture)
Perform-demonstrate understanding by completing exercise (Due Friday by 6pm online)
Use PCRS-Programming Course Resource System for Prepare and Perform
use UTORID and password to log in
Assignments due on Tuesdays before 10:00 pm
submitting partially finished work results in partial marks
Midterm tests are during lecture times
Labs for student use:
CDF:Computing Discipline Facility
24 hour access to rooms in Bahen Centre: BA2200, BA2210, BA2220, BA2240,
BA2270,BA3175, BA3185, BA3195 and BA3200
Getting Help
Piazza (online discussion forum) post questions and answer questions.
Dont post code until after assignments are due
Email instructors and TAs with questions
Use the drop in centre or help centres
To do list for first week:
Read Syllabus
Bookmark course website
Check Blackboard (Portal) for announcements
Install Python 3 (NOT Python 2) and Wing IDE 101(application to write Python
programs)
Complete the week 2 prepare exercise on the PCRS (due Sunday September 20 by 10
pm)
CSC 108-Lecture 2-Types and Operations

Types
different kinds of information in Python are called types
every value has a type, which determines behaviour of values
two kinds are called floats and integers
Floats
short for floating point number
the floating point refers to the decimal
one slash / is used for floating point division, which produces a float result
e.g 10/3 = 3.33333333335 ->a close but inexact answer
Integers
numbers with no decimal points
use two slashes // for integer division, which produces an integer result
e.g: typing 9//3 will produce an integer result of 3
the results are not rounded but truncated
e.g 7/4 = 1.75 but 7//4 = 1
How to write comments
Use a number sign # to write comments in Wing IDE 101
everything that follows will not be executed by the program
Modulo Division
use a percentage sign % for modulo division, which returns a remainder
e.g 10 % 3 = 1
15 % 4 = 3
8 % 2 =0
5 %2=1
5 %3=2
Order of Precedence (from highest to lowest) :
Exponentiation (**)
Negation(-)
multiplication, integer, float, mod division ( * , / , // , %)
Work left to right
addition and subtraction ( + , - )
Use parentheses to override this, and to make equations clearer
Syntax Errors
typing 2 ** 2, 2 ** 2 both will produce the correct result
typing 2 * * 2 will produce a syntax error
Python assumes that anything directly following a * should be a value, unless its **
which means exponentiation
other syntax errors include:
3 +
-> unfinished expressions
4 + 5 ) -> extra parenthese
5/0
-> divide by 0 error

(2 + 4 - 1 -> gives you ellipses (...) because its waiting for the closing

parenthese

Wing IDE 101 highlights errors in red

Built-in Functions that comes with Python


Determining the type of result
the function: type tells you what kind of result you will get
e.g type(9/3)
result: <class float> (tells you that the result is a float)
Maximum and Minimum values
the function max() or min() gives you the largest or smallest value
e.g max(1,2,3) returns the value 3
min(14.5,-7,3,-100) returns the value 14.5

Absolute value
the function abs() gives you the absolute value of a number
e.g abs(-3.7) returns the value 3.7
Typing in more than one value results in error

Help with Functions


to find out more about functions, use the help function which gives you more information
e.g typing: help(oct) gives you the result:
oct(number) -> string
Returns the octal representation of an integer
CSC 108-Lecture 3 - Built-in functions and Variable Assignments
Round function
round function takes arguments and rounds them
entered as round(number[ndigit])
providing the number of digits that you want to round to is optional
if the number ends in .5, it will be rounded to the nearest even number
examples:
round(3.9)
returns value of 4
round(1.1243,2)
returns value of 1.12
Error examples:
round()
there must be at least one number in the bracket
round(1,2,4)
round can only take a maximum of two arguments

Using negative numbers allows you to round to the left of the decimal
round(1234.5678,-2)
returns a value of 1200.0
round(4.5688, -2)
returns a value of 0.0

Variables
Form of a variable : <<variable>> = <<expression>>
variables are places to store information in a computers memory
giving variables a name allows you to refer back to it
assignment variables use the = sign
expressions on the right side of the = sign are evaluated first
the left hand side is set to refer to the value
memory addresses are assigned by Python, and we do not have access to them
e.g the expression x = 7 means:
x gets 7
x refers to value 7
x contains memory address id1
memory address id1 is stored in variable x
memory model diagram:

id1:int
X

id1

when variables get re-assigned, their values are overrided


e.g a = 5
a=4
b=a

id
15

id1
id2

id2

id2
4

CSC 108-Lecture 4-Variables and Function Definitions


Rules for Naming Variables

names must start with a letter or underscore


variables can only include numbers, letters, and underscores
e.g cows1, _fish3 is valid, @alpalcas! is invalid
Python is case sensitive
e.g Dogs = 11

dOgs # Error! This is not defined


try to choose names that are meaningful
it is rare for single-letters to be capitalized e.g K,I,J,L
use pothole_case to make variable names easier to read
pothole_case refers to a method of naming in which only lowercase letters are
used and words are separated by underscores
occasionally camelcase is used but they are not used for functions or variables
CamelCase refers to a method of naming in which every new word in a variable
starts with a capital and no underscores are used

Structure of Function Definitions


Form of function definitions: def <<function_name>> ( <<parameters>> ) :
<<body>>

def is the keyword that indicates there is a function definition


<<function_name>> is where you add the name of the function written in
pothole_case
<<parameters>> is where you add the parameters between parentheses, there
can be 0 or more parameters
the parameters are a list of variables separated by commas
value of the variables are supplied after the function is called
inside a function definition, the variables are parameters
parameters determine what kind of arguments the function accepts
<<body>> is where 1-4 statements can be found, all of the body should be
indented by 4 spaces
usually the body ends in a return statement

Examples of function definitions:


this function triples a number: def triple(num):
return num * 3

Structure of Return Statements


Form of return statements: return <<expression>>
the statement is executed by evaluating the expression which produces a value with a
memory address
the value is returned back to the function call after exiting the function
return statements should be the last thing in the body of a function definition
the function stops evaluating anything after a return statement

Structure of Function Calls

Form of function calls: <<function_name>> (<<arguments>>)


<<function_name>> refers to the name of the function that is being called
<<arguments>> refers to the 0 or more expressions that will be evaluated,
expressions are separated by commas

Rules for Executing a Function Call


arguments are evaluated first
after values are produced, the values are assigned to parameters
parameters store the memory addresses of the values
the function of the body is executed

CSC 108-Lecture 5-The Function Design Recipe


Process of Writing A Function
Think of Examples
writing examples helps other people understand what your function does
the examples should be in the docstring
a docstring is a string in the function that follows the function definition
start and end the docstring with three double quotation marks ( )
use three > signs in your example to make them look like actual function calls

Type Contract
the type contract should be above the examples in the docstring
it provides information about the parameters that are passed in as an argument
and what kind of values are returned
e.g (number) -> int means that a float or an integer may be passed in, and an
integer value is always returned
Header
the header is the beginning portion of the function definition
def <<function_name>> ( <<parameters>> ) :
Description
include a description of what your function does
the description goes after the type contract but before the examples in a
docstring
the parameters should be mentioned in the description
Body
the body is where statements are added
if the print function is used, it should be added before the return statement
the return statement should be the last thing in the body

Example
def hypotenuse(a,b):

(number,number) -> float


Finds the length of hypotenuse of a right-angled triangle using side lengths a and b
>>>hypotenuse(4,3)
5.0
>>>hypotenuse(5,12)
13.0

return ( x ** 2 + y **2) **0.5


Print v.s Return

Using only print - no values are stored in memory addresses after printing
def a():
print(hello)
s1 = a()
(hello is printed, but s1 has a return value of none)

Using only return -the function calls are assigned to variables, nothing is printed
def b():
return(hello)
s2 = b()
(nothing is printed, but s2 has a return value hello)

Using print and return-values are stored and printed


def c()
print(hello)
return(hello)
s3 = c()
(hello is printed and returned)

CSC 108-Lecture 6-Nested Function Calls and Function Reuse


Nested Function Calls
when a function call is executed, arguments are evaluated from left to right
sometimes functions contain subexpressions
figuring out the order that subexpressions are evaluated in can help find errors
e.g in the function f((a() * 2, g())
a() is evaluated first, then 2
then a() * 2 is evaluated
g() is evaluated
then the call on f happens after arguments in f are evaluated -> f((a() * 2, g())

e.g in the function def min_of_max(num1,num2,num3,num4):


return min(max(num1,num2) , max(num3,num4))

first num 1 is evaluated, then num2


then max(num1,num2) is evaluated
then num3 is evaluated, then num 4
then max(num3,num4) is evaluated
then min(max(num1,num2) , max(num3,num4)) is evaluated

e.g in the function print(pow(3, pow(pow(5,1),2)))


first the 3 is evaluated, then 5 is evaluated
then 1 is evaluated and pow(5,1) is evaluated
then 2 is evaluated
then pow(pow(5,1),2) is evaluated
then pow(3, pow(pow(5,1),2) is evaluated
then print(pow(3, pow(pow(5,1),2))) is evaluated

Function Reuse
calling one function within another function definition can help simplify programs
e.g writing a function that switches the last name and first name of a person, and
assigning the name with their gender
def format_name(first_name,last_name):

(str,str) -> str


Function takes strings first_name and last_name and switches their order
>>>format_name(Bob,Smith)
Smith, Bob
>>>format_name(Jane,Smith)
Smith, Jane

return last_name + , + first_name

the first function format_name switches first names and last names
the following function calls format_name from within the function definition
def name_and_gender(first_name,last_name,gender):

(str,str,str) -> str


Function returns strings in format last_name, first_name : gender
>>>format_name(Bob,Smith,Male)
Smith, Bob : Male
>>>format_name(Jane,Smith,Female)
Smith, Jane : Female

return format_name(first_name,last_name) + : + gender

CSC 108-Lecture 7-Type Bool and If Statements


Type Bool
Python type bool, short for Boolean has values True and False

evaluating expressions using comparison operators return Boolean values True or False

Comparison Operator

Symbol

Example

greater than

>

4>3
True

less than

<

1<5
True

equal to

==
(single = is used for
assignment statements)

4 * 3 == 12
True
7 == 7.0
True

greater than or equal to

>=

5 >= 3
True

less than or equal to

<=

4 <= 12
True

not equal to

!=

5 != 3
True

Logical Operators
Logical operators include not, and, and or
in expressions with logical operators, operands are Boolean values
when using the not operator, expressions that were originally True become False and
vice versa
e.g >>>grade = 90
>>>grade >= 50
True
>>>not(grade >= 50)
False

(something that is not True is False!)

when using the and operator, both operands must be True in order to evaluate to True,
otherwise the result will be False

e.g >>>grade1 = 95
>>>grade2 = 40
>>>grade3 = 75
>>> grade1 >= 50 and grade2 >=50
False

>>>grade1 >= 50 and grade 3 >= 50


True

when using the or operator, only one operand has to be true in order to evaluate to True
e.g >>>grade1 >=50 or grade2 >=50
True

Order of Evaluation
Consider the following statements:
>>>tuesday = true
>>>sunny = true
>>>not tuesday or sunny
True
True is returned because not tuesday or sunny was evaluated as: (not tuesday) or sunny
not was evaluated first, however adding parentheses can change the order of
evaluation:
>>>not (tuesday or sunny)
False
Short Circuit Evaluation
Python does short circuit evaluation, or lazy evaluation
when using the or operator, if the first expression is true, it doesnt matter what the
second one is
e.g >>>early = True
>>>early or 1/0 == 6
True

normally there would be a division by 0 error, but after evaluating early, True is returned
if the order was switched:
>>> 1/0 == 6 or early
->results in error
if the operator and is used, as soon as an expression is false, it doesnt matter what the
other expressions are
e.g >>>4 == 3 and 1/0 =6
False
short circuit evaluation can help prevent division by 0 errors
e.g >>> value != 0 and 10/value == 2
value != 0 prevents division by 0 error

Assigning boolean values


You must add bool in front of values
e.g >>>True == 9
False
>>> True == bool(9)

True
>>> True == bool(0)
False

Every integer and float except for 0 returns True


>>>if 5:
>>> print(Hello)
Hello
>>>if 0:
>>> print(Hello)
->nothing will happen

This also works for strings except empty strings


>>>if hi:
>>> print(Hello)
Hello
>>>if :
>>> print(Hello)
->nothing will happen

Using Strings to Compare Dictionary Orders


Using comparison operators can compare the dictionary order of strings
e.g >>>cat > dog
False
(c is before d in the alphabet)
>>>dog > cat
True
Values that are not the same type usually cannot be compared
usually will result in an unorderable types error
however integers and floats may be compared
e.g >>> 5 < hello
-> results in error
characters such as ! and * may also be compared, but their ordering is based on their
ordinal numbers
use the ord function to find their ordinal numbers
e.g >>>! < *
True
>>>ord(!)
33
>>>ord(*)
42
CSC 108-Lecture 8-Indexing and Slicing
Indexing

an index is a position within a string


characters in strings can be retrieved by using a variable that is assigned to a string, and
the position number
numbering from the right side starts from 0, and numbering from the left side starts with
-1
e.g >>>first_name = Michael
>>> first_name[2]
c
>>> first_name[-3]
a
using a number for an index that does not exist will result in an error message
e.g>>>>>> first_name[11]
->results in string index out of range error

position number
(from right)

letter

position number
(from left)

-7

-6

-5

-4

-3

-2

-1

Slicing
a slice is a substring of a string from a start index up to but not including an end index
e.g >>> first_name[2:7]
chael
>>>first_name[0:3]
Mic
leaving the beginning or end position is default for go to end or beginning of string
e.g >>>>>> first_name[:7]
Michael
>>> first_name[0:]
Michael
Stride
slicing operation has a third parameter which determines the stride in the slice
the stride is the distance between characters
e.g >>>s = computer
>>>s[::2]
'cmue'

in the example above, every other character was produced, starting from the first
character in the string to the last character
use negative strides to work backwards
e.g >>>s[-1:-8:-2]
'rtpo'
>>>s[1:8:2]

'optr'
>>>s[-1:-8:-3]
'ruo'
>>>s[1:8:3]
'our'
CSC 108-Lecture 9-Stride examples and Creating Constants
More Stride Examples
e.g >>>a =jacqueline
>>>a[::-1]
'enileuqcaj'

>>>a[0:10:-1]

an empty string is returned because starting at 0, and moving by -1 yields no results


since 0 is already furthest left of the indices
to return a string with characters in reverse, make the starting value the length of the
string
>>>a[9:-11:-1]
'enileuqcaj'
>>> s[9:0:-2]
eieqa

Creating Constants
consider function:
>>>def is_teen(age)
(int) -> boool
Return True if age represents a teenager between the ages of 13 and
18
(inclusive)
>>> is_teen(7)
False
>>>is_teen(40)
False
>>>is_teen(15)
True

return 13 <= age <= 18

the numbers 13 and 18 are known as magic numbers


we can store the numbers 13 and 18 as constants
MIN_TEEN_AGE = 13 and MAX_TEEN_AGE = 18
the constants should be assigned before the function
>>>MIN_TEEN_AGE = 13

>>>MAX_TEEN_AGE = 18
>>>def is_teen(age)
(int) -> boool
Return True if age represents a teenager between the ages of 13 and
18
(inclusive)
>>> is_teen(7)
False
>>>is_teen(40)
False
>>>is_teen(15)
True

return MIN_TEEN_AGE <= age <= MAX_TEEN_AGE = 18

constant names makes statements easier to understand at first glance


they also make it easier to change values, since values must only be changed in one
place
the code will still work exactly the same way
using constants saves you from the trouble of finding all the values in the program and
changing them one at a time
e.g if someone decided that 19 year olds are considered to be teenagers, only the
MAX_TEEN_AGE value needs to be changed
For example, programs with tax rates use constants because tax rates may change over
the years
it would be very troublesome to change every single time the tax rate was used in the
program
the convention for naming constants is to use all capital letters

CSC 108-Lecture 10-Debugging, String Methods, and For Loops


Debugging
under the menu Tools in Wing IDE, click on call stack to see stack frames
you can also use step over, step into, and step out to check different functions when
debugging
its important to master how to examine variables in programs so you can find errors in
more complex programs in the future
String Methods
methods are functions inside values or objects
form of a method call is:
object.method(arguments)
you can use dir to find out which methods are available for strings by typing:
>>>dir(str)
for more information on a method, type in:

>>>help(str.method) <-method is replaced by the name of the method eg


help(str.lower)
some examples include swapcase, lower, isdigit
e.g
s = 'Hi ThErE'
>>>s.swapcase()
'hI tHeRe'
(the lowercase letters switched to uppercase letters and vice
versa)
e.g

s = 'HELLO THERE!'
>>>s.lower()
'hello there!'
(all the letters were changed to lowercase letters)

e.g

s = '1425 cats'
>>>s.isdigit()
False
(false was returned because the string is not made up of only digits)

For Loops Over a String


form of a for loop:
for variable in str:
body
e.g
s = computers
>>>for ch in s:
>>>
print(ch)
c
o
m
p
u
t
e
r
s
(each letter in the string is printed)

e.g

s = computers
>>>for ch in s:
>>>
print(ch)

Creating a new string with the same letters in the original string:
>>>s_new =

>>>
for ch in s:
>>>s_new = s_new + ch
computers
We can use for loops in functions to perform repetitive tasks, such as checking for number
of vowels in a string
>>>def count_vowels(s):
>>>
num_vowels = 0
>>>
for char in s:
>>>
if char in aeiouAEIOU:
>>>
num_vowels = num_vowels + 1
>>>
return num_vowels

CSC 108-Lecture 11-For loop examples

given task is to write a function and check if a string contains only dots or numbers
first we write the doc string and the header of the function definition
>>>def check_string(string):
>>> (str) -> bool
>>>check_string(123.14.5324234)
True
>>>check_string(40 St. George St.)
False
we will first check if this code will work:
>>>for char in string:
>>>
if char == .:
>>>
return True
>>>
elif char.isdigit():
>>>
return True
>>>
else:
>>>
return False
the code above does not work because it will only check the first character, and the
function will exit and not check
we can try adding a flag
>>>def check_string(string):
>>>
flag = True
>>>
for char in string:
>>>
if char == '.':
>>>
flag = True
>>>
elif char.isdigit():
>>>
flag = True
>>>
else:
>>>
return false
the code above does not work because it only returns whether the last character is True
or False

for char in string:


>>> if char == ".":
>>>
flag = True
>>> elif char.isdigit():
>>>
flag = True
>>> else:
>>>
flag = False
>>> return flag
the code above should work, and can be written in simpler ways
for char in string:
>>> if (not char == ".") and (not char.isdigit()):
>>>
flag = False
>>> return flag

this code doesnt use a flag, and also works:


for char in address:
if (not char == ".") and (not char.isdigit()):
return False
return True

the code above will return false as soon as it detects a character that is not a dot or a
digit

More For Loop Over Strings Examples


This function returns the number of lowercase letters in a string
>>>def count_lowercase(string):
>>> (str) -> int
>>> >>>count_lowercase(hello)
>>>
5
>>>
>>> count_lowercase(HELLO)
>>>
5
>>>
>>> num_letters = 0
>>> for char in s:
>>>
if char.isupper():
>>>
num_letters = num_letters + 1
>>> return num_letters

This function returns True if every letter in a string appears in the word smile
>>>def check_string(string):
>>> (str) -> bool
>>> >>>check_string(smile)
>>>
True
>>> >>> check_string(frown)
>>>
False

>>>
>>>
>>>
>>>
>>>

for char in string:


if char not in smile:
return False
return True

This function returns a string that contains an underscore between every character
>>>def add_underscores(string):
>>> ''' (str) -> str
>>> >>>add_underscores(pizza)
>>>
p_i_z_z_a
>>> >>>add_underscores(cat)
>>>
c_a_t
>>>
'''
>>> new_string = ''
>>> for char in string[ : (len(string) - 1)]:
>>>
new_string = new_string + char + '_'
>>> return new_string + string[-1]

CSC 108-Lecture 12-While Loops


form of a while loop is:
while expression:
statements
while loops iterate over boolean expressions, similar to if statements
while loops are more flexible than for loops since they can loop many times
unlike for loops, they check the body and executes until the loop condition becomes
false
e.g a program that prints out characters demonstrates this:
>>> s = 'hello'
>>>i = 0
>>>while not(s[i] in 'aeiouAEIOU'):
>>> print(s[i])
>>> i = i + 1
h
#only h is printed because since e is a vowel, the loop condition is false and the
loop exits

e.g a program that checks number guesses would look like:


>>>guess = int(input(Guess a number between 1 & 20:))
>>>while not 1 <= guess <=20:
>>>
print(Thats not between 1 and 20)
>>>
guess = int(input(Guess a number between 1 & 20:))
>>> #while loop is checking to make sure that the input is valid
>>>answer = 11
>>>if guess == answer:

>>>
print(You won!)
>>> else:
>>>
print(Sorry, wrong answer)
since the int function converts guesses to integers, answers such as 11.45 would still
result in You won! being printed
a function that skips characters in a string would look like:
>>>def every_nth_character(string,n):
>>> (str,int) -> str
>>> precondition: n > 0
>>> Return a string that contains every nth character from string, starting at
>>> index 0
>>> >>> every_nth_character(Computer,3)
>>> Cpe
>>>
>>> result =
>>> i = 0
>>> while i < len(string)
>>>
result = result + string[i]
>>>
i=i+n
>>>
return result

another function that returns a string that includes a certain letter occurring a certain
amount of times
>>> def find_letter_n_times(string,letter,n):
>>>
(str,str,int) -> str
>>> precondition: letter occurs at least n times in string
>>> return the smallest substring of string starting from index 0 that
>>> contains n occurrences of the letter
>>> >>>find_letter_n_times(bubble tea,b,3)
>>> bubb
>>>
>>> i = 0
>>> count = 0
>>> while i < len(string) and count < n:
>>>
if string[i] == letter :
>>>
count = count + 1
>>>
i=i+1
>>>
return string[:i]

CSC108-Lecture 13- List Operations, and Methods


Lists
the general form of a list is: [expression1, expression2, .expression3]

each element in a list has its own memory address


typing in dir(list) in the python shell tells you what you can do with a list
lists can be indexed or sliced just like strings
e.g >>>grades = [85,90,95]
>>>grades[0]
85
>>>grades[0:2]
[85,90]
using in also works for lists
e.g>>>85 in grades
True
using functions len, min, max also work for lists
min and max are based on dictionary order for lists that are made up of characters
e.g >>>fruit = [bananas,oranges,apples]
>>>min(fruit)
apples
data of different types can be contained in the same list, so a list can have integers and
characters at the same time

Difference between Strings and Lists


for strings, we can extract substrings by using slice
e.g >>>s = computers
>>>s[0:4]
comp
however we cannot directly change values in a string
e.g >>>s = computers
>>>s[6] = d
Type Error: str object does not support item assignment
for lists, it is possible to change items in the list using methods
List methods
methods are functions that belong to an object
some methods can modify a list, and some do not
e.g: creating new lists/ modifying lists
a = [2,0], all examples below results in the list [2,0,7]
creating a new list: >>>a = a + 7 and >>>a = a[0], a[1], 7]
modifying a list: >>>a.append(7) and a.insert(len(a),7)
the method list.append adds items to end of a list
Sort Method
e.g >>>a = [0,2,8]
>>>b = a.sort
list a will become: [0,1,8], however b will have type None because the sort method will
return nothing
Memory Addresses of Items in Lists

when one item in a list is changed to a different value, the old item keeps its memory
address
the new value has a new memory address, and the element will point to the new
memory address
e.g: >>>def function(list):
>>>list.append(a)
>>>list.append(b)
>>>list.append(c)
>>>list
[a,b,c]
>>>list[1] = hello
>>> function([1,2])
[1, 'hello', 'a', 'b', 'c']
the digit 2 is replaced with string hello, which has a new memory address

CSC 108-Lecture 14-Function Range


Function Range Properties
we can use function range to create an object
they take arguments of what we want our range to include
form of function range would be : range([start],stop[,step])
the default starting position is index 0 and the default step is 1
just like slicing, range does not include the end index
e.g range(5) ->only includes 0,1,2,3,4
range is like a type that you can use in a loop
Using Function Range with Loops
Function ranges are most commonly used in for loops
e.g >>>for numbers in range(4):
>>>print(numbers)
0
1
2
3
e.g >>>for numbers in range(1,4):
>>>print(numbers)
1
2
3
e.g >>>for numbers in range(1,6,2):
>>>print(numbers)
1
3

5
it is also possible to do accumulation, which is adding as we iterate
e.g >>>my_list = [2,3,4]
>>>double_list = []
>>>for item in my_list:
>>>
double_list.append(item*2)
>>>double_list
[4,6,8]
we must use range to get indices if we want to change items in a list
e.g >>>my_list = [2,3,4]
>>>for item in my_list:
>>>
item = item * 3

>>>my_list
[2,3,4]
values were not multiplied by 3 because even though items get new
values, their new values got new memory addresses
the operation in the function body does not affect the original items in the
list
when we use range however, we can change items in the list by using the index
e.g >>>for index in range(len(my_list)):
>>> my_list[index] = my_list[index] * 3
>>>my_list
>>>[6,9,12]

it is also possible to use while loops instead of for loops


e.g >>>while index < len(my_list):
>>>
my_list[index] = my_list[index] * 3
>>>
index = index + 1

CSC 108-Lecture 15-Mutability, Aliasing, and Parallel Strings


Mutability
lists are mutable types because the values in lists can change, or mutate without altering
the id address of the list
e.g>>>A_list = [1,2,3,4,5]
>>>print(id(A_list)) #prints address in memory
>>>60492816
>>>A_list.append(6)
>>>A_list
>>>[1, 2, 3, 4, 5, 6]
>>>print(id(A_list))
>>>60492816

the address of A_list did not change even though we added a value to the list
some list methods return none
e.g >>>print(A_list.append(7))
>>>None

Aliasing
aliasing is when two or more variables refer to the same object
e.g >>>list1 = [1,2,3,4]
>>>list2 = list 1
>>>list1[1] = 5
>>>list1
[1,5,3,4]
>>>list2
[1,5,3,4]
when list1 changes, list2 also changes because they both refer to the same list
Parallel Strings
it is possible to use more than one list in a function call
e.g the following function returns a new list in which the new item in the new list is the
sum of items at the corresponding positions of list 1 and 2
the function adds items that are at the same position
e.g list1 = [1,2,3]

list2 = [4,5,6]
index: = 0 1 2
>>>def sum(list1,list2)
>>>
>>> sum([1,2,3],[4,5,6])
>>> [5,7,9]
>>>
>>> sum = []
>>> for i in range(len(list1)):
>>>
sum.append(list1[i] + list2[i])
>>> return sum
the following function returns a string with characters in s in the same original order, but
repeated a certain number of times indicated in the second string
>>>def stretchstring(s,stretchfactors):
(str,list of int) ->str
>>>stretchstring(whats , [2,4,3,2,2])
wwhhhhaaattss

>>>
newstring =
>>>
for i in range(len(s):
>>>
newstring = newstring + s[i] * stretchfactors[i]
>>>
return newstring

CSC 108-Lecture 17-Nested Lists and Nested Loops


Writing functions using lists
there are often a couple ways to use lists to write a function that yields the same result
e.g if we want to write a function that returns the greatest absolute difference between
numbers at corresponding positions in lists, we can do it in two ways
the first way is to use a list accumulator:
>>>def greatest_difference(numlist1, numlist2):
>>>
""" (list of number, list of number) -> number
Precondition: len(numlist1) == len(numlist2) and numlist1 != []
Return the greatest absolute difference between numbers at
corresponding positions in numlist1 and numlist2.
>>> greatest_difference([0, 3, 3], [4, 4, 10])
7
>>> greatest_difference([1, -2, 3], [-4, 9, 4])
11
"""
>>>
>>>
>>>
>>>

differences = []
for i in range(len(numlist1)):
differences.append(abs(numlist1[i] - numlist2[i]))
return max(differences)

the second way is to keep track of the maximum absolute value


>>>
max_value = abs(numlist1[0] - numlist2[0])
>>>
for i in range(len(numlist1)):
>>>
if abs(numlist1[i] - numlist2[i]) > max_value:
>>>
max_value = abs(numlist1[i] - numlist2[i])
>>>
return max_value

Nested Lists
nested lists are lists that contain at least one other list
e.g >>>data = [['a', 'b',c,d], [1,2,3, 4], ["epsilon", "zeta"]]
you can access information in the nested lists by using square brackets to indicate which
indices you want to access
e.g data[2][0][1] gives value p because the first index refers to the position of the nested
list inside the list, so item ["epsilon", "zeta"] are at position 2 in the list
the second number in square brackets ( [0] ) refers to the position of the item inside the
nested list, which is item epsilon
the third number in square brackets [1] refers to the position of the character inside the
item which is p
Nested and Non-Nested Lists

some code fragments create nested lists


e.g the three code fragments below creates nested lists
>>>letters = [a,b,c,d]
>>>letters[-1] = [a,b,c]
>>>letters
>>>[a,b,c, [a,b,c]]

>>>numbers =[0,1,2]
>>>letters = [a,nums,c,d]
>>>letters
[a,[0,1,2],c,d]

>>>numbers = []
>>>for i in range(6):
>>>
letters.append([i])
>>>numbers
[[0],[1],[2],[3],[4],[5]]

the following code fragments do not create nested lists


>>>numbers = []
>>>for i in range(6):
>>>
numbers = numbers + [i]
[ >>>numbers
>>>[0,1,2,3,4,5]
CSC108-Lecture 18-Files
Reading Files
in order to read files in Python, they must first be opened
they can be opened by typing: >>>open(filename,mode)
3 modes that can be used would include: r (to open for reading), w (to open for
writing), and a (to open for appending)
to close the file, type: >>>filename.close
Methods
the method readline() reads and returns the next line from the file and also includes the
newline character if it exists
it returns the empty string if there are no more lines in the file
The readline Approach
the readline approach can be used when you only want to process some parts of a file
you can use readline to skip to the beginning of the file, read the first line of the file you
are interested in

the form of the readline approach is: while not at the end of the section of interest,
process the current line, and read the next line of the interesting section

The for line in file Approach


the for line in file approach can be used when you want to process every line in a file
if the file is not large, you can just type print(filename.read()), which lets you read the
whole file at once
The read Approach
the read approach is used when you want to process every line in a file
the form of the read approach is: >>>file.read()
The readlines Approach
the readlines approach lets you return a list of all lines in the file
this approach should be used when you want to be able to examine each line by index
Reading Files Examples
fileA is a spreadsheet file that is opened and assigned to f
e.g >>>f = open(fileA.csv)
>>>for line in f:
>>>
print(line)
this code fragment will loop over every line and print all the lines

>>>line = f.readline()
>>>for line in f:
>>>print(line)
this code fragment will print every line except the first

>>>for line in f:
>>>
print(line)
>>>
f.readline()
this code fragment will print every second line

>>>print(f.readlines()[0])
this code fragment will print only the first line

CSC 108-Lecture 19-Files and Dictionaries


Files Example
The following function checks if a word is in a file
dictionary.txt contains words:
cats
dogs
fish
bird

>>>def is_correct(file, word):


""" (file open for reading, str) -> bool
Return True iff word is in file.
>>> dict_file = open('dictionary.txt')
>>> is_correct(dict_file, 'cats')
True
>>> dict_file.close()
>>> dict_file = open('dictionary.txt')
>>> is_correct(dict_file, 'whale')
False
>>> dict_file.close()
"""
for line in file:
print(line) # to see what's happening
if line == word:
return True
return False

the body of the function will result in False being returned when True should be returned,
because cats will be compared with cats\n and they are not equal
we could replace the if statement with: if word in line:
for line in file:
print(line)
if word in line:
return True
return False
this will return more True values than it should because True will return even if you have
part of a word, for example at is in cats
using strip will remove whitespace from the ends, and will return the correct results
for line in file:
print(line)
if line.strip() == word:
return True
return False

Type Dict
dict: Pythons dictionary type
general form of a dictionary:
{key1: value1, key2: value2, key3: value3...keyN: valueN}
each entry in the dictionary has 2 parts which are separated by colons
e.g grades = {A1 : 80, A2 : 90, A3: 100}
A1,A2,A3 are the keys
80,90,100 are the values associated with the keys
to look up values associated with keys, use brackets and enter the key
dictionaries are different than lists because they use keys instead of index numbers

>>>grades[A2]
90
dictionaries to retrieve information can be simpler than using lists because you would
need to use double indices
>>>grades = [['A1' , 80],[ 'A2' , 90],[ 'A3', 100]]
grades[1][1]
90
in order to be able to look up values, the keys must be unique
keys may only appear once, however the same values may appear multiple times
if we try to access a dictionary with a key that does not exist, an error will occur, however
we can check to see if a key is in a dictionary
>>>AF in grades
False
values in keys will also return False
>>>80 in grades
False

CSC 108-Lecture 20-Type Dict


Properties of Type Dict
method len returns the number of keys in the dictionary, and method in also only works
with keys
e.g >>>animal_to_locomotion = {'bug': ['crawl'],
'kangaroo': ['hop'],
'butterfly': ['fly']}

>>>len(animal_to_locomotion)
3
we cannot use index the same way as lists to get values from dictionaries
e.g >>>animal_to_locomotion[0]
Traceback (most recent call last):
Python Shell, prompt 71, line 1
builtins.KeyError: 0 -> this tells you that the key was in valid
dictionaries are mutable, similar to lists
we can add key value pairs by using assignment statements
e.g >>>animal_to_locomotion = {'bug': ['crawl'],
'kangaroo': ['hop'],
'butterfly': ['fly']}
>>>animal_to_locomotion[penguin] = [waddle]
>>>animal_to_locomotion
{'butterfly': ['fly'], 'kangaroo': ['hop'], 'penguin': ['waddle'], 'bug': ['crawl']}
we can remove a key by using method del
e.g e.g >>>del animal_to_locomotion[kangaroo] #remove key kangaroo
>>>animal_to_locomotion
{'butterfly': ['fly'], 'penguin': ['waddle'], 'bug': ['crawl']}

if a key does not exist yet, we cannot use method append, because to use append
Python needs to get the key first, which has not been added to dictionary yet
e.g >>>animal_to_locomotion[worm].append(wiggle)
Traceback (most recent call last):
Python Shell, prompt 3, line 1
builtins.KeyError: 'worm'
it is also possible to replace the values of keys
e.g>>>animal_to_locomotion[butterfly] = [flutter]
animal_to_locomotion
{'bug': ['crawl'], 'butterfly': ['flutter'], 'kangaroo': ['hop']}

A dictionary can be empty e.g >>> d = {} -> the length would be 0

Keys and values may be different types, however the type of a key must always be
immutable
lists cannot be used as keys, however we can use tuples if we want to use a sequence
as a key
e.g code such as >>>d = {(1,2,3) : banana} is valid

For Loops with Dictionaries


it is also possible to use for loops with dictionaries
e.g if we want to access the keys in a dictionary, we can use for loops
>>>for animal in animal_to_locomotion:
print(animal)
bug
butterfly
kangaroo
CSC108-Lecture 21-Functions with Dictionaries
Inverting a Dictionary
dictionaries can be used as an accumulator
if we want to invert a dictionary, we must build a new dictionary that can iterate over
different keys
for example if we have a dictionary with a list of animals and their corresponding colour,
and we want to create a new dictionary with the colours as keys instead of the animals,
we must map the colours to the animals
e.g >>>animal_to_colour = {
flamingo : pink,
mouse : grey,
goldfish : orange,
owl : grey,
bat : black,

crow : black,}
>>>colour_to_animal = {}
>>>for animal in animal_to_colour:
colour = animal_to_colour[animal]
colour_to_animal[colour] = animal
when we check the new dictionary colour_to_animal, we end up missing some animals
since more than one animal is grey or black, existing entries are replaced when the for
loop is being executed
in order to avoid this problem we should append the animals to the existing list in the
dictionary instead of replacing them
>>>colour_to_animal = {}
>>>for animal in animal_to_colour:
colour = animal_to_colour[animal]
if not(colour in colour_to_animal):
colour_to_animal[colour] = [animal]
else:
colour_to_animal[colour].append[animal]
Building a Dictionary Example
we want to write a function that takes in an ordered lists of shoes worn by runners as
they passed a finish line of a marathon
the function will return a list that maps the shoe companies to a list of the placements
achieved by the runners wearing the shoes
we want the shoe brands to be the keys, and to appear only once
we want to update the keys without overwriting any of the previous values
e.g >>>def build_placements(shoes):
""" (list of str) -> dict of {str: list of int}
Return a dictionary where each key is a company and each value is a
list of placements by people wearing shoes made by that company.
>>> build_placements(['Brooks', 'Asics', 'Asics', 'NB', 'Brooks',
'Nike', 'Asics', 'Adidas', 'Brooks', 'Asics'])
{'Brooks': [1, 5, 9], 'Asics': [2, 3, 7, 10], 'NB': [4], 'Nike': [6], 'Adidas': [8]}
"""
result = {}
for i in range(len(shoes)):
if shoes[i] in result:
result[shoes[i]].append(i+1)
else:
result[shoes[i]] = [i+1]
return result
CSC 108 -Lecture 22-Writing More Complicated Programs

Palindrome Program
we want to write a program that determines whether a string is a palindrome
a palindrome is a string that is read the same backwards and forwards
e.g words like noon and racecar are palindromes
for complicated programs it is easier to write them when we have an Algorithm
an algorithm is a sequence of steps that accomplish a task
sometimes it is possible to have more than one algorithm
there are 3 algorithms that can be used to write this program:
Algorithm 1
step 1: Reverse the string
step 2: Compare the reversed string to the original string
The program:
>>>def reverse(s):
rev =
for ch in s:
rev = ch + rev #reverses the string
return rev
>>>def is_palindrome(s):
(str) -> bool
Return True if and only if s is a palindrome.
>>>is_palindrome(noon)
True
>>>is_palindrome(octopus)
False

return reverse(s) == s #compares the reversed string to the original string


Algorithm 2
step 1: Split the string into two halves
step 2: Reverse the second half
step 3: compare the first half to the reversed second half
The Program
>>>def is_palindrome2(s):
>>>
n = len(s) #number of characters in s
>>>
#compare the first half of s to the reverse of second and omit the middle character of an
odd >>>#length string
return s[:n//2] == reverse(s[n-n//2:])
Algorithm 3
step 1:compare the first character to the last
step 2:compare the second character to the second last

step 3:stop when the middle of the string is reached

The program
i -><- j
noon
we compare the letter values at index i and index j
and we stop when j is less than i

j i
noon
def is_palindrome3(s):
i=0
j = len(s) - 1
while i < j and s[i] == s[j]:
#comparing first character with last, i<j is the stopping position since we #need to
stop when i and j are next to each other, but j is less than i
i=i+1
j = j -1
return j <= i
CSC108-Lecture 23-Doctests
Doctests
a doctest is a python module that allows us to run tests in an automated way
rather than calling example calls in the Python Shell after running programs, it would be
more convenient to run them all at once
to run them all at once we need to import the module
>>>import doctest
>>>doctest.testmod() #to run all tests for this module
if four example calls were all valid, the results should look like:
>>>TestResults(failed = 0, attempted = 4)
The results will return the number of items that had failures, if the results do not match
the expected values
e.g for the function get_divisors, the doctest will return an error
>>>def get_divisors(num, possible_divisors):
(int, list of int) -> list of int
Return a list of the values from possible_divisors that are divisors of num.
>>>get_divisors(8,[1,2,3])
[1,2]
>>>get_divisors(4,[-2,0,2])
[2]

divisors = []
for item in possible_divisors:
if num % item == 0:

divisors.append(item)

return divisors
Since there is a divide by 0 error (second example call), we need to add a new boolean
expression to the condition inside the for loop
>>>def get_divisors(num, possible_divisors):
(int, list of int) -> list of int
Return a list of the values from possible_divisors that are divisors of num.
>>>get_divisors(8,[1,2,3])
[1,2]
>>>get_divisors(4,[-2,0,2])
[2]

divisors = []
for item in possible_divisors:
if item != 0 and num % item == 0:
divisors.append(item)
return divisors
since Python uses lazy evaluation, which occurs if the first operand in an and
expression is false, and the expression evaluates to False, the second operand is not
evaluated, so there will not be a divide by 0 error.
when we check using the doctest, the results would look like:
>>>import doctest
>>>doctest.testmod()
File _main_, line 9, in_main_.get_divisors
Failed example:
get_divisors(4,[-2,0,2])
Expected:
[2]
Got:
[-2,2]
This result shows that there was an error with the test case inside the docstring, since -2
is also a divisor of 4, so the example inside the docstring should be altered

CSC 108-Lec 24- Unitests


Testing Automatically Using Unitest
unitest modules provide another testing framework that allows us to write the test
separately from the function being tested
in the unitest we write a separate method for each test
in the method, we write a call on the function and the expected result
then we call the method assertEqual to compare expected result with the result
to assert something, is to claim that it is true

Similarities to a Doctest
same as in a Doctest, we must also import a a module
we also write code the way it would appear in the shell as well as the expected result
Example Unitest
file named divisors.py contains function def get_divisors
the function def get_divisors takes in an int, and list of int, and returns a list of int
it returns a list of values that are possible divisors of the integer
>>>import unittest
>>>import divisors
>>>class TestDivisors(unittest.Testcase):
>>>
Example unittest test methods for get_divisors.
>>>
def test_divisors_examples_1(self):
>>>
Test get_divisors with 8 and [1,2,3].
>>>
actual = divisors.get_divisors(8,[1,2,3])
>>>
expected = [1,2]
>>>
self.assertEqual(actual,expected) #comparing expected value and actual value
>>>
>>>
>>>
>>>
>>>

def test_divisors_example_2(self):
Test get_divisors with 4 and [-2,0,2].
actual = divisors.ge_divisors(4,[-2,0,2])
expected = [-2,2]
self.assertEqual(actual,expected)

>>>if_name_ == main:
>>>
unittest.main(exit = False) #looks through test cases in class for methods beginning with
#test and reports any unexpected results

running the unitest will result in 2 dots, because the two example tests were successful
if there is a failure you will get two Fs
a result of F is caused by incorrect assertion
when there is an error, we are told names of each method that had a failure and shown
its docstring
we are shown the Traceback, which is series of functions of method calls that led to the
error
we also get AssertionError, which shows the expected and actual values
>>>AssertionError: Lists differ: [8,1,2] != [1,2]
unitests can be more useful than doctests because we get more feedback

CSC 108-Lec 25- Choosing Test Cases


How to choose Test Cases
choosing the right test cases may often help you catch bugs and errors in your programs
an effective way of choosing test cases would be to group different test possibilities into
categories and pick a few to represent each type of scenario

for example, if a function returns a bool, you would need at least 2 test cases to
represent the True case and the False case

Things to Consider:
Size

if your program collects items, you should test with an empty collection, a collection with
1 item, the smallest interesting case, and a collection with several items

Dichotomies
its important to test values from different possible categories that your program uses, for
example vowels/non vowels, even/odd, positive/negative, empty/full etc.
Boundaries
if the function behaves differently for values near a particular threshold, perform tests at
that threshold
e.g if the function checks if a value is less than 18, 18 is a threshold
Order
if a function behaves differently when the values are in different orders, identify and test
each of those orders
Example of different Test Cases for Example Functions
Dichotomy and Boundary Example
the following function returns True if two integers have the same absolute value
>>>def same_abs(int1,int2):
>>> (int, int) -> bool
>>>
Return True iff int1 and int2 have the same absolute value.
>>>

Test Case Description

Int1

Int2

Expected Result

2 zeroes

True

same numbers, both positive

True

same numbers, both negative

-2

-2

True

same numbers, opposite sign

-2

True

different numbers, both positive

22

False

different numbers, both negative

-22

-2

False

different numbers, different signs

22

-2

False

Boundaries Example
the following function returns True if and only if the age entered is a teen age
>>>def is_teenager(age):
>>>
(int) -> bool
>>>
precondition: age >= 0
>>>
Return True iff age is a teenager between 13 and 18 inclusive
>>>

Test Case Description

Age

Expected Result

Below 13

12

False

Testing lower boundary

13

True

In Between

14

True

Testing upper boundary

18

True

Above 18

19

False

Dichotomy, Order, and Size Example

the following function returns true if all the letters are fluffy
>>>def all_fluffy(s):
>>>
(str) -> bool
>>>
Return True iff every letter in s is fluffy. Fluffy letters are those that appear
>>>
in the word fluffy.
>>>

Test Case Description

Expected Result

1 letter, not fluffy

False

1 letter, fluffy

True

many letters, mixed, first is


fluffy

fabcd

False

many letters, mixed, last is


fluffy

abcdf

False

many letters, none fluffy

abcde

False

many letters, all fluffy

fluffy

True

CSC 108-Lec 26-Analyzing Algorithms

Efficiency of Different Codes


instead of timing our code to analyze them, we can read the code and examine the
number of steps they take for a particular input size
this helps us examine the efficiency of the code
Code with Linear Run Times
for example, for functions that print integers, we can count how many times print is
called
e.g >>>def print_ints(n):
>>>Print the integers from 1 to n, inclusive
>>>
for i in range(1,n+1):
>>>
print(i)

if the value of n was 10, the numbers 1 to 10 will be printed since the for loop iterates 10
times
since the for loop iterates 10 times, print is called 10 times
for all values of n, the number of steps is proportional to the size of n
the amount of times print is called is equal to the number of n
another example would be a function that prints odd integers
e.g >>>def print_odd_ints(n):
>>>Print the odd integers from 1 to n, inclusive
>>>
for i in range(1,n+1,2):
>>>
print(i)
if the value of n was 10, the numbers 1,3,5,7,9 will be printed
the number of print function calls is about half of n, depending on if n is even or odd
functions print_ints and print_odd_ints both have linear run times (run time grows linearly
with respect to the input size (n))
if n and the number of steps was plotted on a graph with the number of steps as the
dependent variable on the y-axis, both functions would have a linear graph

Code with Quadratic Run Times


for example, for functions that print pairs of integers, we can count how many times print
is called
e.g >>>def print_pairs(n):
>>>Print all combinations of pairs of integers 1 to n inclusive
>>>
for i in range(1,n+1):
>>>
for j in range(1,n+1):
>>>
print(i,j)
>>>print_pairs(2)
11
12
21
22

print is called n2 times since the inner loop is executed n times, and the outer loop is
executed n times as well
the total number of steps is proportional to the size of input squared

Code with Logarithmic Run Times


for example, for functions that print integers with more complicated conditions, we can
count how many times print is called
e.g >>>def print_double_step(n):
>>>Print the integers from 1 to n, inclusive, with an intial step size of 1 and
>>>each subsequent step size being twice as large as it was previously.
>>>
i=1
>>>
while i < n + 1:
>>>
print(i)
>>>
i=i*2

print is called log2n times since the number of steps change on when n is doubled
the total number of steps is proportional to the size of log2n

number of steps

what is printed

1,2,4

n=5
n=6
n=7

3 (stays the same as when


n = 4)

1,2,4

4 (same for 8 to 15)

1,2,4,8

16

5 (same for 8 to 31)

1,2,4,16

32

1,2,4,16,32

CSC 108-Lec 27-Sorting Algorithms

the following 3 types of algorithms for sorting functions all sort list of numbers from
smallest to largest

Bubble Sort
bubble sort involves bubbling up the largest value in the unsorted part of the list
In pass 1 of the sort, we start off with numbers 7,3,5,2
7

7 is compared with 3

3 and 7 have been swapped, 7 is compared with the next item

5 and 7 have been swapped, 7 is compared with the next item

2 and 7 have swapped

in pass 2 of the sort, we start off looking at the first index and we compare values of the
integers with the next integer up to the part of the list that is already sorted, which would
be up to and excluding integer 7

3 has been compared with 5, no swap is necessary since 3 <


5
next we compare 5 with the next integer

since 2 < 5, 2 and 5 swap places

in pass 3, we look at the first index and compare values of the integers with the next
integer up to the part of the list that is already sorted, which would be up to and
excluding 5

the part of the list that contains 3 and 2 are unsorted, and the
part beyond integer 5 is sorted

2 and 3 swap places, and the sort is completed

the function that performs bubble sort is:


def bubble_sort(L):
""" (list) -> NoneType
Sort the items of L from smallest to largest.
>>> L = [4, 2, 5, 6, 7, 3, 1]
>>> bubble_sort(L)
>>> L
[1, 2, 3, 4, 5, 6, 7]
"""
print("Bubble sort")
# The index of the last unsorted item.
end = len(L) - 1
while end != 0:
# Bubble once through the unsorted section to move the largest item
# to index end.
for i in range(end):
if L[i] > L[i + 1]:

L[i], L[i + 1] = L[i + 1], L[i]


print(L)
end = end - 1

Another example of bubble sort and passes would be sort of the list [8,2,8,7,3,1,2]:
the bolded integers represent the part of the list that is already sorted
After the 1st pass

[2,8,7,3,1,2,8]

After the 2nd pass

[2,7,3,1,2,8,8]

After the 3rd pass

[2,3,1,2,7,8,8]

After the 4th pass

[2,1,2,3,7,8,8]

After the 5th pass

[1,2,2,3,7,8,8]

After the 6th pass

[1,2,2,3,7,8,8]

Selection Sort
in selection sort, the smallest value in the list is found and swapped with the first index of
the unsorted part of the list
in pass 1 of the sort, we start off with the list [3,7,2,5]
the smallest integer is found and swapped with the first index
3

the smallest integer is 2, and the first index of the


unsorted part of the list is index of 3 since the whole list is
unsorted

2 switches places with 3

in pass 2 of the sort, the unsorted part of the list is the part after integer 2
the next smallest integer is found and swapped with the first index of the unsorted part of
the list

the next smallest integer is 3, and it is swapped with the


first index of the unsorted part of the list, which is the
index of 7

3 switches places with 7

in pass 3 of the sort, the unsorted part of the list is the part after integer 3
the next smallest integer is found and swapped with the first index of the unsorted part of
the list
3

the next smallest integer is 5, and it is swapped with the


first index of the unsorted part of the list, which is the

index of 7
2

5 is swapped with 7

the function that performs selection sort is:


def get_index_of_smallest(L, i):
""" (list, int) -> int
Return the index of the smallest item in L[i:].
>>> get_index_of_smallest([2, 7, 3, 5], 1)
2
"""
# The index of the smallest item so far.
index_of_smallest = i
for j in range(i + 1, len(L)):
if L[j] < L[index_of_smallest]:
index_of_smallest = j
return index_of_smallest

def selection_sort(L):
""" (list) -> NoneType
Sort the items of L into non-descending order.
>>> L = [4, 2, 5, 6, 7, 3, 1]
>>> selection_sort(L)
>>> L
[1, 2, 3, 4, 5, 6, 7]
"""
print("Selection sort")
for i in range(len(L)):
# Find the index of the smallest item in L[i:] and swap that
# item with the item at index i.
index_of_smallest = get_index_of_smallest(L, i)
L[index_of_smallest], L[i] = L[i], L[index_of_smallest]
print(L)

Another example of bubble sort and passes would be sort of the list [1,5,8,7,6,1,7]:
the bolded integers represent the part of the list that is already sorted
After the 1st pass

[1,5,8,7,6,1,7]

After the 2nd pass

[1,1,8,7,6,5,7]

After the 3rd pass

[1,1,5,7,6,8,7]

After the 4th pass

[1,1,5,6,7,8,7]

After the 5th pass

[1,1,5,6,7,7,8]

After the 6th pass

[1,1,5,6,7,7,8]

After the 7th pass

[1,1,5,6,7,7,8]

Insertion Sort

in insertion sort, the smallest value in the unsorted part of the list is found and inserted in
its correct position in the sorted part of the list
in pass 1 of the sort, we start off with the list [3,7,2,5]
7

integer 3 is at where it should be in the list, it is not


greater than any integer to the left of it

in pass 2 of the sort we examine the integer at index 1


7

integer 7 is at where it should be in the list, it is not


greater than any integer to the left of it

in pass 3 we examine the integer at index 2

integer 2 is less than 7 and 3, so it moves to the front of


the list

the rest of the list shifts to the right by one

in pass 4, we examine the last integer

5 is less than 7, so it must be inserted in its correct spot

the list is sorted

the function that performs insertion sort is:

def insert(L, i):


""" (list, int) -> NoneType
Precondition: L[:i] is sorted in non-descending order.
Move L[i] to where it belongs in L[:i].
>>> L = [7, 3, 5, 2]
>>> insert(L, 1)
>>> L
[3, 7, 5, 2]
"""
# value to be inserted into the sorted part of the list
value = L[i]
# find the position, i, where value should go
while i > 0 and L[i - 1] > value:
L[i] = L[i - 1]
i=i-1
L[i] = value

def insertion_sort(L):
""" (list) -> NoneType
Sort the items of L into non-descending order.
>>> L = [4, 2, 5, 6, 7, 3, 1]
>>> insertion_sort(L)
>>> L
[1, 2, 3, 4, 5, 6, 7]
"""
print("Insertion sort")
for i in range(len(L)):
insert(L, i)
print(L)

Another example of insertion sort and passes would be sort of the list [6,8,2,1,1,9,4]:
the bolded integers represent the part of the list that is already sorted
After the 1st pass

[6,8,2,1,1,9,4]

After the 2nd pass

[6,8,2,1,1,9,4]

After the 3rd pass

[2,6,8,1,1,9,4]

After the 4th pass

[1,2,6,8,1,9,4]

After the 5th pass

[1,1,2,6,8,9,4]

After the 6th pass

[1,1,2,4,6,8,9]

CSC 108-Lec 28-Insertion Sort Analysis


the function that performs insertion sort is:
def insert(L, i):
""" (list, int) -> NoneType
Precondition: L[:i] is sorted in non-descending order.
Move L[i] to where it belongs in L[:i].
>>> L = [7, 3, 5, 2]
>>> insert(L, 1)
>>> L
[3, 7, 5, 2]
"""
# value to be inserted into the sorted part of the list
value = L[i]
# find the position, i, where value should go
while i > 0 and L[i - 1] > value:
L[i] = L[i - 1]
i=i-1
L[i] = value
def insertion_sort(L):
""" (list) -> NoneType
Sort the items of L into non-descending order.
>>> L = [4, 2, 5, 6, 7, 3, 1]
>>> insertion_sort(L)
>>> L
[1, 2, 3, 4, 5, 6, 7]
"""
print("Insertion sort")
for i in range(len(L)):
insert(L, i)
print(L)
Worst Case for Insertion Sort
in the following example,4 passes of insertion sort has been completed, and the double
bar separates the sorted part from unsorted

i
3

------------------sorted--------------------------| |---------------------unsorted--------------------------|
i is a missing value, and in the worst case, i will ensure that the most number of steps
will be executed when insert(L,i) is called
for the worst case, all the numbers in the sorted area will have to shift over, so in this
case i can be anything less than 3, for example i can = 2
when insert(L,i) is executed, the while loop must iterate 4 times since index of i is at
position 4
the number of assignment statements are executed would be 2x4 + 2 so 10 times since
there are 2 assignment statements in the loop, and the loop iterates 4 times and the
other 2 are from the beginning at end of the code
generally, in the worst case on pass i of insertion sort, the while loop iterates i times,
since for the while loop to exit i must equal 0 and each time 1 is subtracted from i
in the worst case on pass i of insertion sort, 2i + 2 assignment statements are executed
since there are 2 assignment statements in the while loop x i iterations of loop + the 2
assignment statements outside of the loop
in terms of i in the worst case, function insert has linear running time
in function insertion_sort, the last time that function insert is called, i has value len(L) - 1
for the call inserion_sort(L), a formula that expresses the number of comparisons during
insert calls would be n2
there are n2 comparisons because in insert the comparisons are while i > 0 and L[i-1]
> value
which can be written as 1 + (2x1 + 1) + ( 2x2 + 1) +....+(2(n-1)+1)

which becomes 2n + 2n + 2n. = 2n x

n
2

n2

value of i

number of comparisons

the comparisons

while i > 0 (since python does lazy


evaluation, the second comparison is not
executed)

2 + 1 = 3 comparisons

there are two comparisons when i is 1,


plus the one comparison when i is 0

4 + 1 = 5 comparisons

there are two comparisons when i is 2, two


comparisons when i is 1 and one
comparison when i is 0

In the worst case, insertion_sort has quadratic running time

Best Case for Insertion Sort


in the following example,4 passes of insertion sort has been completed, and the double
bar separates the sorted part from unsorted

i
1

-----------------sorted--------------------------| |---------------------unsorted--------------------------|
number at index i can be filled with a value that will cause insert(L,i) to perform the
fewest number of steps, which is the best case
i can be anything greater than 4, like 6 for example
when insert(L,i) is executed, the while loop iterates 0 times because since 6 is greater
than 4, while loop exits
when insert(L,i) is called on the example list, 2 assignments statements are executed
( the ones outside the loop)
In general, in the best case on pass i of insertion sort, the while loop iterates 0 times
In genera., in the best case on pass i of insertion sort, 2 assignment statements are
executed
In the best case, insert has constant running time
for the best case, a formula expressing the number of comparisons that are made during
all the calls to insert would be 2(n-1) + 1 or 2n - 1.
in the best case L(i-1) > value must be false when except when i = 0 only one
comparison is made, otherwise there will be 2 comparisons
In the best case, insertion_sort has linear running time

CSC 108-Lec 29-Selection Sort Analysis

The code for selection sort analysis is:


def get_index_of_smallest(L, i):
""" (list, int) -> int
Return the index of the smallest item in L[i:].
>>> get_index_of_smallest([2, 7, 3, 5], 1)
2
"""
# The index of the smallest item so far.
index_of_smallest = i
for j in range(i + 1, len(L)):
if L[j] < L[index_of_smallest]:
index_of_smallest = j
return index_of_smallest
def selection_sort(L):

""" (list) -> NoneType


Sort the items of L into non-descending order.
>>> L = [4, 2, 5, 6, 7, 3, 1]
>>> selection_sort(L)
>>> L
[1, 2, 3, 4, 5, 6, 7]
"""
print("Selection sort")
for i in range(len(L)):

# Find the index of the smallest item in L[i:] and swap that
# item with the item at index i.
index_of_smallest = get_index_of_smallest(L, i)
L[index_of_smallest], L[i] = L[i], L[index_of_smallest]
print(L)
in the list below, i passes of the selection sort algorithm have been completed
the double bar separates the sorted and unsorted parts of the list
i

sorted

unsorted

Analysis
get_index_of_smallest(L,i) compares pairs of items from the unsorted part of the list

there are n items in L, and when get_index_of_smallest(L,i) is executed n-i-1 pairs of


items are compared
since we start at index i + 1 and end at index n-1
we get (n-1) - (i+1) + 1 = n - i + 1
the + 1 in the equation is there because when we count the number of indices
from for example 4 to 7, there are 4 indices inclusive (4,5,6,7), which is 7-4 + 1,
so the + 1 is added because its inclusive
for function get_index_of_smallest there is no worst case and a best case number of
comparisons because since there are the same number of indices there are the same
number of comparisons
however the number of assignment statements may vary
the smallest number of possible assignment statements is 0 and the maximum is
n-i-1
In terms of the number of items in the unsorted section, get_index_of_smallest has
linear running time
in function selection_sort, value i has the value n-1 when function get_index_of_smallest
is called
For the call selection_sort(L) a formula that expresses the number of comparisons would
be

n2n
2

index

number of comparisons

i=0

n-0-1

n-1

i=1

n-1-1

n-2

i=2

n-2-1

n-3

i = n-1

n-(n-1)-1

(n1) x n
2

which is

n2n
2

total would be

in terms of the length of the list, selection_sort has quadratic running time

CSC 108-Lec 30-Creating a New Type

classes help us represent more complicated information, and allows us to create types
to name classes, start with a capital letter, and separate words with capital letters
instead of underscores
for example to create a new time called CashRegister, we would write the following
code:
class CashRegister:
A class register
def_init_(self,loonies,toonies,fives,tens,twenties):
(CashRegister,int,int,int,int,int) -> NoneType
A cash register with loonies, toonies,fives,tens and twenties.
(A loonie is a Canadian 1$ coin and a toonie is a Canadian $2
coin)
>>>register = CashRegister(5,2,6,3,1)
>>>register.loonies
5
>>>register.toonies
2
>>>register.fives
6
>>>register.tens
3
>>>register.twenties
1

self.loonies = loonies
self.toonies = toonies
self.fives = fives
self.tens = tens
self.twenties = twenties

the first parameter of a method is usually called self, and self refers to the object that is
being initialized
in the method above, self refers to CashRegister
each object is an instance of its class
__init__: is the method that is called to initialize an object, and its first parameter is self
a class describes the behaviour and features of instances of that class
an object is an instance of a class

Instance Variables
assigning to a new variable using dot notation creates that variable inside the object
variables inside an object are called instance variables
for example loonies,toonies,fives,tens, and twenties are instance variables, they exist
inside an instance
Steps for Executing CashRegister(5,2,6,3,1)
1. Create an object of type CashRegister
2. Call that objects __init__ method, which creates instance variables inside the object
3. Produce that objects memory address

you can give variable names to objects of type Cash Register


e.g >>>register2 = CashRegister(2,3,3,6,7)
you can also reassign values of instance variables
e.g >>>register2.loonies = 5
>>>register2.loonies
5

Other Methods
other methods such as add and get_total, can be added
e.g def get_total(self):
(CashRegister) -> int
Return the total amount of cash in the register
>>>register = CashRegister(5,5,5,5,5)
>>>register.get_total()
190

return self.loonies + self.toonies * 2 + self.fives * 5 + self.tens * 10 + self.twenties


*20
e.g def add(self):
(CashRegister,int,str) -> NoneType
Add count items of denomination to the register. Denomination is one of
loonies,toonies,fives,tens, and twenties
>>>register = CashRegister(5,5,5,5,5)
>>>register.add(2,toonies)
>>>register.toonies
7

if denomination == loonies:
self.loonies += count
elif denomination == toonies:
self.toonies += count
elif denomination == fives:
self.fives += count
elif denomination == tens:
self.tens += count
else:
self.twenties += count
CSC 108 - Lec 31- Object-Oriented-Programming Examples
Creating Instance Variables
in the following method, dot assignment is used to create instance variables start_time,
end_time and event_name:
class Event:
"""A new calendar event."""
def __init__(self, start_time, end_time, event_name):
""" (Event, int, int, str) -> NoneType
Precondition: 0 <= start_time < end_time <= 23
Initialize a new event that starts at start_time,
ends at end_time, and is named event_name.
>>> e = Event(12, 13, 'Lunch')
>>> e.start_time 12
>>> e.end_time 13
>>> e.name 'Lunch

self.start_time = start_time
self.end_time = end_time
self.event_name = name
Reassigning Values Example
in the following method, the original name is assigned to new_name, which will change
the event name
def rename(self, new_name):
""" (Event, str) -> NoneType
Change the name of this event to new_name.
>>> e = Event(12, 13, 'Lunch')
>>>
>>>
"""
self.name = new_name

Calculating Values Example


it is possible to return new values by using instance variables as values in various
operations
for example, the following code returns the duration by subtracting the start time of an
event from the end time
def duration(self):
""" (Event) -> int Return the duration of this event.
>>> e = Event(10, 11, 'Lecture')
>>> e.duration() 1
"""
return self.end_time - self.start_time

String Format Example


since the objects refer to memory addresses, we must write a special method (str) that
can get a string to print instead of having values refer to memory addresses
we can use method str.format, which is not a special method
str.format uses placeholders, which are locations in the string that we want to replace
with actual values
these placeholders are curly braces with an index inside
the substitution will be performed with method format
def __str__(self):
""" (Event) -> str Return a string representation of this event.
>>> e = Event(6, 7, 'Run')
>>> str(e) 'Run: from 6 to 7' """
return Run: from {0} to {1}.format(self.start_time,self.end_time)
Using Boolean Expressions Example
the following two methods compare different times, and also checks for overlapping
times
def __eq__(self, other):
""" (Event, Event) -> bool Return True iff this event has the same start time, end
time, and name as other.
>>> e1 = Event(6, 7, 'Run')
>>>
>>>
"""
return self.start_time == other.start_time and self.end_time == other.end_time
def overlaps(self, other):
""" (Event, Event) -> bool
Return True iff this event overlaps with event other.
>>> e1 = Event(6, 7, 'Run')

>>> e2 = Event(0, 7, 'Sleep')


>>>
"""
return not(other.end_time < self.start_time or self.end_time < other.start_time)
CSC 108 - Lec 32- Writing Classes That Interact
it is possible to create two types that are related to each other
for example, we can create different types when keeping track of information of different
kinds of songs
e.g class Song:
A song.
def __init__(self,artist,title,minutes,seconds):
(song,str,str,int,int) -> NoneType
>>>song = song(Neil Young,Harvest Moon,5,3)
>>>song.artist
Neil Young
>>>song.title
Harvest Moon
>>>song.minutes
5
>>>song.seconds
3

self.title = title
self.artist = artist
self.minutes = minutes
self.seconds = seconds
def __str__(self):
(Song) -> str
Return a string representation of this song.
>>>song = Song(Neil Young,Harvest Moon,5,3)
>>>str(song)
Neil Young, Harvest Moon (5:03)

return self.artist + , + self.title + ( + str(self.minutes) + : +


str(self.seconds).rjust(2,0) + )

the string method rjust(width,fillchar) returns a string justified of length width and fillchar
is the filler character
default of fillchar is space
e.g >>>3.rjust(2,0)
03
>>>14.rjust(2,0)
14

in the above example, rjust is used so that the time in minutes is always returned as two
digits, so 3 minutes is 03

Defining A Playlist Class


we can make a new class in a file called playlist.py
initially the playlist wont have any songs in it, and well use a list to keep track of the
number of songs
import song
class Playlist:
A playlist of songs.
def __init__(self,title):
(Playlist,str) ->NoneType
A Playlist of songs titled title.
>>>playlist = Playlist(Canadian Artists)
>>>playlist.title
Canadian Artists
>>>playlist.songs
[]

self.title = title
self.songs = []
the add method adds songs to the playlist
we must specify the class that can be found in module song
songs instance variable is a list, self.songs =[] so we use append

def add(self,song):
(Playlist, Song) -> Nonetype
add song to this playlist
>>>stompa = song.song(Serena Ryder , Stompa, 3, 15)
>>>playlist = Playlist(Canadian Artists)
>>>playlist.add(stompa)
>>>playlist.songs[0] == stompa
True

self.songs.append(song)
we can write a method called get_duration(self): to get the duration of a playlist
def get_duration(self0:
(Playlist) -> tuple of (int,int)
Return the duration of this playlist as a tuple of minutes and seconds.
>>>playlist = Playlist(Canadian Artists)
>>>playlist.add(song.Song(Serena Ryder,Stompa,3,15))
>>>playlist.add(song.Song(Neil Young,Harvest Moon,5,3))
>>>playlist.get_duration()
(8,18)

total_minutes = 0
total_seconds = 0

for song in self.songs:


total_minutes = total_minutes + song.minutes
total_seconds = total_seconds + song.seconds
return (total.minutes + total_seconds//60, total_seconds % 60)
since the number of seconds may be greater than 60, we use integer divison to get the
number of minutes, and modulo 60 to get the

Generating string representation of the Playlist


the following method generates the string representation of the playlist
def__str__(self):
(Playlist) ->str
Return a string representation of this playlist
>>>playlist = Playlist(Canadian Artists)
>>>playlist.add(song.Song(Serena Ryder,Stompa,3,15))
>>>playlist.add(song.Song(Neil Young,Harvest Moon,5,3))
>>>str(playlist)
Canadian Art(8:18)
1.Serena Ryder, Stompa(3:15)
2.Neil Young, Harvest Moon(5:03)

duration = self.get_duration()
minutes = str(duration[0])
seconds = str(duration [1].rjust(2,0)
result = self.title + ( + minutes + : + seconds + )
song_num = 1
for song in self.songs:
result = result + \n + str(song_num) + . + str(song)
song_num = song_num + 1
return result
CSC 108 - Lec 33- Class Interaction Example
Class Day
the following program creates a new class day which interacts with class event that was
in lecture note 31
firstly, instance variables day, month, year, and events was created
import event
class Day:
"""A calendar day and its events."""
def __init__(self, day, month, year):

""" (Day, int, str, int) -> NoneType


Initialize a day on the calendar with day, month and year,
and no events.
>>> d = Day(25, 'November', 2015)
>>> d.day
25
>>> d.month
'November'
>>> d.year
2015
>>> d.events
[]
"""
self.day = day
self.month = month
self.year = year
self.events = []

to write the method schedule_event, first the day was created( d = Day(27, 'November',
2015)
then the event was created by calling method __init__ from the event module which
created the object of type Event ( e = event.Event(11, 12, 'Meeting'))
to schedule an event, dot assignment was used (d.schedule_event(e))
events is part of object self, and it is a list that refers to all the events that were
scheduled (d.events[0] == e)
since events is a list, we can use append to add the new events to the list
def schedule_event(self, new_event):
""" (Day, Event) -> NoneType
Schedule new_event on this day, even if it overlaps with
an existing event.
>>> d = Day(27, 'November', 2015)
>>> e = event.Event(11, 12, 'Meeting')
>>> d.schedule_event(e)
>>> d.events[0] == e
True
"""
self.events.append(new_event)

to return the information in a string format, we must use a for loop to convert events to
strings

we must use a for loop because we are unsure about how many events are inside the
list
to change the event to a string, it can be written as either str(event) or event.__str__()
def __str__(self):
""" (Day) -> str
Return a string representation of this day.
>>> d = Day(8, 'December', 2015)
>>> d.schedule_event(event.Event(15, 16, 'Submit A3 work'))
>>> d.schedule_event(event.Event(16, 23, 'Celebrate end of classes'))
>>> print(d)
8 December 2015:
- Submit A3 work: from 15 to 16
- Celebrate end of classes: from 16 to 23
"""
day_string = '{0} {1} {2}:'.format(self.day, self.month, self.year)
for event in self.events:
day_string = day_string + '\n- ' + str(event)

# or event.__str__() can be used

return day_string
Creating Days and Adding Events

# outside of the Day class


if __name__ == '__main__':
to create the day 23 December 2015 the code is:
dec23 = Day(23, 'December', 2015)
to add an event "Sleep in" from 0 to 11 on 23 December 2015 we can write:
e1 = event.Event(0, 11, 'Sleep in')
dec23.schedule_event(e1)
to add another event "Brunch with friends" from 11 to 13 on 23 December 2015 we can
write:
dec13.schedule_event(event.Event(11, 13, 'Brunch with friends'))
to print the day 23 December 2015, including its events we can write:
print(dec23)

Default Parameters
Python allows you to specify default parameters to make it easier to change values, or
create similar results
for example the following code has two default parameters, subject and number
def favourite_course(subject='CSC', number=108):
return 'My favourite course is {0}{1}'.format(subject, number)

if no parameters are specified in the function call,the result will be the default values
which is CSC108
>>>favourite_course()
'My favourite course is CSC108'

If only one of the parameter values are specified, the other value will be the default value
>>>favourite_course('MAT')
'My favourite course is MAT108'

If both values are specified, the default values will not be used
>>>favourite_course('MAT', 135)
'My favourite course is MAT135'

It is also possible to change one of the default values


>>>favourite_course(number=135)
'My favourite course is CSC135'

CSC 108 - Lec 34- Class Interaction Example Continued

the method below is the same schedule_event method from class day, however it is
improved so that when events are double-booked (they overlap with an existing event),
the method will return False
to check if an event overlaps with another event, we can call method overlaps from
another class, and to check every event we must iterate and use a for-loop
since the existing_event object has all the methods of class event, by referring to the
name of the object it allows us to access the methods
in the following example we used the overlaps method with the object existing_event

def schedule_event(self, new_event):


""" (Day, Event) -> bool
Schedule new_event on this day iff it does not overlap with
existing events. Return True iff new_event is scheduled.
>>> d = Day(8, 'December', 2015)
>>> e = event.Event(16, 23, 'Celebrate end of classes')
>>> d.schedule_event(e)
True
>>> d.events[0] == e
True
"""

for existing_event in self.events:


if existing_event.overlaps(new_event):
#if new_event.overlaps(existing_event):
return False
# we've checked all the existing events and have no conflict
self.events.append(new_event)
return True

the following function returns the number of events that were successfully scheduled
without overlap
this function is an example of calling a method from inside a method
when we want to call methods from inside a method, self should be used as the object
name
in this function, the method schedule_event was used on object self
methods always need to be associated with the object being called on

def schedule_multiple_events(self, event_list):


""" (Day, list of Event) -> int
Return the number of events in event_list that were successfully
scheduled on this day, without overlapping with existing events.
>>> d = Day(5, 'December', 2015)
>>> e1 = event.Event(12, 16, 'Studying')
>>> d.schedule_event(e1)
True
>>> e2 = event.Event(17, 19, 'Dinner with A')
>>> e3 = event.Event(11, 13, 'Lunch with B')
>>> e4 = event.Event(9, 10, 'Gym')
>>> d.schedule_multiple_events([e2, e3, e4])
2
"""
count = 0
for new_event in event_list:
if self.schedule_event(new_event):
count = count + 1
return count
CSC 108-Lec 35-Dealing With Exceptional Situations
Exceptional Situations

Exceptional Situations may occur when input is not valid, or when preconditions of a
function are not met
For example ValueErrors may result when substrings are not found, or wrong arguments
are used
e.g >>>(abc,index(r))
ValueError: Substring not found
e.g >>>int(moogah)
ValueError: invalid literal for int() with base 10 : moogah
you can type help(ValueError) in the python shell for help on class ValueError
>>>help(ValueError)
class ValueError(Exception)
Inappropriate argument value of correct type
in the two examples above, the methods or functions were asked to do something they
couldnt do, which was an exceptional situation
use exceptions when you encounter a situation that is not part of the normal flow of
execution

Inheritance Hierarchy
the method resolution order is part of the results you get when you type help(ValueError)
in the Python shell
the method resolution order lists classes in inheritance hierarchy, which shows the
subclass order of exceptions
Method resolution order:
ValueError
Exception
BaseException
Object
ValueError is a subclass of Exception, which is a subclass of BaseException, which is a
subclass of Object
ZeroDivisionError
typing help(ZeroDivisionError) in the Python shell can give you more information about it
>>>help(ZeroDivisionError)
class ZeroDivisionError(Arithmetic Error)
Second argument to a division or modulo operation was zero
Method resolution order:
ZeroDivisionError
ArithmeticError
Exception
BaseException
Object
ZeroDivisionError is a subclass of ArithmeticError, which is a subclass of Exception,
which is a subclass of BaseException

Handling Statements
Python provides exception handling statements:
try statement(simple form)
try: -> try to execute code
statements
except: ->except when there is a problem
statements
e.g:
>>>try:
1/0
print(Does this get printed?)
>>>except:
print(Divided by zero.)

Divided by Zero
if a statement in the try block raises an exception, the remaining statements in the block
are not executed, which is why Does this get printed? was not printed

Try Statements with Exception Types


form:
try statement(Specifying exception types):
try:
Statements
except Exception Type:
Statements
e.g
>>>try:
1/0
except ZeroDivisionError:
print(Divided by zero.)
Divided by zero

an example where except clause ignores the exception and results in a


ZeroDivisionError:
>>>try:
1/0:
except ValueError:
print(Divided by Zero)
ZeroDivisionError
you can also use both except clauses, and each is tried in order
Python executes block of first matching exception clauses and ignores the rest
we can hide later except clauses by using a super class
>>>try:
1/0:

except Exception:
#the first clause matches
print(Exception happened)
except ZeroDivisionError:
#the second clause is not reached
print(Divided by Zero)

Exception happened
Warning: If you want to handle two kinds of exceptions, and one is a subclass of another,
catch the subclass first

Naming Exceptions
Every exception is an object and we can give them names
>>>try:
1/0
except ValueError:
print(Value error. )
except ZeroDivisionError as zde:
print(Divided by zero.{}.format(zde))
Divided by zero. Division by zero

Das könnte Ihnen auch gefallen