In-Class Lecture 17
Please Log In for full access to the web site.
Note that this link will take you to an external site (https://shimmer.mit.edu) to authenticate, and then you will be redirected back to this page.
Download Materials
Summary
Learning Objectives (click to reveal)
- An object is made up of two kinds of attributes: data and procedures
- An object can be represented in many different ways
- Create a Python class in code
- Set up data attributes in a Python class
- Add methods to a Python class
- Understand the meaning behind self in the context of data attributes and methods
- The meaning of dot notation
Takeaways (click to reveal)
Object Oriented Programming Basics
- Creating your own object type requires a bit of creativity. You get to made some design decisions regarding: data attributes and procedures.
- Creating a Python class means you get to make these design decisions. For ex. someone created a class for the
list
object, or thedict
object. - Using a class that you (or someone else wrote) involves creating objects of that new type. For ex. we've been creating many lists to manipulate:
L1 = [3,5,1]
andL2 = [0,0,0] and
L1.sort()
andL2.extend(L1)
. - Data attributes are variables that describe your object.
- Data attributes are declared inside a class with self. For ex.
self.name = None
. - Procedures are functions that work with your object. They are called methods.
- Methods are defined as regular functions whose first parameter is always
self
. - Dot notation is how we call methods on objects. For ex.
L.sort()
whereL
is an object of typelist
andsort
is the method.
Methods vs. Functions
- Methods are always defined inside a class definition.
- Methods are just functions defined inside a class definition.
- Methods have
self
as the first parameter. - Methods only work with objects of that class type.
- Methods use dot notation. For ex. we've seen
L.append(5)
- If you define a function outside of a class, it's just a regular function that we've been creating so far.
- A regular function defined outside of a class cannot be run on an object using dot notation.
QUICK-CHECK: A post-video exercise to ensure we're all ready for the in-class session!
There is only one right way to create a Python class to meet a set of specifications. Defining a Python class automatically creates an object instance of that type in memory. A Python class definition must always define both data attributes AND methods. Suppose a method named meth(self)
exists. Any object whose class definition contains a method named meth
may use dot notation to call meth()
on itself.
Try: Type these into the repl
Which of these are true?
|
Which of these are true?
|
Notes:
- A
list
or adict
is a standard Python object so Python lets you create them using shorthand notations involving[]
or{}
. - Underneath it all, they are object types defined using a Python class!
- Python lets you create a
list
based on atuple
object. - Python lets you create a
dict
based on some combination oflist
s ortuple
objects. But be careful -- dictionaries map ONE key object to ONE value object.
Try: Fix each of these code snippets
class A(object): def __init__(self, a): a = a def get_a(self): return a mya = A(5) mya.get_a()
class B(object): def __init__(self): self.b = None def get_b(): return self.b myb = B() myb.get_b()
class C(object): def __init__(self, L): self.myL = L def getL(self): return self.L[:] C([1,2,3]).getL() getL([4,5,6])
Try: Think about some other attributes of a book: data attributes and behaviors
We will represent a Book
object. It's your turn to make some design decisions. Start with this code add more attributes and methods to customize our book object. The test cases here don't test anything beyond what is shown, so don't use them to test correctness of your code.
- Work in your IDE to make the design decisions for the
Book
class definition. - Create a bunch of
Book
objects and run your methods on them to test your code. - Paste your final customized
Book
class implementation here.
Be creative and play around with adding all sorts of new data attributes and methods you may want a book to have!
class Book(object):
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
def display_info(self):
return f"{self.title} by {self.author} has {self.pages} pages."
book1 = Book("We Never Go Out Of Style", 'L. Crew', 100)
book2 = Book("I Knew You Were Trouble", 'A. Half', 100)
Try: Implement this class according to this specification.
- There are three ways you can represent a rectangle.
- Using two numbers: one or the width and one for the height
- Using one tuple: the first element represents the width and the second represents the height
- Using 4 tuples representing the 4 corners of a rectangle:
- (x_topleft, y_topleft)
- (x_topright, y_topright)
- (x_botleft, y_botleft)
- (x_botright, y_botright)
- After deciding on this initial representation, write the methods assuming your chosen data attributes (i.e. assuming that representation). Try implementing all 3 of these representations for practice.
- No matter what representation you choose, creating a
Rectangle
object and calling a method on it will be the same code (see example test cases below). - The implementation should be irrelevant to an outsider just interested in using your
Rectangle
class!
class Rectangle(object):
""" A class to represent a rectangle. """
def __init__(self, width, height):
""" width and height are ints """
# your code here
def compute_area(self):
""" Returns the area of self """
# your code here
def compute_perimeter(self):
""" Return the perimeter of self """
# your code here
def is_square(self):
""" Returns True if the width equals the height, and False otherwise """
# your code here
# Examples:
my_rectangle = Rectangle(10,5)
print(my_rectangle.compute_area()) # prints 50
print(my_rectangle.compute_perimeter()) # prints 30
print(my_rectangle.is_square()) # prints False
Notes: 3 Possible Solutions
- Solution 1: Solution that defines the object using 2 attributes: width and height.
- A
Rectangle
object is created withRectangle(10,5)
. class Rectangle(object): def __init__(self, width, height): self.width = width self.height = height def compute_area(self): return self.width * self.height def compute_perimeter(self): return 2*(self.width) + 2*(self.height) def is_square(self): return True if self.width==self.height else False
- A
- Solution 2: Solution uses only one data attribute.
- Data attribute is a tuple with two components.
- A
Rectangle
object is created the same way as Solution 1:Rectangle(10,5)
. class Rectangle(object): def __init__(self, width, height): self.dimensions = (width, height) def compute_area(self): return self.dimensions[0] * self.dimensions[1] def compute_perimeter(self): return 2*(self.dimensions[0]) + 2*(self.dimensions[1]) def is_square(self): return True if self.dimensions[0]==self.dimensions[1] else False
- Solution 3: Alternate design decision with a different
__init__
and implementation.- The way you create an object with this implementation is
Rectangle((1,6), (11,6), (1,1), (11,1))
notRectangle(10,5)
- This implementation reads in the 4 corners instead of a value for the width and a value for the height. This implementation looks wordy. And if all we want to do is to get areas and perimeters, then it is!
- This implementation's strength lies in the fact that it allows flexibility for our
Rectangle
to have a position! The other implementations above do not, at least as defined right now.class Rectangle(object): """ A class to represent a rectangle. """ def __init__(self, topleft, topright, botleft, botright): """ topleft, topright, botleft, botright are tuples with 2 elements, representing the 4 corners of a rectangle """ assert topleft[1] == topright[1] assert topleft[0] == botleft[0] assert botleft[1] == botright[1] assert topright[0] == botright[0] self.topleft = topleft self.topright = topright self.botleft = botleft self.botright = botright def get_width_and_height(self): """ Retruns a tuple with the first element representing the width and the second elements representing the height """ width = self.topright[0] - self.topleft[0] height = self.topright[1] - self.botleft[1] return (width, height) def compute_area(self): """ Returns the area of self """ return self.get_width_and_height()[0] * self.get_width_and_height()[1] def compute_perimeter(self): """ Return the perimeter of self """ return 2*self.get_width_and_height()[0] + 2*self.get_width_and_height()[1] def is_square(self): """ Returns True if the width equals the height, and False otherwise """ return True if self.get_width_and_height()[0]==self.get_width_and_height()[1] else False my_rectangle = Rectangle((1,6), (11,6), (1,1), (11,1)) print(my_rectangle.compute_area()) # prints 50 print(my_rectangle.compute_perimeter()) # prints 30 print(my_rectangle.is_square()) # prints False
- The way you create an object with this implementation is
Please use this box to comment on the content of this lecture (participation!). Post lingering questions, an aha moment, something confusing, interesting, funny, surprising, etc.