Lecture 18 — Classes, Part 1¶
Overview¶
- Define our own types and associated functions
- Encapsulate data and functionality
- Raise the “level of abstraction” in our code
- Make code easier to write and test
- Reuse code
Potential Examples¶
In each of these, think about what data you might need to store to represent the “object” and what functionality you might need to apply to the data.
- Date
- Time
- Point
- Rectangle
- Student
- Animal
- Molecule
An Example from Earlier in the Semester¶
- Think about how difficult it was to keep track of the information about each restaurant in the Yelp data.
- You had to:
- Remember the indices of (a) the restaurant name, (b) the latitude and longitude, (c) the type of restaurant, (d) the address, etc.
- Form a separate list inside the list for the ratings.
- Write additional functions to exploit this information
- If we used a class to represent each restaurant:
- All of the information about the restaurant would be stored and accessed as named attributes
- Information about the restaurants would be accessed through functions that we write for the class.
Point2d Class¶
Simplest step is to just tell Python that
Point2d
will exist as a class, deferring the addition of information until later.class Point2d(object): pass
The Python reserved word
pass
says that this is the end of the class definition.- We will not need this later when we put information into the class.
Attributes¶
- Classes do not get interesting until we put something in them.
- The first thing we want is variables so that we can put data into a
class.
- In Python these variables are often called attributes.
- Other languages call them member variables.
- We will see three different ways to specify attributes.
Assigning Attributes to Each Instance¶
Points have an
x
and ay
location, so we can write, for example,from math import sqrt p = Point2d() p.x = 10 p.y = 5 dist_from_origin = sqrt(p.x**2 + p.y**2)
We have to do this for each class instance.
This is prone to mistakes:
- Could forget to assign the attributes
- Could accidentally use different names for what is intended to be the same attribute.
Example of an error
q = Point2d() q.x = -5 dist_from_origin = sqrt(q.x**2 + q.y**2) # q.y does not exist
Defining the Attributes Inside the Class¶
The simplest way to make sure that all variables that are instances of a class have the appropriate attributes is to define them inside the class.
For example, we could redefine our class as
class Point2d(object): x = 0 y = 0
All instances of
Point2d
now have two attributes,x
andy
, and they are each initialized to 0.We no longer need the
pass
because there is now something in the class.
Defining the Attributes Through An Initializer / Constructor¶
We still need to initialize
x
andy
to values other than0
:p = Point2d() p.x = 10 p.y = 5
What we’d really like to do is initialize them at the time we actually create the
Point2d
object:p = Point2d(10,5)
We do this through a special function called an initializer in Python and a constructor in some other programming languages.
Inside the class this looks like
class Point2d(object): def __init__( self, x0, y0 ): self.x = x0 self.y = y0
Our code to create the point now becomes
p = Point2d(10,5)
Notes:
- Python uses names that start and end with two
'_'
to indicate functions with special meanings. More on this later in the lecture. - The name
self
is special notation to indicate that the object itself is passed to the function.
- Python uses names that start and end with two
If we’d like to initialize the point to \((0,0)\) without passing these values to the constructor every time then we can specify default arguments
class Point2d(object): def __init__( self, x0=0, y0=0 ): self.x = x0 self.y = y0
allowing the initalization
p = Point2d()
Methods — Functions Associated with the Class¶
We create functions that operate on the class objects inside the class definition:
import math class Point2d(object): def __init__( self, x0, y0 ): self.x = x0 self.y = y0 def magnitude(self): return math.sqrt(self.x**2 + self.y**2) def dist(self, o): return math.sqrt( (self.x-o.x)**2 + (self.y-o.y)**2 )
these are called methods
This is used as
p = Point2d(0,4) q = Point2d(5,10) leng = q.magnitude() print("Magnitude {:.2f}".format( leng )) print("Distance is {:.2f}".format( p.dist(q)))
The method
magnitude
takes a single argument, which is thePoint2d
object calledself
. Let’s examine this:The call
q.magnitude()
appears to have no arguments, but when Python sees this, it turns it into its equivalent:Point2d.magnitude(q)
which is completely legal Python syntax.
The name
self
is not technically special in Python, but it is used by convention to refer to the object that the method is “called upon”. This is q` in the callq.magnitude()
The method
dist
takes twoPoint2d
objects as arguments. The example callp.dist(q)
becomes
Point2d.dist(p,q)
so now argument
p
maps to parameterself
and argumentq
maps to parameterso
Lecture Exercises, Part 1¶
Our lecture exercises for today will involve adding to the Point2d
class and testing it. Make sure you have downloaded the
Point2d.py
file from the Piazza site.
We will allow some time to work on the first lecture exercise.
Operators and Other Special Functions¶
We’d like to write code that uses our new objects in the most intuitive way possible.
For our point class, this involves use of operators such as
p = Point2d(1,2) q = Point2d(3,5) r = p+q s = p-q t = -s
Notice how in each case, we work with the
Point2d
variables (objects) just like we do with int and float variable (objects).We implement these by writing the special functions
__add__
,__sub__
, and__neg__
For example, inside the
Point2d
class we might havedef __add__(self,other): return Point2d(self.x + other.x, self.y+other.y)
Very important: this creates a new
Point2d
object.When Python sees
p+q
, it turns it into the function callPoint2d.__add__(p,q)
which is exactly the syntax of the function definition we created.
We have already seen this with operators on integers and strings. As examples,
5+6
is equivalent to
int.__add__(5,6)
and
str(13)
is equivalent to
int.__str__(13)
Implicit in this discussion is the notion that
int
is in fact a class in Python. The same is true ofstr
andfloat
andlist
.Note that we can also define boolean operators such as
==
and!=
through the special functions__eq__
and__neq__
Classes and Modules¶
- Each class should generally be put into its own module, or several
closely-related classes should be combined in a single module.
- We are already doing this with
Point2d
.
- We are already doing this with
- Doing so is good practice for languages like C++ and Java, where classes are placed in separate files.
- Testing code can be included in the module or placed in a separate module.
- We will demonstrate this in class and post the result on the course website.
More Lecture Exercises¶
At this point we will stop and take a bit of time to work on the next part of the lecture exercises.
When to Modify, When to Create New Object¶
- Some methods, such as
scale
, modify a singlePoint2d
object - Other methods, such as our operators, create new
Point2d
objects without modifying existing ones. - The choice between this is made on a method-by-method basis by thinking about the meaning — the semantics — of the behavior of the method.
Programming Conventions¶
- Don’t create attributes outside the class.
- Don’t directly access or change attributes except through class
methods.
- Languages like C++ and Java have constructions that enforce this.
- In languages like Python it is not a hard-and-fast rule.
- Class design is often most effective by thinking about the required
methods rather than the required attributes.
- As an example, we rarely think about how the Python
list
anddict
classes are implemented.
- As an example, we rarely think about how the Python
Time Example¶
- In the remainder of the lecture, we will work through an extended
example of a
Time
class - By this, we mean the time of day, measured in hours, minutes and seconds.
- We’ll brainstorm some of the methods we might need to have.
- We’ll then consider several different ways to represent the time
internally:
- Hours, minutes and seconds
- Seconds only
- Military time
- Despite potential internal differences, the methods — or at least the
way we call them — will remain the same
- This is an example of the notion of encapsulation, which we will discuss more in Lecture 19.
- At the end of lecture, the resulting code will be posted and tests will be generated to complete the class definition.
Summary¶
- Define new types in Python by creating classes
- Classes consist of attributes and methods
- Attributes should be defined and initialized through the special
method call
__init__
. This is a constructor - Other special methods allow us to create operators for our classes.
- We looked at a Point2d and Time example.