Advanced Python
In-Class Exercises, Session 7
Notes if you are using Jupyter Notebook: to call exit() from a notebook, please use sys.exit() (requires import sys). If a strange error occurs, it may be because Jupyter retains variables from all executed cells. To reset the notebook, click 'Restart Kernel' (the circular arrow) -- this will not undo any changes made. |
|
CLASS VARIABLES / ATTRIBUTES |
|
Ex. 7.1 | Identify the object and attribute in each of the bottom 4 code lines. |
import json
import sys
mylist = [1, 2, 3, 4]
mystr = 'hello'
fh = open('../pconfig.json')
# identify the object and attribute in each of the below
mylist.append(5)
mystr.upper()
json.load(fh)
sys.argv
|
|
Ex. 7.2 | Without calling any of the below attributes, print the type of each. |
import json
import sys
mylist = [1, 2, 3, 4]
mystr = 'hello'
fh = open('../pconfig.json')
# print the type of each of the following:
# mylist.append
# mystr.upper
# json.load
# sys.argv
|
|
Ex. 7.3 | What exception does this code cause Python to raise and why? |
mystr = '1, 2, 3, 4, 5'
mystr.append(5)
|
|
Ex. 7.4 | Use dir() on one or more of these objects and examine the result. |
import sys # module object
import json # module object
mylist = ['a', 'b', 'c'] # list object
mydict = {'a': 1, 'b': 2, 'c': 3} # dict object
mystr = 'hello' # str object
myint = 55 # int object
|
|
Ex. 7.5 | Use hasattr() in an if statement to check for an attribute within an object; if True, use getattr() to retrieve and print the attribute. |
import sys # retrieve the 'version' attribute
mylist = ['a', 'b', 'c'] # retrieve the 'append' attribute
|
|
Ex. 7.6 | Do is a class object; the class has a cval attribute assigned as a class variable. Investigate the class' attributes with the following tools: |
|
|
class Do:
cval = 5
def dothis(self):
print('done!')
|
|
Ex. 7.7 | obj is an instance of the Do() class; an attribute has been set in the instance, and the class also has its own attribute, set as a class variable. Investigate the instance and class attributes with the following tools: |
|
|
class Do():
cval = 10
def __init__(self):
self.oattr = 500 # setting an attribute in the instance
def dothis(self):
print('done!')
obj = Do()
|
|
Ex. 7.8 | class What inherits from class Do. Investigate the What instance attributes with the following tools: |
|
|
class Do:
dovar = 5
def dothis(self):
print('done!')
class What(Do):
whatvar = 10
def __init__(self):
self.instval = 500
z = What()
|
|
Ex. 7.9 | Again with class What inheriting from class Do, compare the results from dir() and the results from the .__dict__ attribute of the class. (No need to create an instance, simply work with the What class object.) |
class Do:
d = 5
def dothis(self):
print('done!')
class What(Do):
w = 10
def whatever(self):
print('chill!')
|
|
Ex. 7.10 | Again with class What inheriting from class Do, view the .__bases__ attribute of both the What class and the Do class. (Again, no need to create an instance.) |
class Do:
dval = 5
class What(Do):
wval = 10
|
|
Ex. 7.11 | Compare dir() of Do to dir() of object. Is one a subset of the other? |
class Do:
pass
|
|
Ex. 7.12 | Cause class Do to inherit from the builtin class list. See if the list .append attribute is available in the instance. |
class Do:
d = 5
def dothis(self):
print('done!')
|
|
Ex. 7.13 | Look at the .__bases__ attribute of the exception class ValueError; see if you can follow the chain of bases all the way up to object. |
Ex. 7.14 | Examine the return value from the following function calls: vars().keys(), locals().keys() (called from inside the function) and dir(builtins). |
import builtins
a = 5
b = [1, 2, 3]
print('== vars().keys() ==')
print(vars().keys())
print()
def do():
lvar = 500
print('== locals().keys() ==')
print(locals().keys())
print()
do()
print('== dir(builtins) ==')
print(dir(builtins))
|
|
Ex. 7.15 | Run the code to view the '.__doc__' attribute for the class and for the dothis() method. Now add "docstrings" (floating triple-quoted strings) just inside both the class and def statements. Run the program again to see how the .__doc__ attributes have changed. |
class Do:
d = 5
def dothis(self):
print('done!')
print(Do.__doc__)
print(Do.dothis.__doc__)
|
|
Ex. 7.16 | Compare dir(Do) with inspect.getmembers(Do). |
import inspect
class Do:
d = 5
def dothis(self):
print('done!')
|
|
Ex. 7.17 | Check to see whether Do.d and Do.dothis are callable. |
class Do:
d = 5
def dothis(self):
print('done!')
|
|
"MAGIC" METHODS |
|
Ex. 7.18 | Allow a class to "print" itself. Define a __str__(self) method that returns a string. Now print the object. |
class Value:
def __init__(self, val):
self.aaa = val
def getval(self):
return self.aaa
mynum = Value(10)
|
|
Ex. 7.19 | Allow an object to respond to subscripting. Define a __setitem__(self) method that takes two arguments (besides self) and prints the two arguments. Now attempt to subscript the object. |
class Value:
def __init__(self, val):
self.aaa = val
mynum = Value(10)
mynum['a'] = 55 # prints "called __setitem__(a, 55)"
|
|
Ex. 7.20 | Replace getval() with __getitem__(). The method should take one argument (besides self) and return the .aaa attribute. Now attempt to subscript the object. |
class Value:
def __init__(self, val):
self.aaa = val
def getval(self):
return self.aaa
mynum = Value('10')
val = mynum.getval() # 10
# uncomment after adding __getitem__
# val = mynum['whaeva'] # 10
|
|
INHERITING FROM BUILTINS |
|
Ex. 7.21 | Make IntList inherit from list and then treat it like a list. |
class IntList:
pass # means an empty block
x = IntList()
# uncomment once we are inheriting from list.
#x.append(5)
#x.append(3)
#print(x)
|
|
Ex. 7.22 | Add an append() method to IntList to specialize the method. Inside the method, only print a message; don't try to perform the append. |
class IntList(list):
pass
x = IntList()
x.append(5) # now appending 5!
x.append(3) # now appending 3!
print(x) # [5, 3]
|
|
Ex. 7.23 | Call list.append() from within the inheriting class. From inside the IntList .append() method, call the append() method on the object by calling it on the parent class. (Don't call it on self or you will set up an endless loop.) |
class IntList(list):
def append(self, item):
print(f'now appending {5}!')
x = IntList()
x.append(5) # now appending 5!
x.append(3) # now appending 3!
print(x) # []
|
|
TRAPPING AND RAISING EXCEPTIONS |
|
Ex. 7.24 | (Review) Trap the exception. First, run the script to see what exception is raised. Then, use a try/except statement to trap the error. In the except block, set xi to 1. |
x = input('please enter an integer: ')
xi = int(x)
print(f'{xi} * 2 = {xi * 2}')
|
|
PLEASE DO NOT USE except: BY ITSELF! This works, but we must always specify the exception we are expecting. To test, run the program once with "correct" input (i.e., numbers only) and then run it again with "bad" input (i.e., letters) to trigger the exception. |
|
Ex. 7.25 | (Review) Trap the exception. Again, determine the exception type that will occur if the user types in a key that doesn't exist in the dict. Then use the try/except to trap the exception (PLEASE DO NOT USE except: BY ITSELF). If the exception is raised, assign None to val. |
keydict = {'a': 1, 'b': 2, 'c': 3}
x = input('please enter a key for this dict: ')
val = keydict[x]
print(f'the value for {x} is {val}.')
|
|
Ex. 7.26 | Trap the exception, then raise a new one. Inside the except block, raise another KeyError, or any existing exception that you choose. |
keydict = {'a': 1, 'b': 2, 'c': 3}
x = input('please enter a key for this dict: ')
try:
val = keydict[x]
except KeyError:
print('uh-oh') # replace this code
print(f'the value for {x} is {val}.')
|
|
Ex. 7.27 | Without any reason, raise a ValueError exception. Choose your favorite exception! You don't have one? Then raise ZeroDivisionError. |
# your code here
|
|