Wednesday, October 9, 2015
Topic: More Object Oriented Programming (OOP) in Python
code/names.py:
class NameClassifier:
def __init__(self, femalefile, malefile):
self.LoadNameData(femalefile, malefile)
def LoadNameData(self, femalefile, malefile):
# Creates a dictionary with the name data from the two input files
self.namedata = {}
f = open(femalefile,'r')
for line in f:
self.namedata[line.split()[0]] = 1.0
f.close()
f = open(malefile,'r')
for line in f:
name = line.split()[0]
if name in self.namedata:
# Just assume a 50/50 distribution for names on both lists
self.namedata[name] = 0.5
else:
self.namedata[name] = 0.0
f.close()
def ClassifyName(self, name):
if name in self.namedata:
return self.namedata[name]
else:
# Don't have this name in our data
return 0.5code/main.py:
import names
# Create an instance of the name classifier
classifier = names.NameClassifier('dist.female.first', 'dist.male.first')
# Setup test data
testdata = ['PETER', 'LOIS', 'STEWIE', 'BRIAN', 'MEG', 'CHRIS']
# Invoke the ClassifyName() method
for name in testdata:
print('{}: {}'.format(name, classifier.ClassifyName(name)))- Output:
$ python main.py
PETER: 1.0
LOIS: 1.0
STEWIE: 0.5
BRIAN: 1.0
MEG: 1.0
CHRIS: 1.0
Let's inspect a Student object:
code/student1.py:
import copy
class Student:
def __init__(self, id):
self.id = id
self.classes = {}
def getId(self):
return self.id
def addClass(self, name, gradepoint):
self.classes[name] = gradepoint
sumgradepoints = float(sum(self.classes.values()))
self.gpa = sumgradepoints/len(self.classes)
def getGPA(self):
return self.gpa
def getClasses(self):
return copy.deepcopy(self.classes)
s = Student(7)
s.addClass("gym", 4)
s.addClass("math", 3)
print("s = {}".format(s))
# lots of print statements to get information
print(s.getId())
print(s.getClasses())
print(s.getGPA())Output:
$ python student1.py
s = <__main__.Student instance at 0x7f27f94acdd0>
7
{'gym': 4, 'math': 3}
3.5
$
-
Your user defined objects can be made to work with the Python built-in operators
-
Why would you want to do that?
code/student2.py:
import copy
class Student:
def __init__(self, id):
self.id = id
self.classes = {}
def getId(self):
return self.id
def addClass(self, name, gradepoint):
self.classes[name] = gradepoint
sumgradepoints = float(sum(self.classes.values()))
self.gpa = sumgradepoints/len(self.classes)
def getGPA(self):
return self.gpa
def getClasses(self):
return copy.deepcopy(self.classes)
def __repr__(self):
string = "Student %d: " % self.getId()
string += " %s, " % self.getClasses()
string += "GPA = %4.2f" % self.getGPA()
return string
s = Student(7)
s.addClass("gym", 4)
s.addClass("math", 3)
# now easy to print a student
print(s)Output:
$ python student2.py
Student 7: {'gym': 4, 'math': 3}, GPA = 3.50
$
| method | operation |
|----------------------+----------------------------|
| __len__(self) | Returns the length of self |
| __add__(self, other) | Returns self + other |
| __mul__(self, other) | Returns self * other |
| __neg__(self) | Returns -sefl |
| __abs__(self) | Returns abs(self) |
| __float__(self) | Returns float(self) |
Over 50+ methods in total
-
Some will argue that putting your data in an object, and adding a bunch of put / get methods to interface with it, is just a glorified container and interface
-
Real power of OOP might be in allowing objects to interact with each other by overriding appropriate methods
p_blue = Particle(...)
p_red = Particle(...)
...
p_purple = p_blue + p_red
code/particle.py:
class Particle:
def __init__(self, mass, velx):
self.mass = mass
self.velx = velx
def __add__(self, other):
# inelastic collision (momentum is conserved)
mass = self.mass + other.mass
velx = (self.mass*self.velx + other.mass*other.velx)/mass
return Particle(mass, velx)
def __repr__(self):
return "Mass: %s, Velocity: %s" % (self.mass, self.velx)$ python -i particle.py
>>> p_blue = Particle(4.3, 2.5)
>>> p_red = Particle(1.4, -0.8)
>>> p_purple = p_blue + p_red
>>> p_purple
Mass: 5.7, Velocity: 1.68947368421
>>>
code/badoverloading.py:
class User:
def __init__(self, id):
self.id = id
def __len__(self):
return self.getId()
def getId(self):
return self.idOutput:
$ python -i badoverloading.py
>>> user = User(7)
>>> len(user)
7
>>>
Is this intuitive?
-
Inheritance is a way for a class to inherit attributes from another class
-
This is a form of code reuse
-
The original class is called a base class, or a superclass, or a parent class
-
The new class is called a derived class, or a subclass, or a child class
-
The new class will typically redefine or add new attributes
code/inheritance1.py:
# parent class
class User:
def __init__(self, id):
self.id = id
def getId(self):
return self.id
# child class
class MovieWatcher(User):
passOutput:
$ python -i inheritance1.py
>>> user = MovieWatcher(3)
>>> user.getId()
3
>>>
code/inheritance2.py:
class User:
def __init__(self, id):
self.id = id
def getId(self):
return self.id
class MovieWatcher(User):
def __init__(self, id):
# Call the parent class initialization
User.__init__(self, id)
# MovieWatcher specific initialization
self.avgmovieranking = -1.
self.movies = {}code/inheritance3.py:
class User:
def __init__(self, id):
self.id = id
def getId(self):
return self.id
class MovieWatcher(User):
def __init__(self, id):
# Call the parent class initialization
User.__init__(self, id)
# MovieWatcher specific initialization
self.avgmovieranking = -1.
self.movies = {}
class CookieEater(User):
def __init__(self, id):
# Call the parent class initialization
User.__init__(self, id)
# CookieEater specific initialization
self.cookieseaten = 0
self.cookies = {}-
Different types of objects have methods with the same name that take the same arguments
-
Programmer does not need to know the exact type of an object for common operations
-
Typically the objects inherit from the same parent class
code/shapes.py:
import math
class Shape:
def GetArea(self):
raise RuntimeError, "Not implemented yet"
class Circle(Shape):
def __init__ (self, x, y, radius):
self.x = x
self.y = y
self.radius = radius
def GetArea(self):
area = math.pi * math.pow(self.radius, 2)
return area
class Rectangle(Shape):
def __init__ (self, x0, y0, x1, y1):
self.x0 = x0
self.y0 = y0
self.x1 = x1
self.y1 = y1
def GetArea(self):
xDistance = self.x1 - self.x0
yDistance = self.y1 - self.y0
return abs(xDistance * yDistance)
shapes = []
shapes.append(Circle(0., 0., 1.0))
shapes.append(Rectangle(0., 0., 2., 4.))
for shape in shapes:
print("area = {}".format(shape.GetArea()))Output:
$ python shapes.py
area = 3.14159265359
area = 8.0
$
-
Abstraction
-
Represent things in a form familiar to humans
-
Encapsulation
-
Restrict access to internal data by providing interfaces
-
Inheritance
-
Parent / child classes for code reuse
-
Polymorphism
-
Share method names and arguments across (sibling) classes



