The file circles.txt
contains measurements of circle radii. Your task is to write a script that reports the average area of the circles. You will not use the numpy
mean
function. Here are the requirements:
circles.txt
, read in the data, and convert the data to floats.myave
, that computes the average of the areas of the circles. At the very least, myave
should accept the list of radii as one argument and the circle function that you wrote in step 2 as another argument. There are other ways of doing this task, but I want you to do it this way.
In [10]:
with open("circles.txt","r") as f:
circles_text=f.read().split()
In [8]:
def mywave(radius):
return (float(radius)*float(radius)*3.14)
def myave(func, radii_list):
total_area=0
num_area=0
for radius in radii_list:
total_area+=func(radius)
num_area+=1
return (total_area/num_area)
print(myave(mywave,circles_text))
The goal of this problem is to write a simple bank account withdraw system. The problem is based off of one in Structure and Interpretation of Computer Programs.
Instructions: Do each part in a different cell block and clearly label each part.
Write a closure to make withdraws from a bank account. The outer function should be accept the initial balance as an argument (I'll refer to this argument as balance
in this problem statement, but you can call it whatever you want). The inner function should accept the withdraw amount as an argument and return the new balance.
NOTE1: For this part, do not try to reassign balance
in the inner function. Just see what happens when you return a new balance. You can store the new balance in a new name (call it new_bal
if you want) or just return the new balance directly.
NOTE2: You may want to check for basic exceptions (e.g. attempting to withdraw more than the current balance).
Once you write your functions, demo them in your notebook as follows:
wd = make_withdraw(init_balance)
wd(withdraw_amount)
wd(new_withdraw_amount)
You should observe that this does not behave correctly. Why not?
You can fix things up by updating balance
within the inner function. But this won't work. Try it out and explain why it doesn't work. Try to use the language that we used in lecture. Hint: Python Execution Model.
Now, make just one small change to your code from Part 2. Declare balance
as a nonlocal variable using the nonlocal keyword. That is, before you get to the inner function, say nonlocal balance
. Here's some information on the nonlocal
statement: nonlocal
.
Now test things out like you did in Part 1. It should be behaving correctly now.
Finally, visualize your code with Python Tutor and embed your visualization in your notebook. Pay attention to the variable balance
.
In [1]:
def make_withdraw(balance):
def inner_func(withdraw):
if (balance<withdraw):
raise ValueError("balance is smaller than withdraw")
return(balance-withdraw)
return(inner_func)
initial_balance=10
withdraw_amount=2
new_withdraw_amount=4
wd=make_withdraw(initial_balance)
wd(withdraw_amount)
wd(new_withdraw_amount)
Out[1]:
In [2]:
def make_withdraw(balance):
def inner_func(withdraw):
p=balance-withdraw
if (balance<withdraw):
raise ValueError("balance is smaller than withdraw")
balance=p
return(p)
return(inner_func)
initial_balance=10
withdraw_amount=2
new_withdraw_amount=4
wd=make_withdraw(initial_balance)
wd(withdraw_amount)
wd(new_withdraw_amount)
The reason is that balance refers to a immutable object. The scope of the object that balance binds to is within the function. After the function is called, the balance variable is skilled. So it won't affect the next call.
In [5]:
def make_withdraw(balance):
def inner_func(withdraw):
nonlocal balance
p=balance-withdraw
if (balance<withdraw):
raise ValueError("balance is smaller than withdraw")
balance=p
return(p)
return(inner_func)
initial_balance=10
withdraw_amount=2
new_withdraw_amount=4
wd=make_withdraw(initial_balance)
wd(withdraw_amount)
wd(new_withdraw_amount)
Out[5]:
In [12]:
from IPython.display import HTML # Allows us to embed HTML into our notebook.
HTML('<iframe width="800" height="400" frameborder="0" src="http://pythontutor.com/visualize.html#code=def%20make_withdraw%28balance%29%3A%20%20%0A%20%20%20%20%0A%20%20%20%20def%20inner_func%28withdraw%29%3A%0A%20%20%20%20%20%20%20%20nonlocal%20balance%0A%20%20%20%20%20%20%20%20p%3Dbalance-withdraw%0A%20%20%20%20%20%20%20%20if%20%28balance%3Cwithdraw%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError%28%22balance%20is%20smaller%20than%20withdraw%22%29%0A%20%20%20%20%20%20%20%20balance%3Dp%0A%20%20%20%20%20%20%20%20return%28p%29%0A%20%20%20%20return%28inner_func%29%0Ainitial_balance%3D10%0Awithdraw_amount%3D2%0Anew_withdraw_amount%3D4%0Awd%3Dmake_withdraw%28initial_balance%29%0Awd%28withdraw_amount%29%0Awd%28new_withdraw_amount%29&cumulative=false&curInstr=0&heapPrimitives=false&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe>')
Out[12]:
Let's return to the data from Problem 1. Write two functions: 1.) The first function should return the average circle radius (you can re-use the one you already wrote if you'd like, but you might need to update it slightly for this problem) and 2.) The second function should just use numpy
to compute the average.
Write a decorator to time the evaluation of each function. You can use the timing decorator from lecture.
circles.txt
, compute the area from each point, and store that area in an array. Do you know why this is more fair? Also, try to not use append
. Instead, preallocate space for your area
list/array.my_ave
function should accept your areas data as a list. Remember, to allocate a list you should do [0.0]*N
: if you use such a construct your list will will be filled in with zeros.np_ave
function should accept your areas data as a numpy
array. To allocate a numpy
array do areas_np = np.zeros(len(radii))
.
In [10]:
# First we write our timer function
import time
def timer(f):
def inner(*args):
t0 = time.time()
output = f(*args)
elapsed = time.time() - t0
print("Time Elapsed", elapsed)
return output
return inner
In [19]:
import cmath
def my_ave(area_list):
return myave(area_list)
In [20]:
import cmath
def np_ave(area_np):
return myave(area_np)
In [21]:
import numpy as np
def mywave(radius):
return (float(radius)*float(radius)*3.14)
def myave(area_list):
total_area=0
num_area=0
for area in area_list:
total_area+=area_list[num_area]
num_area+=1
return (total_area/num_area)
with open("circles.txt","r") as f:
circles_text=f.read().split()
area_list=[0.0]*len(circles_text)
area_np=np.zeros(len(circles_text))
num_radius=0
for radius in circles_text:
area_list[num_radius]=mywave(circles_text[num_radius])
area_np[num_radius]=mywave(circles_text[num_radius])
num_radius+=1
print(my_ave(area_list))
print(np_ave(area_np))
In [22]:
timer1=timer(my_ave)
timer2=timer(np_ave)
print(timer1(area_list))
print(timer1(area_np))
Write a decorator to check if a quantity returned from a function is positive. An exception should be raised if the quantity is not positive.
Write three functions and decorate them with your decorator:
Show that your decorator behaves correctly. That is, for each function, show two cases (where applicable):
In [34]:
def positive_check(f):
def inner(*args):
if (f(*args)<0):
raise ValueError("The result is negative")
else:
return True
return inner
In [35]:
def function1(a,b,c):
return(b*b-4*a*c)
In [36]:
def function2(a):
if a>0:
return a
else:
return -a
In [37]:
def function3(a):
return (a+1)*(a-2)
In [38]:
check1=positive_check(function1)
check2=positive_check(function2)
check3=positive_check(function3)
print(check1(1,3,1))
print(check2(1))
print(check3(3))
print(check3(0))
print(check1(1,1,1))
print(check2(0))