Lec9 Classes Inheritance
Lec9 Classes Inheritance
ORIENTED
PROGRAMMING
6.00.01X LECTURE 1
OBJECTS
Python supports many different kinds of data
1234 3.14159 "Hello" [1, 5, 7, 11, 13]
{"CA": "California", "MA": "Massachusetts"}
each is an instance of an object, and every object has:
• a type
• an internal data representation (primitive or composite)
• a set of procedures for interaction with the object
each instance is a particular type of object
• 1234 is an instance of an int
• a = "hello"
a is an instance of a string
Object have Types because that brings with them the ability to manipulate them
6.00.01X LECTURE 2
OBJECT ORIENTED
PROGRAMMING (OOP)
everything in Python is an object and has a type
objects are a data abstraction that capture:
• internal representation through data attributes
• interface for interacting with object through methods
(procedures), defines behaviors but hides implementation
every data abstraction is a internal data representation and returning with an answer
can create new instances of objects
can destroy objects
• explicitly using del or just “forget” about them
• Python system will reclaim destroyed or inaccessible
objects – called “garbage collection”
6.00.01X LECTURE 3
STANDARD DATA OBJECTS
some object types built in to Python
• lists – [1, 2, 3, 4]
Lists, Tuples, and Strings are built in data objects
• tuples – (1, 2, 3, 4)
• strings – ‘abcd’
want to explore ability to create our own data object
types
6.00.01X LECTURE 4
EXAMPLE: [1,2,3,4]
[1,2,3,4] is of type list lists are represented by allocating a sequence of consec
in memory. But not a great way to represent. Use Linked
how are lists represented internally? linked list of cells
L = 1 -> 2 -> 3 -> 4 ->
6.00.01X LECTURE 6
By bundling things together, you can treat that bundle as an abstract data structure.
To get into it, you need to have defined methods that can pull out pieces and do things with them. But you can
as an element.
ADVANTAGES OF OOP
bundle data into packages together with procedures
that work on them through well-defined interfaces
divide-and-conquer development
• implement and test behavior of each class separately
• increased modularity reduces complexity
classes make it easy to reuse code
• many Python modules define new classes
• each class has a separate environment (no collision on
function names)
• inheritance allows subclasses to redefine or extend a
selected subset of a superclass’ behavior
Method with a particular in one class can be used the method in the same name in another class.
Makes it easier to write code because don't have to write the same behaviour of its parent class and modify it.
6.00.01X LECTURE 7
6.00.01X LECTURE 8
Implementing the class Using the class
First, start with Class definition and give it a name. In this case, "Coordinate". Each class definition will take one de
the parent class.
class Coordinate(object):
<define attributes here> <- where the bounds of class definition li
similar to def, indent code to indicate which statements
are part of the class definition
the word object means that Coordinate is a Python
object and inherits all its attributes (coming soon)
• Coordinate is a subclass of object
• object is a superclass of Coordinate
6.00.01X LECTURE 9
How do you put attributes in the class definition?
Ex. Coordinate has X and Y values. So the attributes will have to have x and y values that are associated with
6.00.01X LECTURE 10
Implementing the class Using the class
6.00.01X LECTURE 11
Implementing the class Using the class
ACTUALLY CREATING AN
INSTANCE OF A CLASS
c = Coordinate(3,4)
origin = Coordinate(0,0)
print(c.x)
print(origin.x) no need to repeat s
Coordinate is creating a new frame associated with c.
within c there is a binding for x and y
c.x is 'get the value of c (which is a frame), and then inside that
frame, lookup the value of x.
data attributes of an instance are called instance
variables
don’t provide argument for self, Python does this
automatically
origin.x gives you back 0 because each in case, they are referring to a different frame in which the variable x is boun
6.00.01X LECTURE 12
Implementing the class Using the class
ACTUALLY CREATING AN
INSTANCE OF A CLASS
c = Coordinate(3,4)
origin = Coordinate(0,0)
print(c.x)
print(origin.x)
think of c as pointing to a frame (like we saw with
function calls)
• within the scope of that frame we bound values to data
attribute variables
• c.x is interpreted as getting the value of c (a frame) and
then looking up the value associate with x within that frame
(thus the specific value for this instance)
6.00.01X LECTURE 13
6.00.01X LECTURE 14
Methods are ways to manipulate the data attributes. Think of it as a function or procedure that only works with
WHAT IS A METHOD?
procedural attribute, like a function that works only
with this class
Python always passes the actual object as the first
argument, convention is to use self as the name of the
first argument of all methods
the “.” operator is used to access any attribute
• a data attribute of an object
• a method of an object
6.00.01X LECTURE 15
Implementing the class Using the class
6.00.01X LECTURE 16
Implementing the class Using the class
in this version, c being a frame what's the distance between c and origin.
inside coordinate frame, look at distance method
and lookup value of distance. python already provides
c as first argument to this distance function and apply both parameters, c and origin.
6.00.01X LECTURE 17
Implementing the class Using the class
PRINT REPRESENTATION OF
AN OBJECT
In [1]: c = Coordinate(3,4)
In [2]: print(c)
<__main__.Coordinate object at 0x7fa918510488>
uninformative print representation by default
define a __str__ method for a class when printing instance, python will prin
the string method
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
In [3]: print(c)
<3,4>
6.00.01X LECTURE 19
Implementing the class Using the class
define special method string. However you want to define, it must return a string.
6.00.01X LECTURE 20
Implementing the class Using the class
6.00.01X LECTURE 21
__sub__ will take the instance itself, subtract, the x values, and return a coordinate.
SPECIAL OPERATORS
+, -, ==, <, >, len(), print, and many others
https://docs.python.org/3/reference/datamodel.html#basic-customization
6.00.01X LECTURE 22
6.00.01X LECTURE 23
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
• print representation
• add, subtract
• convert to a float
6.00.01X LECTURE 24
Implementing the class Using the class
You need init method to define what it means to create one of these objects. It takes 2 arguments besides self.
You also want to define what to do to print it out, which is a string representation of the number and denominato
6.00.01X LECTURE 25
Implementing the class Using the class
In [11]: print(oneHalf)
1 / 2
In [12]: print(twoThirds)
2 / 3
6.00.01X LECTURE 26
Implementing the class Using the class
6.00.01X LECTURE 27
Implementing the class Using the class
In [13]: oneHalf.getNumer()
Out[13]: 1
the dot says: get the binding for getNumer(). Use the () to actually invoke it.
In [14]: fraction.getDenom(twoThirds)
Out[14]: 3
6.00.01X LECTURE 28
Implementing the class Using the class
ADDING METHODS
class fraction(object):
def __init__(self, numer, denom):
self.numer = numer
self.denom = denom
def __str__(self):
return str(self.numer) + ' / ' + str(self.denom)
def getNumer(self):
return self.numer
def getDenom(self):
return self.denom
def __add__(self, other):
numerNew = other.getDenon() * self.getNumer() \
+ other.getNumER() * self.getDenom()
denomNew = other.getDenom() * self.getDenom()
return fraction(numerNew, denomNew)
def __sub__(self, other):
numerNew = other.getDenom() * self.getNumer() \
- other.getNumer() * self.getDenom()
denomNew = other.getDenom() * self.getDenom()
return fraction(numerNew, denomNew)
__add__: defines what it means to add two fractions together. Note we have the return function
6.00.01X LECTURE 29
Implementing the class Using the class
ADDING METHODS
In [9]: oneHalf = fraction(1,2)
In [16]: print(new)
7 / 6
6.00.01X LECTURE 30
Implementing the class Using the class
In [18]: new.convert()
Out[18]: 1.1666666666666667
6.00.01X LECTURE 32
why do we want to create separate getters and setters? Because we don't want to directly manipulate the interna
6.00.01X LECTURE 33
Implementing the class Using the class
6.00.01X LECTURE 34
Implementing the class Using the class
In [20]: print(s)
{}
In [21]: s.insert(3)
In [22]: s.insert(4)
In [23]: s.insert(3)
In [24]: print(s)
{3, 4}
6.00.01X LECTURE 35
Implementing the class Using the class
In [25]: s.member(3)
True
In [26]: s.member(6)
False
6.00.01X LECTURE 36
Implementing the class Using the class
In [27]: s.remove(3)
In [28]: s.insert(6) using built in methods to manipulate set
In [29]: print(s)
{4,6}
6.00.01X LECTURE 37
Implementing the class Using the class
In [30]: s.remove(3)
ValueError: 3 not found
if removing an element that's not in the set, it will throw out an error.
6.00.01X LECTURE 38
6.00.01X LECTURE 39
OOP is helpful because it is good to bundle together objects that share common attributes.
It lets us make distinction between how to implement and how to use the object.
6.00.01X LECTURE 40
We want to distinguish between Implementing and Using the class.
IMPLEMENTING USING
THE CLASS vs THE CLASS
write code from two different perspectives
all class examples we saw so far were numerical
6.00.01X LECTURE 41
Class is the Type itself. Class is defined generically.
6.00.01X LECTURE 42
Ex. We may have different cats, but also different instances of rabbits. But we said both can be clustered together
CLASSES OF OBJECTS?
• mimic real life
• group different objects as part of the same type
6.00.01X LECTURE 43
WHY USE OOP AND
CLASSES OF OBJECTS?
• mimic real life
• group different objects as part of the same type
6.00.01X LECTURE 44
GROUPS OF OBJECTS HAVE
ATTRIBUTES
data attributes
• how can you represent your object with data?
• what it is
• <for a coordinate: x and y values>
• <for an animal: age, name>
procedural attributes (behavior/operations/methods)
• what kinds of things can you do with the object?
• what it does
• <for a coordinate: find distance between two>
• <for an animal: make a sound>
Helps generalize what procedural attributes would do
6.00.01X LECTURE 45
DEFINING A CLASS (Recap)
class Animal(object):
def __init__(self, age):
self.age = age
self.name = None
myanimal = Animal(3)
In this case, Name is not something you pass it, but the init function will create a binding for both Age and Name in
Initially, Name is set to none. i.e. newborn animal, it doesn't have a name yet and we get to name it.
6.00.01X LECTURE 46
GETTER AND SETTER
METHODS
class Animal(object):
def __init__(self, age):
self.age = age
self.name = None
def get_age(self): methods to get out values. Something to get out
return self.age and something to get out Age.
def get_name(self):
return self.name 'Setters' are methods that can change th
def set_age(self, newage): bindings for internal attributes.
self.age = newage
def set_name(self, newname=""): If we want to change the age, you can re
self.name = newname it to the new value
def __str__(self):
Finally, way to print
out what the animal is return "animal:"+str(self.name)+":"+str(self.age)
getters and setters should be used outside of class to
access data attributes
6.00.01X LECTURE 47
AN INSTANCE and
DOT NOTATION (Recap)
instantiation creates an instance of an object
a = Animal(3)
dot notation used to access attributes (data and
methods) though it is better to use getters and setters
to access data attributes
a.age
a.get_age()
You want to use the getter because you want to separate the internal representation
from access to that representation.
6.00.01X LECTURE 48
INFORMATION HIDING
author of class definition may change data attribute
variable names
class Animal(object):
def __init__(self, age):
self.years = age replace age data with use of
def get_age(self): can simply change getter to refle
return self.years years
if you are accessing data attributes outside the class and
class definition changes, may get errors
outside of class, use getters and setters instead
In this case, changed Class definition to Yea
use a.get_age() NOT a.age So if you try to use a.age and you made a ch
• good style you will get an error.
• easy to maintain code
So use the getter
• prevents bugs
6.00.01X LECTURE 49
PYTHON NOT GREAT AT
INFORMATION HIDING
allows you to access data from outside class definition
print(a.age)
allows you to write to data from outside class definition
a.age = 'infinite'
allows you to create data attributes for an instance from
outside class definition
a.size = "tiny"
6.00.01X LECTURE 50
self AND OTHER ARGS
self determined from instance, passed in as argument
• for the method: def __init__(self, age)
• creates self, passes it in automatically as argument
a = Animal(3)when we call it, it is provided automatically. We don't need to specifically call
• for the method: def get_age(self)
• call method with a.get_age()
• or an alternate way Animal.get_age(a)
6.00.01X LECTURE 51
6.00.01X LECTURE 52
HIERARCHIES
6.00.01X LECTURE 53
Animal has children subclasses and will inherit the behaviours as the parent unless you explicitly override.
HIERARCHIES
parent class
(superclass) Animal
child class
(subclass)
• inherits all data Person Cat Rabbit
and behaviors of
parent class
• add more info
• add more behavior Student
• override behavior
6.00.01X LECTURE 54
INHERITANCE
class Animal(object):
def __init__(self, age):
self.age = age
self.name = None
def get_age(self):
return self.age
def get_name(self):
return self.name
def set_age(self, newage):
self.age = newage
def set_name(self, newname=""):
self.name = newname
def __str__(self):
return "animal:"+str(self.name)+":"+str(self.age)
6.00.01X LECTURE 55
INHERITANCE
class Cat(Animal):
def speak(self): Speak method
print("meow”)
def __str__(self):
return "cat:"+str(self.name)+":"+str(self.age)
Prints almost same things, name and age, but also the symbol Cat so you know you'r
a cat.
Inherits all the property of an animal.
In [34]: print(Animal.__str__(jelly)
Animal:JellyBelly:1
In [37]: blob.set_name()
In [38]: print(blob)
animal::1
6.00.01X LECTURE 57
INHERITANCE
class Cat(Animal):
def speak(self):
print("meow”)
def __str__(self):
return "cat:"+str(self.name)+":"+str(self.age)
class Rabbit(Animal):
def speak(self):
print(”meep”)
def __str__(self):
return ”rabbit:"+str(self.name)+":"+str(self.age)
6.00.01X LECTURE 58
USING THE HIERARCHY
In [31]: jelly = Cat(1)
In [34]: blob = Animal(1)
In [38]: peter = Rabbit(5)
In [39]: jelly.speak()
meow
In [40]: peter.speak()
meep
In [41]: blob.speak()
AttributeError: 'Animal' object has no
attribute 'speak'no speak method associated with Animal.
there are two speak methods. One with Rabbit and One with Cats.
Given jelly, let me get what that is bound to in the global or the top level environment. That is a pointer to an inst
6.00.01X LECTURE 59
WHICH METHOD TO USE?
• subclass can have methods with same name as
superclass
• subclass can have methods with same name as other
subclasses i.e. we have speak in both cat and in rabbit
• for an instance of a class, look for a method name in
current class definition
• if not found, look for method name up the hierarchy
(in parent, then grandparent, and so on) otherwise, throw and error
• use first method up the hierarchy that you found with
that method name
6.00.01X LECTURE 60
class Person(Animal):
def __init__(self, name, age):
Animal.__init__(self, age)
Animal.set_name(self, name)
self.friends = []
def get_friends(self):
return self.friends
def add_friend(self, fname):
if fname not in self.friends:
self.friends.append(fname)
def speak(self):
print("hello”)
def age_diff(self, other):
# alternate way: diff = self.age - other.age
diff = self.get_age() - other.get_age()using getters of 2 ppl to get the diff
if self.age > other.age:
print(self.name, "is", diff, "years older than", other.name)
else:
print(self.name, "is", -diff, "years younger than", other.name)
def __str__(self):
return "person:"+str(self.name)+":"+str(self.age)
6.00.01X LECTURE 61
USING THE HIERARCHY
In [42]: eric = Person(‘Eric’, 45)
In [43]: john = Person(‘John’, 55)
In [44]: eric.speak()
Hello
In [45]: eric.age_diff(john)
Eric is 10 years younger than John
In [46]: Person.age_diff(john,eric)
John is 10 years older than Eric
6.00.01X LECTURE 62
import random
6.00.01X LECTURE 63
USING THE HIERARCHY
In [42]: eric = Person(‘Eric’, 45)
In [47]: fred = Student(‘Fred’, 18, ‘Course VI’)
In [48]: print(fred)
student:Fred:18:Course VI
In [49]: fred.speak()
i have homework
In [50]: fred.speak()
i have homework
In [51]: fred.speak()
i am watching tv
In [52]: fred.speak()
i should eat
Everytime you call speak(), it will compute a random number between 0 and 1, and he will either say
"I have homework, I am watching TV" or I should eat"
6.00.01X LECTURE 64
6.00.01X LECTURE 65
INSTANCE CLASS
VARIABLES vs VARIABLES
we have seen instance introduce class variables
variables so far in code that belong to the class
specific to an instance defined inside class but
created for each outside any class methods,
instance, belongs to an outside __init__
instance shared among all
used the generic variable objects/instances of that
name self within the class class
not inside init because otherwise it will be created i
definition the instance.
self.variable_name
6.00.01X LECTURE 66
RECALL THE Animal CLASS
class Animal(object):
def __init__(self, age):
self.age = age
age and name are instance variables. Only accessed within ins
self.name = None
def get_age(self):
return self.age
def get_name(self):
return self.name
def set_age(self, newage):
self.age = newage
def set_name(self, newname=""):
self.name = newname
def __str__(self):
return "animal:"+str(self.name)+":"+str(self.age)
6.00.01X LECTURE 67
CLASS VARIABLES AND THE
Rabbit SUBCLASS
subclasses inherit all data attributes and methods of the
parent class rabbit still inherits from animal class
class Rabbit(Animal):
tag = 1
def __init__(self, age, parent1=None, parent2=None):
Animal.__init__(self, age)
self.parent1 = parent1
self.parent2 = parent2
self.rid = Rabbit.tag
Rabbit.tag += 1
everytime create init, and tag increas
tag used to give unique id to each new rabbit instance
now initialization method for rabbit will pass in two other arguments (parent1 and parent 2). Also, will set bindings f
6.00.01X LECTURE 68
Rabbit GETTER METHODS
class Rabbit(Animal):
tag = 1
def __init__(self, age, parent1=None, parent2=None):
Animal.__init__(self, age)
self.parent1 = parent1
self.parent2 = parent2
self.rid = Rabbit.tag
Rabbit.tag += 1
def get_rid(self):
return str(self.rid).zfill(3)
def get_parent1(self):
return self.parent1
def get_parent2(self):
return self.parent2
since get_rid is a string, you can pad it so everything prints at same size.
6.00.01X LECTURE 69
EXAMPLE USAGE
In [53]: peter = Rabbit(2)
In [54]: peter.set_name(‘Peter’)
In [55]: hopsy = Rabbit(3)
In [56]: hopsy.set_name(‘Hopsy’)
In [57]: cotton = Rabbit(1, peter, hopsy)
In [58]: cotton.set_name(‘Cottontail’)
In [59]: print(cotton)
animal:Cottontail:1
In [60]: print(cotton.get_parent1())
animal:Peter:2
6.00.01X LECTURE 70
WORKING WITH YOUR OWN
TYPES
def __add__(self, other):
# returning object of same type as this class
return Rabbit(0, self, other)
In [64]: print(mopsy.get_parent2())
animal:Hopsy:3
6.00.01X LECTURE 72
using the tags to compare two rabbits.
SPECIAL METHOD TO
COMPARE TWO Rabbits
decide that two rabbits are equal if they have the same two
parents
def __eq__(self, other): two rabbits equal if they have they same parent.
parents_same = self.parent1.rid == other.parent1.rid \
and self.parent2.rid == other.parent2.rid
parents_opposite = self.parent2.rid == other.parent1.rid \
and self.parent1.rid == other.parent2.rid
return parents_same or parents_opposite
comparing ids of parents since ids are unique (due to class var)
note that comparing objects (self.parent1==other.parent1)
will call the __eq__ method over and over until call it on None
(will get AttributeError)
6.00.01X LECTURE 73
EXAMPLE USAGE
In [53]: peter = Rabbit(2)
In [54]: peter.set_name(‘Peter’)
In [55]: hopsy = Rabbit(3)
In [56]: hopsy.set_name(‘Hopsy’)
In [57]: cotton = Rabbit(1, peter, hopsy)
In [58]: cotton.set_name(‘Cottontail’)
In [61]: mopsy = peter + hopsy
In [62]: mopsy.set_name('Mopsy')
6.00.01X LECTURE 74
SUMMARY OF CLASSES & OOP
bundle together objects that share
• common attributes and
• procedures that operate on those attributes
use abstraction to make a distinction between how to
implement an object vs how to use the object
build layers of object abstractions that inherit
behaviors from other classes of objects
create our own classes of objects on top of Python’s
basic classes
6.00.01X LECTURE 75