EXCEPTIONS
Let's write a code that takes number as an input:
answer=input("Enter a number:") print(int(answer))
If the user enters number the program works:
Enter a number:4 4
what happens if the user makes a mistake?
Enter a number:er Traceback (most recent call last): File "simple_exceptions.py", line 2, in <module> print(int(answer)) ValueError: invalid literal for int() with base 10: 'er'
We could add exception handling mechanism to prevent such errors (note the matching error values in red between the above example and below):
answer=input("Enter a number:") try: print(int(answer)) except ValueError: print("It appears that you did not enter a number!")
Now the bad input is handled:
Enter a number:r It appears that you did not enter a number!
You do not need to specify the error type in the except handler, the programs below omit the error type. We will revisit this shortly.
What if we had other statements before we handled the exception and after the line that was at risk?
answer=input("Enter a number:") try: print("1") print(int(answer)) print("2") except: print("It appears that you did not enter a number!")
good input, no exception:
Enter a number:45 1 45 2
Bad input, the last print statement is skipped:
Enter a number:eee 1 It appears that you did not enter a number!
Let's look at handling multiple bad inputs. In the program below, what will happen if one of the inputs is bad?
answer1=input("Enter a number:") answer2=input("Enter a number:") try: print(int(answer1)) print(int(answer2)) except: print("It appears that you did not enter a number!")
Write a program that will continue asking the user to enter a number until the input is successful.
def get_number(): while (True): try: ans=input("Enter a number:") return int(ans) except: print("It appears that you did not enter a number!") print(get_number()) print(get_number())
Recording for the lecture up to this point is here
Let's write a program that asks user for a filename, opens that file and adds up all the numbers in the file.
What type of errors can we run into with this program?
fileName = input("Enter the file name:") total = 0 f = open(fileName) while(True): l = f.readline() if(not l): break total+= int(l) f.close() print(total)
We want to give user appropriate feedback with every type of error, so we are back looking at the error types. Let's apply the exception knowledge we have so far:
- the file may not exist
- file may contain bad data
We want to give user appropriate feedback with every type of error, so we are back looking at the error types. Let's apply the exception knowledge we have so far:
fileName = input("Enter the file name:") total = 0 try: f = open(fileName) while(True): l = f.readline() if(not l): break total += int(l) f.close() except: print("Error Occured") print("The total is:", total)
good run:
Enter the file name:numbers.txt The total is: 21
bad run
Enter the file name:pp.txt Error Occurred
With the bad run, we are not explaining user what error has occurred, which is not a good practice. Let's look at how we can provide different feedback for various errors.
We will start with ValueError, because we have seen how to handle that already.
To test it, I modified the input file to contain a bad input:
1 2 3 4 5 6 rr
now let's modify the code (I had to strip the new line character that was coming along with the line):
fileName = input("Enter the file name:") total = 0 try: f = open(fileName) while(True): l = f.readline() if(not l): break total += int(l) f.close() except ValueError: print("Value error:", l.strip(), " is not a number") except: print("Error Occured") print("The total is:", total)
Enter the file name:numbers.txt Value error: rr is not a number The total is: 21
Good, that works, what about a bad file name now? to look at the error we will get, let's remove the "catchall" except: statement:
fileName = input("Enter the file name:") total = 0 try: f = open(fileName) while(True): l = f.readline() if(not l): break total += int(l) f.close() except ValueError: print("Value error:", l.strip(), " is not a number") print("The total is:", total)
Now we should get the error if bad file name is entered:
Enter the file name:ff Traceback (most recent call last): File "add_numbers.py", line 5, in <module> f = open(fileName) FileNotFoundError: [Errno 2] No such file or directory: 'ff'
Modify the code to handle the FileNotFound error:
fileName = input("Enter the file name:") total = 0 try: f = open(fileName) while(True): l = f.readline() if(not l): break total += int(l) f.close() except ValueError: print("Value error:", l.strip(), " is not a number") except FileNotFoundError: print("Could not find file:", fileName) print("The total is:", total)
Enter the file name:ff Could not find file: ff The total is: 0
What about catchall generic except statement? should we not use it? - you can always use it for general, unpredictable cases, but make sure to put it at the bottom after all the specific handlers. Let's take a look at this, what happens if generic except statement is prior to other except clauses?
fileName = input("Enter the file name:") total = 0 try: f = open(fileName) while(True): l = f.readline() if(not l): break total += int(l) f.close() except: print("UNKNOWN ERROR") except ValueError: print("Value error:", l.strip(), " is not a number") except FileNotFoundError: print("Could not find file:", fileName) print("The total is:", total)
SyntaxError: default 'except:' must be last
So let's move it to the bottom of the except block and think of what other error can we trigger here?
How about instead of file name we enter a directory name?
fileName = input("Enter the file name:") total = 0 try: f = open(fileName) while(True): l = f.readline() if(not l): break total += int(l) f.close() except ValueError: print("Value error:", l.strip(), " is not a number") except FileNotFoundError: print("Could not find file:", fileName) except: print("UNKNOWN ERROR") print("The total is:", total)
I created a directory named "directory" and ran the program:
Enter the file name:directory UNKNOWN ERROR The total is: 0
Sometimes we can predict the most common errors, but we still want to give the user as much information as possible about unpredicted error and let the program continue to run. We can do that by assigning the Exception to a variable and then interacting with the variable:
fileName = input("Enter the file name:") total = 0 try: f = open(fileName) while(True): l = f.readline() if(not l): break total += int(l) f.close() except ValueError: print("Value error:", l.strip(), " is not a number") except FileNotFoundError: print("Could not find file:", fileName) except Exception as e: print(type(e)) print(e.args) print(e) print("The total is:", total)
Enter the file name:directory <class 'IsADirectoryError'> (21, 'Is a directory') [Errno 21] Is a directory: 'directory' The total is: 0
You can use traceback to print entire stack trace
import traceback fileName = input("Enter the file name:") total = 0 try: f = open(fileName) while(True): l = f.readline() if(not l): break total += int(l) f.close() except ValueError: print("Value error:", l.strip(), " is not a number") except FileNotFoundError: print("Could not find file:", fileName) except Exception as e: traceback.print_exc() print("The total is:", total)
Enter the file name:t Traceback (most recent call last): File "lecture8.py", line 6, in <module> f = open(fileName) IsADirectoryError: [Errno 21] Is a directory: 't' The total is: 0
You can catch multiple errors with one except clause
fileName = input("Enter the file name:") total = 0 try: f = open(fileName) while(True): l = f.readline() if(not l): break total += int(l) f.close() except (ValueError, FileNotFoundError): print("I knew this was gonna happen!") except Exception as e: print("I couldn't imagine this would ever happen, but your program did not crash! (yet, happy halloween!)") print("The total is:", total)
EATING AN EXCEPTION
The lazy human nature often holds the developers from "letting someone know" an exception happened,
take a look at the code below
take a look at the code below
fileName = input("Enter the file name:") total = 0 try: f = open(fileName) while(True): l = f.readline() if(not l): break total += int(l) except (ValueError, FileNotFoundError): pass except Exception as e: print("I couldn't imagine this would ever happen, but your program did not crash! (yet, happy halloween!)") print("The total is:", total)
If the exception did happen, we would not know anything about it. No one will ever know there was an issue! How would you like your local media doing this to you?
this is referred to as "eating an exception" and generally is a bad practice. Sometimes, very rarely, you truly don't need to do anything about it, but most of the time you'd want to either log it or display it to the user or store in the error repository
FINALLY
Sometimes wether an exception happened or not, you may want to do some cleanup action. If you remember, the statements within the try block are skipped when the exception happened before them, that may cause some important actions to be skipped.
One of such actions is closing files.
Let's look at our number reader program again, if we come across an error, will the file be closed?
One of such actions is closing files.
Let's look at our number reader program again, if we come across an error, will the file be closed?
fileName = input("Enter the file name:") total = 0 try: f = open(fileName) while(True): l = f.readline() if(not l): break total += int(l) print("closing the file") f.close() except ValueError: print("Value error:", l.strip(), " is not a number") except FileNotFoundError: print("Could not find file:", fileName) print("The total is:", total)
The file will not be closed, here is a sample output:
Enter the file name:numbers.txt Value error: rr is not a number The total is: 21
fileName = input("Enter the file name:") total = 0 try: f = open(fileName) while(True): l = f.readline() if(not l): break total += int(l) except ValueError: print("Value error:", l.strip(), " is not a number") except FileNotFoundError: print("Could not find file:", fileName) finally: print("closing the file") f.close() print("The total is:", total)
Now the code gets executed regardless of an error:
Enter the file name:numbers.txt Value error: rr is not a number closing the file The total is: 21
The code will be executed if the program does not encounter an error
What happens if the user enters a wrong file name?
We will now have an exception generated in the finally block itself! how can we mitigate this?
We could add another exception handler around the close statement:
What happens if the user enters a wrong file name?
We will now have an exception generated in the finally block itself! how can we mitigate this?
We could add another exception handler around the close statement:
fileName = input("Enter the file name:") total = 0 try: f = open(fileName) while(True): l = f.readline() if(not l): break total += int(l) except ValueError: print("Value error:", l.strip(), " is not a number") except FileNotFoundError: print("Could not find file:", fileName) finally: print("closing the file") try: f.close() except: pass print("The total is:", total)
Enter the file name:ee Could not find file: ee closing the file The total is: 0
We already covered a better way of handling this could be using 'with' keyword, in this case Python will take care of the clean up action for you
fileName = input("Enter the file name:") total = 0 try: with open(fileName) as f: while(True): l = f.readline() if(not l): break total += int(l) except ValueError: print("Value error:", l.strip(), " is not a number") except FileNotFoundError: print("Could not find file:", fileName) print("The total is:", total)
finally block can follow the try block directly:
try: 3/0 finally: print("in finally block")
in finally block Traceback (most recent call last): File "try_finally.py", line 2, in 3/0 ZeroDivisionError: division by zero
The try ... except statement has an optional else clause. An else block has to be positioned after all the except clauses. An else clause will be executed if the try clause doesn't raise an exception.
else clause must follow all except blocks and appear before finally block
Let's take a look at this code:
try: 3/0 except ZeroDivisionError: print("in except block") else: print("in else block") finally: print("in finally block")
in except block in finally block
Now, let's change it into a non-error try block
try: print("in try block") except ZeroDivisionError: print("in except block") else: print("in else block") finally: print("in finally block")
in try block in else block in finally block
RAISING EXCEPTIONS
try: raise SyntaxError("my own error") except ZeroDivisionError: print("caught division by zero") except SyntaxError: print("caught syntax error") else: print("in else block") finally: print("in finally block")
caught syntax error in finally block
EXCEPTION STACK TRACE
Exceptions get forwarded over the call stack. Take a look at the following code:
def function3(param3): raise SyntaxError("ERROR:"+param3) def function2(param2): function3(param2) def function1(param1): function2(param1) function1("Test")
Traceback (most recent call last): File "stack_trace.py", line 10, in function1("Test") File "stack_trace.py", line 8, in function1 function2(param1) File "stack_trace.py", line 5, in function2 function3(param2) File "stack_trace.py", line 2, in function3 raise SyntaxError("ERROR:"+param3) SyntaxError: ERROR:Test
Let's take a look at handling the exception at different execution blocks of our stack:
def function3(param3): print("in function3") raise SyntaxError("ERROR:"+param3) print("after exception 3") def function2(param2): print("in function2") function3(param2) print("after exception 2") def function1(param1): print("in function1") function2(param1) print("after exception 1") try: function1("Test") except: print("caught at main")
in function1 in function2 in function3 caught at main
Conclusion: we caught the exception, but all the statements "after exception ..." were skipped
Modify your program to get following outputs:
Modify your program to get following outputs:
Output1: in function1 in function2 in function3 after exception 1 Output2: in function1 in function2 in function3 after exception 2 after exception 1 Output3: in function1 in function2 in function3 after exception 3 after exception 2 after exception 1
CLASSES AND OBJECTS
Classes describe objects. Everything in Python is a class:
>>> a=4 >>> type(a) <class 'int'> >>> s="" >>> type(s) <class 'str'> >>> l=[] >>> type(l) <class 'list'> >>> b=True >>> type(b) <class 'bool'>
Let's build a simple class
class Circle: pass
and this is all you need to create a class in Python, you don't need to place it in a separate file, you don't need a constructor or destructor for it.
class Circle: #class declaration pass myCircle=Circle() #object creation myCircle.radius=3 print(myCircle.radius)
3
Instance variables are the fields of object and are described by the fields in the class
You can initialize the variables using __init__ method, which is called upon the object creation. The first argument in any instance method has to be self, which is the reference to the object
class Circle: def __init__(self): self.radius = 1 myCircle=Circle() print(myCircle.radius)
1
Let's build a Robot class.
The robot will have a name that can be initiated when the Robot is created, Robot can tell its name (a method to tell the Robot's name)
The robot will have a name that can be initiated when the Robot is created, Robot can tell its name (a method to tell the Robot's name)
class Robot: def __init__(self, name): self.name = name def tellName(self): print("h-e-l-o! My Name is ", self.name) gordon = Robot("Gordon") ron = Robot("Ron") gordon.tellName() ron.tellName()
h-e-l-o! My Name is Gordon h-e-l-o! My Name is Ron
if you omit self as a parameter, you will get an error when calling the method on an instantiated object, because Python will recognize that it's an instance of an object and will try to pass the object reference to the method, but there will be nothing to receive that reference:
class Robot: def __init__(self, name): self.name = name def tellName(): print("h-e-l-o! My Name is ", self.name) gordon = Robot("Gordon") ron = Robot("Ron") gordon.tellName() ron.tellName()
Traceback (most recent call last): File "lecture8.py", line 12, in <module> gordon.tellName() TypeError: tellName() takes 0 positional arguments but 1 was given
__dict__
Class instances hold their fields and the values in a dictionary __dict__
class Circle: def __init__(self): self.r = 3 c = Circle() print(c.__dict__)
{'r': 3}
Note that creating a variable inside __init__ without a reference to self doesn't create an instance variable, instead it creates just a method local variable
class Circle: def __init__(self): self.r = 3 f=6 c = Circle() print(c.__dict__)
{'r': 3}
Python's toString equivalent
Let's look at the example below, what would you expect the print statement to print?
class Circle: def __init__(self): self.r = 3 c = Circle() print(c)
<__main__.Circle object at 0x10053e7b8>
If you want a print statement on your objects to have a different meaning than the class name and the memory location (most of the time you do), you can override the __str__ method:
class Circle: def __init__(self): self.r = 3 def __str__(self): return str(self.r) c = Circle() print(c)
3
PRIVATE, PROTECTED AND PUBLIC VARIABLES
Naming |
Type |
Meaning |
---|---|---|
name | Public |
These attributes can be freely used inside or outside of a class
definition. |
_name | Protected |
Protected attributes should not be used outside of the class
definition, unless inside of a subclass definition. The "should not" is not enforced by Python, just a convention - a friendly ask to other developers
|
__name | Private |
Supposed to be inaccessible and invisible except inside of the class definition itself. BUT still can be! (see below) So again, more of a convention |
class Circle: def __init__(self): self.r = 3 self._r = 6 self.__r = 9 def __str__(self): return str(self.r)+" "+str(self._r)+" "+str(self.__r) c = Circle() print(c) c.r = 4 c._r = 8 c.__r=10 print(c)
3 6 9 4 8 9
it looks like the modification to __r did not pickup, but let's look at the code below:
class Circle: def __init__(self): self.r = 3 self._r = 6 self.__r = 9 def __str__(self): return str(self.r)+" "+str(self._r)+" "+str(self.__r) c = Circle() print(c.__dict__) c.r = 4 c._r = 8 c._Circle__r = 10 print(c.__dict__)
{'_Circle__r': 9, 'r': 3, '_r': 6} {'_Circle__r': 10, 'r': 4, '_r': 8}
print it from __str__:
class Circle: def __init__(self): self.r = 3 self._r = 6 self.__r = 9 def __str__(self): return str(self.r)+" "+str(self._r)+" "+str(self.__r) c = Circle() print(c) c.r = 4 c._r = 8 c._Circle__r = 10 c.__r=100 print(c)
3 6 9 4 8 10
__del__
__del__ is called when an object is being destroyed
class Circle: def __init__(self): self.r = 3 def __str__(self): return str(self.r) def __del__(self): print("Circle dying!") c = Circle() del c
Circle dying!
you do not need to explicitly delete the object, you can just make it eligible for garbage collection:
import time class Circle: def __init__(self): self.r = 3 def __str__(self): return str(self.r) def __del__(self): print("Circle dying!") c = Circle() c = None time.sleep(10)
Circle dying!
Sometimes we need to be able to compare the objects, like in the code below
class Circle: def __init__(self, r): self.r = r c1 = Circle(4) c2 = Circle(8) print(c1,"<",c2,":", c1 < c2)
Traceback (most recent call last): File "classes.py", line 8, in <module> print(c1,"<",c2,":", c1 < c2) TypeError: unorderable types: Circle() < Circle()
Here is what we can do:
class Circle: def __init__(self, r): self.r = r def __str__(self): return "Circle with radius "+str(self.r) def __lt__ (self, other): return self.r < other.r def __gt__ (self, other): return other.__lt__(self) def __eq__ (self, other): return self.r == other.r def __ne__ (self, other): return not self.__eq__(other) c1 = Circle(4) c2 = Circle(8) print(c1,"<",c2,":", c1 < c2) print(c1,">",c2,":", c1 > c2) print(c1,"==",c2,":", c1 == c2) print(c1,"!=",c2,":", c1 != c2)
Circle with radius 4 < Circle with radius 8 : True Circle with radius 4 > Circle with radius 8 : False Circle with radius 4 == Circle with radius 8 : False Circle with radius 4 != Circle with radius 8 : True
HASHING
By default, Python provides you with hashing:
class Circle: def __init__(self, r, color): self.r = r self.color=color def __str__(self): return "Circle with radius "+str(self.r) c1 = Circle(4, "red") c2 = Circle(8, "blue") print(hash(c1))
-9223372036584230305
However, once you implement your own __eq__ , you must implement your own hash function as well:
class Circle: def __init__(self, r, color): self.r = r self.color=color def __str__(self): return "Circle with radius "+str(self.r) def __eq__ (self, other): return self.r == other.r c1 = Circle(4, "red") c2 = Circle(8, "blue") print(hash(c1))
Traceback (most recent call last): File "classes.py", line 17, in <module> print(hash(c1)) TypeError: unhashable type: 'Circle'
you can use hash function in the __eq__method itself:
class Circle: def __init__(self, r, color): self.r = r self.color=color def __hash__(self): return hash(self.color+str(self.r)) def __str__(self): return "Circle with radius "+str(self.r) def __eq__ (self, other): return hash(self) == hash(other) def __ne__ (self, other): return not self.__eq__(other) c1 = Circle(4, "red") c2 = Circle(8, "blue") c3 = Circle(4, "red") print(c1 == c2) print(c1 == c3)
False True
Sorting custom objects and __repr__
providing comparison functions will let your objects be sortable as well (try removing the comparison functions in the code below)
__str__ will not be called to print the objects inside the list when list is being called in the code below, in this case __repr__ can be used
without
class Circle: def __init__(self, r, color): self.r = r self.color=color def __str__(self): return "Circle with radius "+str(self.r) def __lt__ (self, other): return self.r < other.r def __gt__ (self, other): return other.__lt__(self) def __eq__ (self, other): return self.r == other.r def __ne__ (self, other): return not self.__eq__(other) c1 = Circle(4, "red") c2 = Circle(8, "blue") print(c1) print(c2) lst=[c2, c1] print(lst) lst.sort() print(lst)
Circle with radius 4 Circle with radius 8 [<__main__.Circle object at 0x7fc95affa210>, <__main__.Circle object at 0x7fc95aff1f90>] [<__main__.Circle object at 0x7fc95aff1f90>, <__main__.Circle object at 0x7fc95affa210>]
with __repr__
class Circle: def __init__(self, r, color): self.r = r self.color=color def __str__(self): return "Circle with radius "+str(self.r) def __repr__(self): return "Circle with radius "+str(self.r) def __lt__ (self, other): return self.r < other.r def __gt__ (self, other): return other.__lt__(self) def __eq__ (self, other): return self.r == other.r def __ne__ (self, other): return not self.__eq__(other) c1 = Circle(4, "red") c2 = Circle(8, "blue") print(c1) print(c2) lst=[c2, c1] print(lst) lst.sort() print(lst)
Circle with radius 4 Circle with radius 8 [Circle with radius 8, Circle with radius 4] [Circle with radius 4, Circle with radius 8
CLASS VARIABLES
Class variables are variables that don't belong to a specific instance (Object) of a class, but belong to the Class and hence are shared. In the example below,
radius is a class variable and
r is an instance variable
radius is a class variable and
r is an instance variable
class Circle: radius = 4 def __init__(self): self.r = 3 myCircle1=Circle() print(myCircle1.radius) print(myCircle1.r) myCircle2=Circle() print(myCircle2.radius) print(myCircle2.r)
4 3 4 3
So far they look similar, they start acting differently when we try to access them by referencing the class, vs referencing instance:
class Circle: radius = 4 def __init__(self): self.r = 3 myCircle1=Circle() print(myCircle1.radius) print(myCircle1.r) myCircle2=Circle() print(myCircle2.radius) print(myCircle2.r) print(Circle.radius) # this will print successfully, because class Circle does have a radius print(Circle.r) #this will result in an error, because r is not a class level variable
4 3 4 3 4 Traceback (most recent call last): File "lecture8.py", line 17, in <module> print(Circle.r) #this will result in an error, because r is not a class level variable AttributeError: type object 'Circle' has no attribute 'r'
Here is when things get tricky:
class Circle: radius = 4 def __init__(self): self.r = 3 myCircle1=Circle() print(myCircle1.radius) print(myCircle1.r) myCircle2=Circle() print(myCircle2.radius) print(myCircle2.r) Circle.radius = 5 # this modifies the class variable radius to 5 print(myCircle1.radius) # prints 5 print(myCircle2.radius) # prints 5 myCircle1.radius = 100 #this DOES NOT modify the class variable radius, #but creates an instance variable radius for MyCircle1 (only) print(myCircle1.radius) # prints 100 print(myCircle2.radius) #prints 5 print(myCircle1.__class__.radius) # this prints the class variable 5 print(myCircle1.radius) # this prints the instance variable 100
4 3 4 3 5 5 100 5 5 100
Which shows that if there are instance and class variables with the same name, the instance variable is preferred by Python.
If you want to access the class variables within the class definition, you still need to use the class reference, like in the example below:
class Circle: radius = 4 def __init__(self): Circle.radius = 100000 print(Circle.radius) myCircle = Circle() print(myCircle.radius) print(Circle.radius)
4 100000 100000
STATIC METHODS
Static methods are methods that can be invoked through the reference to the class instance.
Python does not pass a reference to the object for such methods
To declare a static method, use @staticmethod decorator
Python does not pass a reference to the object for such methods
To declare a static method, use @staticmethod decorator
class Circle: all_circles = [] def __init__(self, color): self.color=color @staticmethod def print_circles(): if (len(Circle.all_circles) < 1): print("No Circles") return print("Here are the Circles:") for c in Circle.all_circles: print(c.color) Circle.print_circles() c1 = Circle("red") Circle.all_circles.append(c1) Circle.all_circles.append(Circle("blue")) Circle.all_circles.append(Circle("green")) Circle.print_circles() c1.print_circles()
No Circles Here are the Circles: red blue green Here are the Circles: red blue green
and here is what would happen if the @staticmethoddecorator was missing,
Python would try to send the reference to self to the method
class Circle: all_circles = [] def __init__(self, color): self.color=color def print_circles(): if (len(Circle.all_circles) < 1): print("No Circles") return print("Here are the Circles:") for c in Circle.all_circles: print(c.color) Circle.print_circles() c1 = Circle("red") Circle.all_circles.append(c1) Circle.all_circles.append(Circle("blue")) Circle.all_circles.append(Circle("green")) Circle.print_circles() c1.print_circles()
No Circles Here are the Circles: red blue green Traceback (most recent call last): File "lecture8.py", line 25, in <module> c1.print_circles() TypeError: print_circles() takes 0 positional arguments but 1 was given
CLASS METHODS
Class methods get passed a reference to the class and return the class
class Circle: def __init__(self, color, size): self.color=color self.size=size @classmethod def from_size(cls, size): return cls("black", size) c1 = Circle("red", 13) c2 = Circle.from_size(10) print(c1.color) print(c1.size) print(c2.color) print(c2.size)
red 13 black 10
INHERITANCE
Inheritance is a way of re-using the class structure. Given two classes that share some of the structure, you can create third class, which the two classes can subclass (inherit from). Let's look at the following two classes:
class Circle: def __init__(self, x, y, color, radius): self.x = x self.y = y self.color=color self.radius = radius class Square: def __init__(self, x, y, color, side): self.x = x self.y = y self.color=color self.side = side c = Circle(3, 4, "red", 2) s = Square(5, 6, "blue", 3)
Since x, y, and color components of the two shapes are common, we can delegate that common structure/functionality to another class, class Shape in this case. We move the x,y and color to class Shape, subclass Circle and Square from Shape and then reference the superclass (parent) through a call to super()
class Shape: def __init__(self, x, y, color): self.x = x self.y = y self.color=color class Circle(Shape): def __init__(self, x, y, color, radius): super().__init__(x,y,color) self.radius = radius class Square(Shape): def __init__(self, x, y, color, side): super().__init__(x,y,color) self.side = side c = Circle(3, 4, "red", 2) print("CIRCLE: x={}, y={}, color={}, radius={}".format(c.x, c.y, c.color, c.radius)) s = Square(5, 6, "blue", 3) print("SQUARE: x={}, y={}, color={}, side={}".format(s.x, s.y, s.color, s.side))
CIRCLE: x=3, y=4, color=red, radius=2 SQUARE: x=5, y=6, color=blue, side=3
Method inheritance works in a similar manner, where you can define a method in a parent class which is accessible by the subclasses (children) of that class. Let's take a look at the code below, the print_color() method of the Shape class is available to the objects of the Circle and Square child classes
class Shape: def __init__(self, x, y, color): self.x = x self.y = y self.color=color def print_color(self): print("COLOR=", self.color) class Circle(Shape): def __init__(self, x, y, color, radius): super().__init__(x,y,color) self.radius = radius class Square(Shape): def __init__(self, x, y, color, side): super().__init__(x,y,color) self.side = side c = Circle(3, 4, "red", 2) c.print_color() s = Square(5, 6, "blue", 3) s.print_color()
COLOR= red COLOR= blue
You can have a method with the same name in the child class that will override the parent method
class Shape: def __init__(self, x, y, color): self.x = x self.y = y self.color=color def print_color(self): print("COLOR=", self.color) class Circle(Shape): def __init__(self, x, y, color, radius): super().__init__(x,y,color) self.radius = radius def print_color(self): print("CIRCLE COLOR=", self.color) class Square(Shape): def __init__(self, x, y, color, side): super().__init__(x,y,color) self.side = side c = Circle(3, 4, "red", 2) c.print_color() s = Square(5, 6, "blue", 3) s.print_color()
CIRCLE COLOR= red COLOR= blue
You can utilize the super() class to call the methods of the superclass (parent)
class Shape: def __init__(self, x, y, color): self.x = x self.y = y self.color=color def print_color(self): print("COLOR=", self.color) class Circle(Shape): def __init__(self, x, y, color, radius): super().__init__(x,y,color) self.radius = radius #method overriding def print_color(self): print("CIRCLE") super().print_color() class Square(Shape): def __init__(self, x, y, color, side): super().__init__(x,y,color) self.side = side c = Circle(3, 4, "red", 2) c.print_color() s = Square(5, 6, "blue", 3) s.print_color()
CIRCLE COLOR= red COLOR= blue
HOMEWORK (Optional, not graded)
Program1:
You are building a game for kids. The game will give 3 fun fact hints and the user has to guess what animal it is.
Your main program must look like the code I will paste below. Animal must be a class.
e = Animal("elephant")
t = Animal("tiger")
b = Animal("bat")
e.guess_who_am_i()
t.guess_who_am_i()
b.guess_who_am_i()
Sample Output
I will give you 3 hints, guess what animal I am
I have exceptional memory
Who am I?:dolphin
Nope, try again!
I am the largest land-living mammal in the world
Who am I?:elephant
You got it! I am elephant
I will give you 3 hints, guess what animal I am
I am the biggest cat
Who am I?:lion
Nope, try again!
I come in black and white or orange and black
Who am I?:tiger
You got it! I am tiger
I will give you 3 hints, guess what animal I am
I use echo-location
Who am I?:human
Nope, try again!
I can fly
Who am I?:butterfly
Nope, try again!
I see well in dark
Who am I?:cat
Nope, try again!
I'm out of hints! The answer is: bat
Program2:
This is a learning game for kids to learn Integer Divisions. The user will be given a problem and should answer it. Provide a "Correct" or "Incorrect" feedback, handle bad input using Exceptions.
Following code will give you random integer 0-5:
from random import randrange
a = randrange(5)
Sample Output:
INTEGER DIVISIONS
6/3=2
CORRECT!
12/4=3
CORRECT!
4/2=4
INCORRECT!
0/1=e
Please enter Integers Only!
0/0=4
INCORRECT!
1/1=1
CORRECT!
0/2=0
CORRECT!
4/1=r
Please enter Integers Only!
You are building a game for kids. The game will give 3 fun fact hints and the user has to guess what animal it is.
Your main program must look like the code I will paste below. Animal must be a class.
e = Animal("elephant")
t = Animal("tiger")
b = Animal("bat")
e.guess_who_am_i()
t.guess_who_am_i()
b.guess_who_am_i()
Sample Output
I will give you 3 hints, guess what animal I am
I have exceptional memory
Who am I?:dolphin
Nope, try again!
I am the largest land-living mammal in the world
Who am I?:elephant
You got it! I am elephant
I will give you 3 hints, guess what animal I am
I am the biggest cat
Who am I?:lion
Nope, try again!
I come in black and white or orange and black
Who am I?:tiger
You got it! I am tiger
I will give you 3 hints, guess what animal I am
I use echo-location
Who am I?:human
Nope, try again!
I can fly
Who am I?:butterfly
Nope, try again!
I see well in dark
Who am I?:cat
Nope, try again!
I'm out of hints! The answer is: bat
Program2:
This is a learning game for kids to learn Integer Divisions. The user will be given a problem and should answer it. Provide a "Correct" or "Incorrect" feedback, handle bad input using Exceptions.
Following code will give you random integer 0-5:
from random import randrange
a = randrange(5)
Sample Output:
INTEGER DIVISIONS
6/3=2
CORRECT!
12/4=3
CORRECT!
4/2=4
INCORRECT!
0/1=e
Please enter Integers Only!
0/0=4
INCORRECT!
1/1=1
CORRECT!
0/2=0
CORRECT!
4/1=r
Please enter Integers Only!
Additional Notes
Multiple inheritance is supported, but may have unexpected behavior
class Shape: def __init__(self): self.color = "red" class Angleless: def __init__(self): self.color = "blue" class Circle(Shape, Angleless): pass c = Circle() print(c.color)
$ python lecture8.py red
class Shape: def __init__(self): self.color = "red" class Angleless: def __init__(self): self.angles = 0 class Circle(Shape, Angleless): pass c = Circle() print(c.color) print(c.angles)
$ python lecture8.py red Traceback (most recent call last): File "lecture8.py", line 14, in <module> print(c.angles) AttributeError: 'Circle' object has no attribute 'angles'
class Shape: color = "red" class Angleless: def __init__(self): self.angles = 0 class Circle(Shape, Angleless): pass c = Circle() print(c.color) print(c.angles)
$ python lecture8.py red 0
class Shape: color = "red" class Angleless: angles = 0 class Circle(Shape, Angleless): pass c = Circle() print(c.color) print(c.angles)
$ python lecture8.py red 0
Circle factory using generator and classmethod
class Circle: def __init__(self, color): self.color=color def __str__(self): return str(self.color) @classmethod def from_file(cls, filename): with open(filename, 'r') as f: lines = f.readlines() for l in lines: yield cls(l) @classmethod def from_user(cls): color = input("Enter Color:") return cls(color) circles = Circle.from_file("circles.txt") for c in circles: print(c) c = Circle.from_user() print(c)
circles.txt
red yellow green
$ python lecture8.py red yellow green Enter Color:black black
Mini Project 1 - Indexer, Crawler, File Traverser and Search.
File Traverser is optional
You will be building a mini search engine.
You may be able to reuse the crawler program and the keywords search that you build as one of your homework programs.
use http://newhaven.edu/search/ as inspiration and validation
The scope of this project is:
You may be able to reuse the crawler program and the keywords search that you build as one of your homework programs.
use http://newhaven.edu/search/ as inspiration and validation
The scope of this project is:
- Your root directory must have README.md with instructions on how to install and run the project. You will need to have another student from the same class validate your instructions by running and installing your project and working with you to improve the instructions. The student who does the validation receives 5% extra credit if I am able to run the project without any issues. A single student can do validations for multiple other students.
- Build a crawler that crawls all the pages in the newhaven.edu domain. There are about 26,100 pages. Use * search to see all pages on the http://newhaven.edu/search/. If you want to research for existing python crawler frameworks and use those, that's ok! If you consider multiple ways to crawl a content and document the trade off analysis as part of your README.md, you will receive up to 20% extra credit. I will explain this in the class.
- You may want to introspect the Google results behind the scenes to draw some decisions around what you should and should not extract from a page when crawling it. I will show in the class how to retrieve that information. Here is one example of the google document returned.
{ "cacheUrl": "http://www.google.com/search?q=cache:NK4cDcsUUYUJ:www.newhaven.edu", "clicktrackUrl": "https://www.google.com/url?client=internal-element-cse&cx=017404113844510084297:wxasmbgdgl0&q=https://www.newhaven.edu/&sa=U&ved=2ahUKEwjqu8j-0b7vAhWbZs0KHVyyC2MQFjAAegQIBRAB&usg=AOvVaw1T26xXIgOiASa7-BCy7YTm", "content": "The University of New Haven, founded on the Yale campus in 1920, founded on \nthe Yale campus in 1920, is a private, coeducational university situated on the ...", "contentNoFormatting": "The University of New Haven, founded on the Yale campus in 1920, founded on \nthe Yale campus in 1920, is a private, coeducational university situated on the ...", "title": "University of New Haven: Home", "titleNoFormatting": "University of New Haven: Home", "formattedUrl": "https://www.newhaven.edu/", "unescapedUrl": "https://www.newhaven.edu/", "url": "https://www.newhaven.edu/", "visibleUrl": "www.newhaven.edu", "richSnippet": { "cseImage": { "src": "https://www.newhaven.edu/_resources/images/hero/charger-statue-snow-2021.jpg", "width": "150", "type": "0", "height": "80" }, "metatags": { "twitterCard": "summary_large_image", "twitterSite": "@unewhaven", "twitterTitle": "Home - University of New Haven", "viewport": "width=device-width, initial-scale=1", "twitterDescription": "The University of New Haven, founded on the Yale campus in 1920, founded on the Yale campus in 1920, is a private, coeducational university situated on the coast of southern New England in West Haven, Connecticut. It’s a diverse and vibrant community of 7,000 students, with campuses across the country and around the world. Within our colleges and schools, students immerse themselves in a transformative, career-focused education across the liberal arts and sciences, fine arts, business, engineering, healthcare, public safety, and public service. We offer more than 100 academic programs, all grounded in a long-standing commitment to collaborative, interdisciplinary, project-based learning.", "twitterImage": "https://www.newhaven.edu/_resources/images/hero/charger-statue-snow-2021.jpg", "ogTitle": "Home - University of New Haven", "ogDescription": "The University of New Haven, founded on the Yale campus in 1920, founded on the Yale campus in 1920, is a private, coeducational university situated on the coast of southern New England in West Haven, Connecticut. It’s a diverse and vibrant community of 7,000 students, with campuses across the country and around the world. Within our colleges and schools, students immerse themselves in a transformative, career-focused education across the liberal arts and sciences, fine arts, business, engineering, healthcare, public safety, and public service. We offer more than 100 academic programs, all grounded in a long-standing commitment to collaborative, interdisciplinary, project-based learning.", "ogSiteName": "University of New Haven", "ogImage": "https://www.newhaven.edu/_resources/images/hero/charger-statue-snow-2021.jpg", "ogType": "website" } }, "breadcrumbUrl": { "host": "www.newhaven.edu" } }
- Store crawled content any way you like but do document in README.md what you used as storage and what is approximate byte store you will need to store ~26,100 pages and how you calculated. The part of the program that will store the data has to be in a separate class than the crawler, we will call the program that stores the content an indexer and the stored content - index. To store the data you can use:
- a noSQL datastore. I won't mind if you use a search engine as your data store! You may get lot of hidden benefits if you go that route. Look into https://lucene.apache.org/ and some search engines built on top of it: https://www.elastic.co/elasticsearch/, https://solr.apache.org/. Those are the leaders in the industry. https://neo4j.com/ is a Graph DB, it's not a search engine, but also utilizes indexing underneath and may add interesting analytical capabilities to your search.
- In-Memory datastore. Something like a data structure of custom objects or a dictionary will be sufficient. You may want to consider persisting your structure in a file (pickle maybe?) so that you don't need to re-crawl the content every time you shut down the program.
- SQL datastore.
- Implement Search over the Content. Multiple Keywords must be covered, it is up to you how, use the UNH site search for inspiration.
- Results must be returned in a JSON with the number of results and the time search took to retrieve the results.
File Traverser Optional. 50% Extra Credit
File traverser is similar to crawler, but uses a folder on your drive as a seed and traverses all files in that directory, handing them off to indexer.
URL to the file content is replaced with file URL scheme: https://en.wikipedia.org/wiki/File_URI_scheme
Since there is no file content on the UNH site, you can either add random files with text or maybe your python homeworks and programs. the file traversed content can be mixed with crawled content.
URL to the file content is replaced with file URL scheme: https://en.wikipedia.org/wiki/File_URI_scheme
Since there is no file content on the UNH site, you can either add random files with text or maybe your python homeworks and programs. the file traversed content can be mixed with crawled content.