assert turn_clockwise("N") == "E"
assert turn_clockwise("W") == "N"
In [ ]:
def turn_clockwise(direction):
compass = {"N":"E" , "E": "S", "S":"W", "W":"N"}
return compass[direction]
assert turn_clockwise("N") == "E"
assert turn_clockwise("W") == "N"
turn_clockwise("N")
assert turn_clockwise(42) == None
assert turn_clockwise("rubbish") == None
In [ ]:
def turn_clockwise(direction):
compass = {"N":"E" , "E": "S", "S":"W", "W":"N"}
try:
return compass[direction]
except KeyError:
print("That's not a direction ya dingus!")
return None
try:
turn_clockwise()
except KeyError:
print("Enter a direction")
"""
assert turn_clockwise() == None
assert turn_clockwise(47) == None
assert turn_clockwise("rubbish") == None
"""
assert day_name(3) == "Wednesday"
assert day_name(6) == "Saturday"
assert day_name(42) == None
In [ ]:
def day_name(num):
days = ("Sun", "Mon", "Tues", "Wed", "Thur")
try:
return days[num]
except:
return None
assert day_name(3) == "Wed"
assert day_name(0) == "Sun"
assert day_name(42) == None
day_name(2)
assert day_num("Friday") == 5
assert day_num("Sunday") == 0
assert day_num(day_name(3)) == 3
assert day_name(day_num("Thursday")) == "Thursday"
In [ ]:
days = ("Sun", "Mon", "Tues", "Wed", "Thur")
days.index("Mon")
lst = []
for day in days:
lst.append((days.index(day), day) )
dct = dict(lst)
dct
assert day_num("Halloween") == None
assert day_add("Monday", 4) == "Friday"
assert day_add("Tuesday", 0) == "Tuesday"
test(day_add("Tuesday", 14) == "Tuesday"
test(day_add("Sunday", 100) == "Tuesday"
Hint: use the first two functions written above to help you write this one.
In [ ]:
def day_name(num):
"""Takes in day index and returns day name"""
days = ("Sun", "Mon", "Tues", "Wed", "Thur", "Fri",
"Sat")
try:
return days[num]
except:
return None
def day_num(day):
days = ("Sun", "Mon", "Tues", "Wed", "Thur", "Fri",
"Sat")
try:
return days.index(day)
except:
return None
In [ ]:
def day_add(day, delta):
number = (day_num(day) + delta) % 7
return day_name(number)
In [ ]:
assert day_name(3) == "Wed"
assert day_name(0) == "Sun"
assert day_name(42) == None
assert day_num("Fri") == 5
assert day_num("Sun") == 0
assert day_num(day_name(3)) == 3
assert day_add("Tues", 0) == "Tues"
assert day_add("Fri", 3) == "Mon"
assert day_add("Fri", -2) == "Wed"
In [ ]:
-1%7
assert day_add("Sunday", -1) == "Saturday"
assert day_add("Sunday", -7) == "Sunday"
assert day_add("Tuesday", -100) == "Sunday"
assert days_in_month("February") == 28
assert days_in_month("December") == 31
If the function is given invalid arguments, it should return None.Write a function to_secs that converts hours, minutes and seconds to a total number of seconds. Here are some tests that should pass:
assert to_secs(2, 30, 10) == 9010
assert to_secs(2, 0, 0) == 7200
assert to_secs(0, 2, 0) == 120
assert to_secs(0, 0, 42) == 42
assert to_secs(0, -10, 10) == -590
assert to_secs(2.5, 0, 10.71) == 9010
assert to_secs(2.433,0,0) == 8758
assert hours_in(9010) == 2
assert minutes_in(9010) == 30
assert seconds_in(9010) == 10
In [ ]:
def mult(x1, x2, x3, x4):
multi = x1*x2*x3*x4
add = x1 + x2 + x3 + x4
return multi*add
mult(1,1,1,2)
In [ ]:
%quickref
In [ ]:
def bad():
print("Hi")
return "bye"
bad()
def bad_absolute_value(x):
if x < 0:
return -x
elif x > 0:
return x
In [ ]:
def bad_absolute_value(x):
if x <= 0:
return -x
elif x > 0:
return x
bad_absolute_value(0)
Sometimes sticking a return in a for loop is a good idea:
In [ ]:
In [ ]:
def find_first_2_letter_word(xs):
""" Returns the first two letter word in a list. If no two letter word exists, returns an empty string"""
for index, wd in enumerate(xs):
if len(wd) == 2:
return (wd, index)
return ("", index)
print('res1', find_first_2_letter_word(["This", "is", "a", "dead", "parrot"]))
print('res2', find_first_2_letter_word(["I", "like", "cheese", "bah"]))
The key aspects of the process are:
Another powerful technique for debugging (an alternative to single-stepping and inspection of program variables), is to insert extra print functions in carefully selected places in your code. Then, by inspecting the output of the program, you can check whether the algorithm is doing what you expect it to. Be clear about the following, however:
You must have a clear solution to the problem, and must know what should happen before you can debug a program. Work on solving the problem on a piece of paper (perhaps using a flowchart to record the steps you take) before you concern yourself with writing code. Writing a program doesn’t solve the problem — it simply automates the manual steps you would take. So first make sure you have a pen-and-paper manual solution that works. Programming then is about making those manual steps happen automatically.
Do not write chatterbox functions. A chatterbox is a fruitful function that, in addition to its primary task, also asks the user for input, or prints output, when it would be more useful if it simply shut up and did its work quietly.
For example, we’ve seen built-in functions like range, max and abs. None of these would be useful building blocks for other programs if they prompted the user for input, or printed their results while they performed their tasks. So a good tip is to avoid calling print and input functions inside fruitful functions, unless the primary purpose of your function is to perform input and output. The one exception to this rule might be to temporarily sprinkle some calls to print into your code to help debug and understand what is happening when the code runs, but these will then be removed once you get things working.
In [ ]:
day_name(day_num("Wed"))
In [ ]:
def tester(line):
tests a bunch of stuff
if all the stuff is good:
return True
else:
return False
def main_func(emails):
for line in emails:
if tester(line):
return line
A few uses of random numbers:
In [7]:
import random
# Create a black box object that generates random numbers
rng = random.Random()
dice_throw = rng.randrange(1,7) # Return an int, one of 1,2,3,4,5,6
delay_in_seconds = rng.random() * 5.0
print('dice_throw', dice_throw)
print('delay_in_seconds', delay_in_seconds)
How would we get odd numbers between 1 and 100 (exclusive)?
In [15]:
for num in range(10):
print(rng.randrange(1,100,2))
random.Random() returns a uniform distribution.
There are other distributions as well.
In [27]:
random.random() # returns a number in interval [0,1). We need to scale it!
for num in range(10):
print(random.random())
In [32]:
cards = list(range(52)) # Generate ints [0 .. 51]
# representing a pack of cards.
rng.shuffle(cards) # Shuffle the pack
cards
Out[32]:
In [44]:
drng = random.Random(15) # Create generator with known starting state
for n in range(10):
print(drng.randint(1,100)) # Always 7.
In [47]:
import random
def make_random_ints(num, lower_bound, upper_bound):
"""
Generate a list containing num random ints between lower_bound
and upper_bound. upper_bound is an open bound.
"""
rng = random.Random() # Create a random number generator
result = []
for i in range(num):
result.append(rng.randrange(lower_bound, upper_bound))
return result
make_random_ints(4, 3,10)
Out[47]:
In [48]:
make_random_ints(5, 1, 13) # Pick 5 random month numbers
Out[48]:
In [49]:
xs = list(range(1,13)) # Make list 1..12 (there are no duplicates)
rng = random.Random() # Make a random number generator
rng.shuffle(xs) # Shuffle the list
result = xs[:5] # Take the first five elements
In [50]:
result
Out[50]:
The 'shuffle and slice' method is okay for small numbers but would not be so great if you only wanted a few elements, but from a very large domain.
Suppose I wanted five numbers between one and ten million, without duplicates. Generating a list of ten million items, shuffling it, and then slicing off the first five would be a performance disaster! So let us have another try:
In [55]:
import random
def make_random_ints_no_dups(num, lower_bound, upper_bound):
"""
Generate a list containing num random ints between
lower_bound and upper_bound. upper_bound is an open bound.
The result list cannot contain duplicates.
"""
result = []
rng = random.Random()
for i in range(num):
while True:
candidate = rng.randrange(lower_bound, upper_bound)
if candidate not in result:
break
result.append(candidate)
return result
xs = make_random_ints_no_dups(5, 1, 10000000)
print(xs)
In [54]:
"""
def make_random_ints_no_dups(num, lower_bound, upper_bound):
"""
Generate a list containing num random ints between
lower_bound and upper_bound. upper_bound is an open bound.
The result list cannot contain duplicates.
"""
result = []
rng = random.Random()
while len(result) < num:
candidate = rng.randrange(lower_bound, upper_bound)
if candidate in result:
continue
result.append(candidate)
return result
make_random_ints_no_dups(5, 1, 1000)
"""
Out[54]:
This method is okay but still has some problems.
Can you see what's going to happen in the next case?
In [ ]:
xs = make_random_ints_no_dups(10, 1, 6) # Yikes!
In [61]:
from timeit import default_timer as timer
t1 = timer()
print("hi")
t2 = timer()
print(t2 - t1)
In [62]:
from timeit import default_timer as timer
def do_my_sum(xs):
sum = 0
for v in xs:
sum += v
return sum
sz = 10000000 # Lets have 10 million elements in the list
testdata = range(sz)
t0 = timer()
my_result = do_my_sum(testdata)
t1 = timer()
print("my_result = {0} (time taken = {1:.4f} seconds)"
.format(my_result, t1-t0))
t2 = timer()
their_result = sum(testdata)
t3 = timer()
print("their_result = {0} (time taken = {1:.4f} seconds)"
.format(their_result, t3-t2))
In [ ]:
def do_my_sum(xs):
sum = 0
for v in xs:
sum += v
return sum
sz = 10000000 # Lets have 10 million elements in the list
testdata = range(sz)
In [63]:
%%timeit
my_result = do_my_sum(testdata)
In [64]:
%%timeit
their_result = sum(testdata)
There are three important scopes in Python:
Python (like most other computer languages) uses precedence rules: the same name could occur in more than one of these scopes, but the innermost, or local scope, will always take precedence over the global scope, and the global scope always gets used in preference to the built-in scope. Let’s start with a simple example:
In [65]:
def range(n):
return 123*n
print(range(10)) # What will this print?
In [66]:
n = 10
m = 3
def f(n):
m = 7
return 2*n+m
print(f(5), n, m) # What about this one?
Now we know why we use a return in our functions: to pass between namespaces!
Variables defined inside a module are called attributes of the module. We’ve seen that objects have attributes too: for example, most objects have a doc attribute, some functions have a annotations attribute. Attributes are accessed using the dot operator (.).
In [ ]:
import math
x = math.sqrt(10)
In [ ]:
from math import cos, sin, sqrt
x = sqrt(10)
In [ ]:
from math import * # Import all the identifiers from math,
# adding them to the current namespace.
x = sqrt(10) # Use them without qualification.
In [ ]:
# Here's a freebie since I like you guys
import math as m
m.pi
In [ ]:
def area(radius):
import math
return math.pi * radius * radius
x = math.sqrt(10) # This gives an error
Open help for the calendar module.
import calendar
cal = calendar.TextCalendar() # Create an instance
cal.pryear(2012) # What happens here?
Observe that the week starts on Monday. An adventurous CompSci student believes that it is better mental chunking to have his week start on Thursday, because then there are only two working days to the weekend, and every week has a break in the middle. Read the documentation for TextCalendar, and see how you can help him print a calendar that suits his needs.
Find a function to print just the month in which your birthday occurs this year.
d = calendar.LocaleTextCalendar(6, "SPANISH")
d.pryear(2012)
print( (mymodule2.myage - mymodule1.myage) ==
(mymodule2.year - mymodule1.year) )
When you will run namespace_test.py you will see either True or False as output depending on whether or not you’ve already had your birthday this year.
What this example illustrates is that out different modules can both have attributes named myage and year. Because they’re in different namespaces, they don’t clash with one another. When we write namespace_test.py, we fully qualify exactly which variable year or myage we are referring to.
Add the following statement to mymodule1.py, mymodule2.py, and namespace_test.py from the previous exercise:
print("My name is", __name__)
if __name__ == "__main__":
print("This won't run if I'm imported.")
>>> import this
What does Tim Peters have to say about namespaces?
Give the Python interpreter’s response to each of the following from a continuous interpreter session:
>>> s = "If we took the bones out, it wouldn't be crunchy, would it?"
>>> s.split()
>>> type(s.split())
>>> s.split("o")
>>> s.split("i")
>>> "0".join(s.split("o"))
Be sure you understand why you get each result. Then apply what you have learned to fill in the body of the function below using the split and join methods of str objects:
```
def myreplace(old, new, s):
""" Replace all occurrences of old with new in s. """
...assert myreplace(",", ";", "this, that, and some other thing") == "this; that; and some other thing") assert myreplace(" ", "", "Words will now be separated by stars.") == "Wordswillnowbeseparatedby**stars.") ``` Your solution should pass the tests.