MORE PYTHON CLASS
METHODS
(download slides and .py files to follow along)
6.100L Lecture 18
Ana Bell
1
IMPLEMENTING USING
THE CLASS vs THE CLASS
Write code from two different perspectives
Implementing a new Using the new object type in
object type with a class code
Define the class • Create instances of the
Define data attributes object type
(WHAT IS the object)
Define methods
• Do operations with them
(HOW TO use the object)
Class abstractly captures Instances have specific
common properties and values for attributes
behaviors
2
6.100L Lecture 18
RECALL THE COORDINATE CLASS
Class definition tells Python the blueprint for a type Coordinate
class Coordinate(object):
""" A coordinate made up of an x and y value """
def __init__(self, x, y):
""" Sets the x and y values """
self.x = x
self.y = y
def distance(self, other):
""" Returns euclidean dist between two Coord obj """
x_diff_sq = (self.x-other.x)**2
y_diff_sq = (self.y-other.y)**2
return (x_diff_sq + y_diff_sq)**0.5
6.100L Lecture 18
ADDING METHODS TO THE
COORDINATE CLASS
Methods are functions that only work with objects of this type
class Coordinate(object):
""" A coordinate made up of an x and y value """
def __init__(self, x, y):
""" Sets the x and y values """
self.x = x
self.y = y
def distance(self, other):
""" Returns euclidean dist between two Coord obj """
x_diff_sq = (self.x-other.x)**2
y_diff_sq = (self.y-other.y)**2
return (x_diff_sq + y_diff_sq)**0.5
def to_origin(self):
""" always sets self.x and self.y to 0,0 """
self.x = 0
self.y = 0
4
6.100L Lecture 18
MAKING COORDINATE INSTANCES
Creating instances makes actual Coordinate objects in memory
The objects can be manipulated
Use dot notation to call methods and access data attributes
c = Coordinate(3,4)
origin = Coordinate(0,0)
print(f"c's x is {c.x} and origin's x is {origin.x}")
print(c.distance(origin))
c.to_origin()
print(c.x, c.y)
5
6.100L Lecture 18
CLASS DEFINITION INSTANCE
OF AN OBJECT TYPE vs OF A CLASS
Class name is the type Instance is one specific object
class Coordinate(object) coord = Coordinate(1,2)
Class is defined generically Data attribute values vary
Use self to refer to some between instances
instance while defining the c1 = Coordinate(1,2)
class c2 = Coordinate(3,4)
(self.x – self.y)**2
• c1 and c2 have different data
self is a parameter to attribute values c1.x and c2.x
methods in class definition because they are different objects
Class defines data and Instance has the structure of
methods common across all the class
instances
6
6.100L Lecture 18
USING CLASSES TO BUILD OTHER
CLASSES
Example: use Coordinates to build Circles
Our implementation will use 2 data attributes
Coordinate object representing the center
int object representing the radius
radius
Center
coordinate
6.100L Lecture 18
CIRCLE CLASS:
DEFINITION and INSTANCES
class Circle(object):
def __init__(self, center, radius):
self.center = center
self.radius = radius
center = Coordinate(2, 2)
my_circle = Circle(center, 2)
6.100L Lecture 18
YOU TRY IT!
Add code to the init method to check that the type of center is
a Coordinate obj and the type of radius is an int. If either are
not these types, raise a ValueError.
def __init__(self, center, radius):
self.center = center
self.radius = radius
6.100L Lecture 18
CIRCLE CLASS:
DEFINITION and INSTANCES
class Circle(object):
def __init__(self, center, radius):
self.center = center
self.radius = radius
def is_inside(self, point):
""" Returns True if point is in self, False otherwise """
return point.distance(self.center) < self.radius
center = Coordinate(2, 2)
my_circle = Circle(center, 2)
p = Coordinate(1,1)
print(my_circle.is_inside(p))
10
6.100L Lecture 18
YOU TRY IT!
Are these two methods in the Circle class functionally equivalent?
class Circle(object):
def __init__(self, center, radius):
self.center = center
self.radius = radius
def is_inside1(self, point):
return point.distance(self.center) < self.radius
def is_inside2(self, point):
return self.center.distance(point) < self.radius
11
6.100L Lecture 18
EXAMPLE:
FRACTIONS
Create a new type to represent a number as a fraction
Internal representation is two integers
• Numerator
• Denominator
Interface a.k.a. methods a.k.a how to interact with
Fraction objects
• Add, subtract
• Invert the fraction
Let’s write it together!
12
6.100L Lecture 18
NEED TO CREATE INSTANCES
class SimpleFraction(object):
def __init__(self, n, d):
self.num = n
self.denom = d
13
6.100L Lecture 18
MULTIPLY FRACTIONS
class SimpleFraction(object):
def __init__(self, n, d):
self.num = n
self.denom = d
def times(self, oth):
top = self.num*oth.num
bottom = self.denom*oth.denom
return top/bottom
14
6.100L Lecture 18
ADD FRACTIONS
class SimpleFraction(object):
def __init__(self, n, d):
self.num = n
self.denom = d
………
def plus(self, oth):
top = self.num*oth.denom + self.denom*oth.num
bottom = self.denom*oth.denom
return top/bottom
15
6.100L Lecture 18
LET’S TRY IT OUT
f1 = SimpleFraction(3, 4)
f2 = SimpleFraction(1, 4)
print(f1.num) 3
print(f1.denom) 4
print(f1.plus(f2)) 1.0
print(f1.times(f2)) 0.1875
16
6.100L Lecture 18
YOU TRY IT!
Add two methods to invert fraction object according to the specs below:
class SimpleFraction(object):
""" A number represented as a fraction """
def __init__(self, num, denom):
self.num = num
self.denom = denom
def get_inverse(self):
""" Returns a float representing 1/self """
pass
def invert(self):
""" Sets self's num to denom and vice versa.
Returns None. """
pass
# Example:
f1 = SimpleFraction(3,4)
print(f1.get_inverse()) # prints 1.33333333 (note this one returns value)
f1.invert() # acts on data attributes internally, no return
print(f1.num, f1.denom) # prints 4 3
17
6.100L Lecture 18
LET’S TRY IT OUT WITH MORE
THINGS
f1 = SimpleFraction(3, 4)
f2 = SimpleFraction(1, 4)
print(f1.num) 3
print(f1.denom) 4
print(f1.plus(f2)) 1.0
print(f1.times(f2)) 0.1875
print(f1) <__main__.SimpleFraction object at 0x00000234A8C41DF0>
print(f1 * f2) Error!
18
6.100L Lecture 18
SPECIAL OPERATORS IMPLEMENTED
WITH DUNDER METHODS
+, -, ==, <, >, len(), print, and many others are
shorthand notations
Behind the scenes, these get replaced by a method!
https://docs.python.org/3/reference/datamodel.html#basic-customization
Can override these to work with your class
19
6.100L Lecture 18
SPECIAL OPERATORS IMPLEMENTED
WITH DUNDER METHODS
Define them with double underscores before/after
__add__(self, other) self + other
__sub__(self, other) self - other
__mul__(self, other) self * other
__truediv__(self, other) self / other
__eq__(self, other) self == other
__lt__(self, other) self < other
__len__(self) len(self)
__str__(self) print(self)
__float__(self) float(self) i.e cast
__pow__ self**other
... and others
20
6.100L Lecture 18
PRINTING OUR OWN
DATA TYPES
21
6.100L Lecture 18
PRINT REPRESENTATION OF AN
OBJECT
>>> c = Coordinate(3,4)
>>> print(c)
<__main__.Coordinate object at 0x7fa918510488>
Uninformative print representation by default
Define a __str__ method for a class
Python calls the __str__ method when used with
print on your class object
You choose what it does! Say that when we print a
Coordinate object, want to show
>>> print(c)
<3,4>
22
6.100L Lecture 18
DEFINING YOUR OWN PRINT
METHOD
class Coordinate(object):
def __init__(self, xval, yval):
self.x = xval
self.y = yval
def distance(self, other):
x_diff_sq = (self.x-other.x)**2
y_diff_sq = (self.y-other.y)**2
return (x_diff_sq + y_diff_sq)**0.5
def __str__(self):
return "<"+str(self.x)+","+str(self.y)+">"
23
6.100L Lecture 18
WRAPPING YOUR HEAD AROUND
TYPES AND CLASSES
Can ask for the type of an object instance
>>> c = Coordinate(3,4)
>>> print(c)
<3,4>
>>> print(type(c))
<class __main__.Coordinate>
This makes sense since
>>> print(Coordinate)
<class __main__.Coordinate>
>>> print(type(Coordinate))
<type 'type'>
Use isinstance() to check if an object is a Coordinate
>>> print(isinstance(c, Coordinate))
True
24
6.100L Lecture 18
EXAMPLE: FRACTIONS WITH
DUNDER METHODS
Create a new type to represent a number as a fraction
Internal representation is two integers
• Numerator
• Denominator
Interface a.k.a. methods a.k.a how to interact with
Fraction objects
• Add, sub, mult, div to work with +, -, *, /
• Print representation, convert to a float
• Invert the fraction
Let’s write it together!
25
6.100L Lecture 18
CREATE & PRINT INSTANCES
class Fraction(object):
def __init__(self, n, d):
self.num = n
self.denom = d
def __str__(self):
return str(self.num) + "/" + str(self.denom)
26
6.100L Lecture 18
LET’S TRY IT OUT
f1 = Fraction(3, 4)
f2 = Fraction(1, 4)
f3 = Fraction(5, 1)
print(f1) 3/4
print(f2) 1/4
print(f3) 5/1
Ok, but looks weird!
27
6.100L Lecture 18
YOU TRY IT!
Modify the str method to represent the Fraction as just the
numerator, when the denominator is 1. Otherwise its
representation is the numerator then a / then the denominator.
class Fraction(object):
def __init__(self, num, denom):
self.num = num
self.denom = denom
def __str__(self):
return str(self.num) + "/" + str(self.denom)
# Example:
a = Fraction(1,4)
b = Fraction(3,1)
print(a) # prints 1/4
print(b) # prints 3
28
6.100L Lecture 18
IMPLEMENTING
+-*/
float()
29
6.100L Lecture 18
COMPARING METHOD vs.
DUNDER METHOD
class SimpleFraction(object): class Fraction(object):
def __init__(self, n, d): def __init__(self, n, d):
self.num = n self.num = n
self.denom = d self.denom = d
……… ………
def times(self, oth): def __mul__(self, other):
top = self.num*oth.num top = self.num*other.num
bottom = self.denom*oth.denom bottom = self.denom*other.denom
return top/bottom return Fraction(top, bottom)
30
6.100L Lecture 18
LETS TRY IT OUT
a = Fraction(1,4)
b = Fraction(3,4)
print(a) 1/4
c = a * b
print(c) 3/16
31
6.100L Lecture 18
CLASSES CAN HIDE DETAILS
These are all equivalent
print(a * b)
print(a.__mul__(b))
print(Fraction.__mul__(a, b))
Every operation in Python
comes back to a method call
The first instance makes clear
the operation, without worrying
about the internal details!
Abstraction at work
32
6.100L Lecture 18
BIG IDEA
Special operations we’ve
been using are just
methods behind the
scenes.
Things like:
print, len
+, *, -, /, <, >, <=, >=, ==, !=
[]
and many others!
33
6.100L Lecture 18
CAN KEEP BOTH OPTIONS BY ADDING
A METHOD TO CAST TO A float
class Fraction(object):
def __init__(self, n, d):
self.num = n
self.denom = d
………
def __float__(self):
return self.num/self.denom
c = a * b
print(c) 3/16
print(float(c)) 0.1875
34
6.100L Lecture 18
LETS TRY IT OUT SOME MORE
a = Fraction(1,4)
b = Fraction(2,3)
c = a * b
print(c) 2/12
Not quite what we might expect? It’s not reduced.
Can we fix this?
35
6.100L Lecture 18
ADD A METHOD
class Fraction(object):
………
def reduce(self):
def gcd(n, d):
while d != 0:
(d, n) = (n%d, d)
return n
if self.denom == 0:
return None
elif self.denom == 1:
return self.num
else:
greatest_common_divisor = gcd(self.num, self.denom)
top = int(self.num/greatest_common_divisor)
bottom = int(self.denom/greatest_common_divisor)
return Fraction(top, bottom)
c = a*b
print(c) 2/12
print(c.reduce()) 1/6 36
6.100L Lecture 18
WE HAVE SOME IMPROVEMENTS TO MAKE
class Fraction(object):
…………
def reduce(self):
def gcd(n, d):
while d != 0:
(d, n) = (n%d, d)
return n
if self.denom == 0:
return None
elif self.denom == 1:
s
return self.num
else:
greatest_common_divisor = gcd(self.num, self.denom)
top = int(self.num/greatest_common_divisor)
bottom = int(self.denom/greatest_common_divisor)
return Fraction(top, bottom)
37
6.100L Lecture 18
CHECK THE TYPES, THEY’RE DIFFERENT
a = Fraction(4,1)
b = Fraction(3,9)
ar = a.reduce() 4
br = b.reduce() 1/3
print(ar, type(ar)) 4 <class 'int'>
print(br, type(br)) 1/3 <class '__main__.Fraction'>
c = ar * br
38
6.100L Lecture 18
YOU TRY IT!
Modify the code to return a Fraction object when denominator
is 1
class Fraction(object):
def reduce(self):
def gcd(n, d):
while d != 0:
(d, n) = (n%d, d)
return n
if self.denom == 0:
return None
elif self.denom == 1:
return self.num
else:
greatest_common_divisor = gcd(self.num, self.denom)
top = int(self.num/greatest_common_divisor)
bottom = int(self.denom/greatest_common_divisor)
return Fraction(top, bottom)
# Example:
f1 = Fraction(5,1)
print(f1.reduce()) # prints 5/1
39
not 5
6.100L Lecture 18
WHY OOP and BUNDLING THE
DATA IN THIS WAY?
Code is organized and modular
Code is easy to maintain
It’s easy to build upon objects to make more complex objects
Decomposition and abstraction at work with Python classes
Bundling data and behaviors means you can use objects consistently
Dunder methods are abstracted by common operations, but they’re
just methods behind the scenes!
40
6.100L Lecture 18
MITOpenCourseWare
https://ocw.mit.edu
6.100L Introduction to Computer Science and Programming Using Python
Fall 2022
For information about citing these materials or our Terms ofUse,visit: https://ocw.mit.edu/terms.
41