Python is an object-oriented programming language. That means it provides features that support object-oriented programming (OOP).
Up to now, some of the programs we have been writing use a procedural programming paradigm. In procedural programming the focus is on writing functions or procedures which operate on data.
In object-oriented programming the focus is on the creation of objects which contain both data and functionality together.
Object definition
Usually, each object definition corresponds to some object or concept in the real world, and the functions that operate on that object correspond to the ways real-world objects interact.
class Point:
""" Point class represents and manipulates x,y coords. """
def __init__(self):
""" Create a new point at the origin """
self.x = 0
self.y = 0
Every class should have a method with the special name __init__
. This initializer method is automatically called whenever a new instance is created.
It gives the programmer the opportunity to set up the attributes required within the new instance by giving them their initial state/values.
The self parameter is automatically set to reference the newly created object that needs to be initialized.
p = Point() # Instantiate an object of type Point
q = Point() # and make a second point
print(p)
print(q)
print(p is q, p == q)
print(p.x, q.y)
p.x, p.y = 5, 6
print(p.x, p.y)
A function like Point() that creates a new object instance is called a constructor.
Every class automatically uses the name of the class as the name of the constructor function.
The definition of the constructor function is done when you write the __init__
function.
It may be helpful to think of a class as a factory for making objects.
The class itself isn’t an instance of a point, but it contains the machinery to make point instances.
Every time we call the constructor, we’re asking the factory to make us a new object. As the object comes off the production line, its initialization method is executed to get the object properly set up with its factory default settings.
The combined process of “make me a new object” and “get its settings initialized to the factory default settings” is called instantiation.
class Point:
""" Point class represents and manipulates x,y coords. """
def __init__(self, x=0, y=0):
""" Create a new point at x, y """
self.x = x
self.y = y
p = Point(4, 2)
q = Point(6, 3)
r = Point() # r represents the origin (0, 0)
print(p.x, q.y, r.x)
A method behaves like a function but it is invoked on a specific instance, e.g. tess.right(90)
. Like a data attribute, methods are accessed using dot notation.
class Point:
""" Create a new Point, at coordinates x, y """
def __init__(self, x=0, y=0):
""" Create a new point at x, y """
self.x = x
self.y = y
def distance_from_origin(self):
""" Compute my distance from the origin """
return (self.x ** 2 + self.y ** 2) ** 0.5
p = Point(3, 4)
print(p.x, p.y)
print(p.distance_from_origin())
q = Point(5, 12)
print(q.x, q.y)
print(q.distance_from_origin())
def print_point(pt):
print("({0}, {1})".format(pt.x, pt.y))
print_point(p)
import math
def distance(point1, point2):
xdiff = point2.x - point1.x
ydiff = point2.y - point1.y
dist = math.sqrt(xdiff**2 + ydiff**2)
return dist
p = Point(4, 3)
q = Point(0, 0)
print(distance(p, q))
print(p)
str(p)
class Point:
""" Point class represents and manipulates x,y coords. """
def __init__(self, x=0, y=0):
""" Create a new point at x, y """
self.x = x
self.y = y
def __str__(self):
return "({},{})".format(self.x, self.y)
p = Point(1,2)
print(p)
print(str(p))
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "({},{})".format(self.x, self.y)
def halfway(self, target):
""" Return the halfway point between myself and the target """
mx = (self.x + target.x)/2
my = (self.y + target.y)/2
return Point(mx, my)
p = Point(3, 4)
q = Point(5, 12)
r = p.halfway(q)
print(r)
The original syntax for a function call, print_time(current_time)
, suggests that the function is the active agent.
"Hey, print_time! Here’s an object for you to print."
In object-oriented programming, the objects are considered the active agents.
current_time.print_time()
says "Hey current_time! Please print yourself!"
class Fraction:
def __init__(self, top, bottom):
self.num = top # the numerator is on top
self.den = bottom # the denominator is on the bottom
def __str__(self):
return str(self.num) + "/" + str(self.den)
def getNum(self):
return self.num
def getDen(self):
return self.den
myfraction = Fraction(3, 4)
print(myfraction)
print(myfraction.getNum())
print(myfraction.getDen())
myfraction = Fraction(3, 4)
myfraction.num +=2
print(myfraction)
get lowest terms
12/16 == 3/4
def gcd(m, n):
while m % n != 0:
m, n = n, m % n
return n
print(gcd(12, 16))
class Fraction:
def __init__(self, top, bottom):
self.num = top # the numerator is on top
self.den = bottom # the denominator is on the bottom
def __str__(self):
return str(self.num) + "/" + str(self.den)
def simplify(self):
common = gcd(self.num, self.den)
self.num = self.num // common
self.den = self.den // common
myfraction = Fraction(12, 16)
print(myfraction)
myfraction.simplify()
print(myfraction)
f1 = Fraction(12, 16)
f2 = Fraction(12, 16)
f3 = Fraction(3, 4)
print(f1 == f2)
print(f1 == f3)
class Fraction:
def __init__(self, top, bottom):
self.num = top # the numerator is on top
self.den = bottom # the denominator is on the bottom
def __str__(self):
return str(self.num) + "/" + str(self.den)
def __eq__(self, target):
self.simplify()
target.simplify()
return self.num == target.num and self.den == target.den
def simplify(self):
common = gcd(self.num, self.den)
self.num = self.num // common
self.den = self.den // common
f1 = Fraction(12, 16)
f2 = Fraction(12, 16)
f3 = Fraction(3, 4)
print(f1 == f2)
print(f1 == f3)
def __add__(self,otherfraction):
newnum = self.num*otherfraction.den + self.den*otherfraction.num
newden = self.den * otherfraction.den
common = gcd(newnum,newden)
return Fraction(newnum//common,newden//common)
def gcd(m, n):
while m % n != 0:
m, n = n, m % n
return n
class Fraction:
def __init__(self, top, bottom):
self.num = top # the numerator is on top
self.den = bottom # the denominator is on the bottom
def __str__(self):
return str(self.num) + "/" + str(self.den)
def __eq__(self, target):
self.simplify()
target.simplify()
return self.num == target.num and self.den == target.den
def simplify(self):
common = gcd(self.num, self.den)
self.num = self.num // common
self.den = self.den // common
def __add__(self,otherfraction):
newnum = self.num*otherfraction.den + self.den*otherfraction.num
newden = self.den * otherfraction.den
common = gcd(newnum, newden)
return Fraction(newnum // common, newden // common)
f1 = Fraction(1, 2)
f2 = Fraction(1, 4)
f3 = f1 + f2 # calls the __add__ method of f1
print(f3)
Make object iterable
class Fraction:
def __init__(self, top, bottom):
self.num = top # the numerator is on top
self.den = bottom # the denominator is on the bottom
def __iter__(self):
return (i for i in (self.num, self.den))
f1 = Fraction(1,3)
num, den = f1
print(num,den)
Make object hashable
class Fraction:
def __init__(self, top, bottom):
self.num = top # the numerator is on top
self.den = bottom # the denominator is on the bottom
def __hash__(self):
return hash(self.num) ^ hash(self.den)
print(len(set([Fraction(1,2),Fraction(3,4)])))
import copy
f1 = Fraction(3, 4)
f2 = copy.copy(f1) # shallow copy
print(f1 is f2)
print(f1 == f2)
To copy a simple object like a Point, which doesn’t contain any embedded objects, copy is sufficient. This is called shallow copying.
import copy
f1 = Fraction(3, 4)
f2 = copy.deepcopy(f1) # deep copy
print(f1 is f2)
print(f1 == f2)
deepcopy copies not only the object but also any embedded objects.
c struct like
Sometimes it is useful to have a data type similar to the Pascal “record” or C “struct”, bundling together a few named data items.
class Employee:
pass
john = Employee() # Create an empty employee record
# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
print(john.name)