Author: Ahmed Hasan
Made for U of T Coders, to be delivered on 05/04/2017
pass, continue, breakassert() - if we have timeEverything in Madeleine's Intro to Python lesson! I tried to have this lesson pick up right where the intro material left off. This includes:
In [ ]:
# let's make sure of our working directory
import os
os.chdir('/Users/Ahmed/Desktop')
In [ ]:
with open('scores.txt', 'r') as file:
scores = file.read().split(',')
In [ ]:
print(scores)
The eagle-eyed amongst you may notice the quotes around each number - but we'll address that in a bit.
In [ ]:
len(scores)
Our txt file missed a student! Let's say she got an 89 on the test. How can we add her score to our list?
In [ ]:
# should we do this?
scoresfixed = scores + [89]
# or should we open the file in Notepad/TextEdit and manually put the 89 in?
In [ ]:
# Python contains built-in methods and attributes for each object type
dir(scores)
In [ ]:
help(scores.append)
Looks like what we want, but let's check on insert and extend just to be sure -
In [ ]:
help(scores.insert)
In [ ]:
help(scores.extend)
In [ ]:
# append it is!
scores.append(89)
print(scores)
It's possible to define a custom set of methods and attributes in order to create new classes of objects. We won't go into much depth about these here, though. (See: Lina's lesson on classes in Python)
In [ ]:
dir(mylist)
Apparently not. Perhaps a for loop is the way to go? Let's test that out.
In [ ]:
for i in range(len(scores)):
scores[i] = scores[i] * 0.4
print(scores)
Whoa, that's not right!
In [ ]:
print(scores)
Looks like Python imported the data as a list of strings, save for that one 89% we appended.
We need to find a way to change those strings to integers instead, and then multiply them by 0.4.
In [ ]:
# this is one way?
for num in scores:
if isinstance(num, str):
num = int(num)
num = num * 0.4
else:
pass
In [ ]:
# or - we could use a list comprehension!
scores = [int(num)*0.4 for num in scores]
# conditionals can also be included in list comprehensions - this is valid syntax
scores = [int(num)*0.4 for num in scores if isinstance(num, str)]
In [ ]:
# else clauses in list comprehensions
scores = [int(num)*0.4 if isinstance(num, str) else num*0.4 for num in scores]
For large operations, list comprehensions are also faster than using list.append. When the latter is a part of a loop, Python looks up the list in memory at every single iteration, which can really slow things down for large lists. List comprehensions, on the other hand, do not require the look up operation. They're also far more visually compact while conveying the same amount of information.
In [ ]:
print(scores)
In [ ]:
len(scores)
Notice how the value we appended is gone. This is because we reassigned the list comprehension to scores above. Unlike append, list comprehensions do not alter an object in place - they are an object in their own right. It can sometimes be useful to print out a list comprehension to stdout before assigning it.
For future reference, we could use an assertion to make sure we did things right. We'll talk about that further down.
In [ ]:
# putting it all together - the right way, all along!
with open('scores.txt','r') as f:
scores = [int(num) for num in f.read().split(',')]
In [ ]:
print(scores)
So you'll notice that in that sample loop before the list comprehension, we used a 'pass' statement. Here it is again:
In [ ]:
for num in scores:
if isinstance(num, str):
num = int(num)
num = num * 0.4
else:
pass
pass is part of a family of useful operators within Python that allow for more precise control flow operations.
In [ ]:
mylist = [1,2,3,4,5,6,7]
In [ ]:
# pass is a placeholder
for num in mylist:
if num%2 == 0:
pass
print(num)
In [ ]:
# break will exit the loop
for num in mylist:
if num%2 == 0:
break
print(num)
In [ ]:
# continue will immediately jump to the next iteration
for num in mylist:
if num%2 == 0:
continue
print(num)
In [ ]:
mylist.append('hello')
print(mylist)
In [ ]:
# pass is useful to make certain conditions explicit and take care of outside cases
# while it's not 'necessary' here for the loop to function, it makes the operation clearer
for i in range(len(mylist)):
if isinstance(mylist[i], int):
mylist[i] = mylist[i] * 0.5
elif isinstance(mylist[i], str):
pass
print(mylist)
Let's say the following function was in the middle of a long script that runs input data through a series of operations.
In [ ]:
def sumdoubles(x,y):
'''Multiplies inputs by 2, and returns their sum.'''
out = x*2 + y*2
return out
In [ ]:
sumdoubles(2,3)
What happens if something goes wrong, and a pair of strings are somehow given as input?
In [ ]:
sumdoubles('hello', 'how goes?')
That output could be really bad for the downstream parts of our script if they're expecting numerical input!
In [ ]:
# assertions are handy to avoid the propagation of errors
def sumdoubles(x,y):
assert(not isinstance(x, str) and not isinstance(y, str))
out = x*2 + y*2
return out
In [ ]:
sumdoubles(2,3)
In [ ]:
sumdoubles(4,6.5)
In [ ]:
sumdoubles('hey', 'strings are cool')
Assertions do stop a script in its tracks though. What if we want Python to ignore an error and move forward?
In [ ]:
newlist = [1,2,3,'a',4,5]
In [ ]:
for item in newlist:
print(item * 0.5)
In [ ]:
# but let's say we just want to ignore the string, instead of ceasing the operation
for item in newlist:
try:
print(item * 0.5)
except TypeError:
print('ignored', item)