CSci 150: Foundations of computer science
Home Syllabus Readings Projects Tests

Classes

We've seen that values in Python each have a type: an integer, a floating-point number, a Boolean, a string, a list, a tuple, a dictionary, or a file handle. In writing larger programs, though, it's useful to develop our own types, so that we can think in terms of manipulating things specific to our application, like customers or warehouses.

Using classes

Developing software by developing new types is called object-oriented programming. Object-oriented programming involves understanding several new terms. First, a class a user-defined type, defining the properties that each value of that type has. An individual value of a class's type is called an instance or object (the two terms are synonymous). Each instance will have some named values providing information about that instance, each called a data attribute. And instances will have methods, which are basically functions that the instance can perform; we've seen several examples of methods before, including the split method that strings have and the append method that lists have.

As an example of a class in Python, let's suppose we want to define a type for representing points, called Point. We'll get to how to define a class later, but for now let's concentrate on what how we might want to use points.

o = Point()           # create point at (0,0)
print(o.x)            # access o's x-coordinate: 0

p = Point()           # create point that we move to (5, 5)
p.move_to(55)

p.move_up(7)          # move point p to (5, 12)
print(p.y)            # access p's y-coordinate: 12

d = o.distance_to(p)  # compute distance between both points
print(d)

The first line creates an individual Point object: “Calling” the class as if it were a function is what creates a new instance of the class. As it happens, each Point instance has two data attributes, an x-coordinate and a y-coordinate, named appropriate enough x and y. So if o refers to an individual instance, I can retrieve o's x-coordinate using o.x.

The Point class also defines some methods that each individual point can perform. One method, named move_to, simply moves the point to the coordinates named in the parameters. Another, named translate, adds the parameters to the point's x- and y-coordinates. And finally, the distance_to method takes another point as a parameter and returns the distance between those two points.

Defining classes

The code above won't work unless we actually have a Point class. In fact, we have to create it. Following is an example:

class Point:
    def __init__(self):
        self.x = 0
        self.y = 0

    def move_to(selfnew_xnew_y):
        self.x = new_x
        self.y = new_y

    def move_up(selfchange_in_y):
        self.y += change_in_y

    def distance_to(selfother_point):
        dx = self.x - other_point.x
        dy = self.y - other_point.y
        return (dx * dx + dy * dy) ** 0.5

We define the class beginning with a class declaration, followed by the name of the class we are defining. Inside the class are a list of several “functions” that actually correspond to the class's methods.

One method named __init__ (note the double-underscore on both sides) is called the constructor. It has a special function: This method is executed immediately upon creation of a new object. When we say “o = Point()”, Python first allocates memory for the object and then enters the constructor, passing the newly created object as the first parameter. The job of the constructor is to assign initial values to each of the object's data attributes.

In this case, the constructor wants to initialize the two attributes of each point, x and a y. Notice how we use the syntax self.x to refer to the data attribute. If we had written “x = 0” instead, we would simply be creating a local variable that would disappear as soon as the function is over. But when we write self.x, we are depositing the data attribute x into self, which is the newly created object.

Following __init__, you see three more functions, each corresponding to one of the methods. Each function starts with an additional parameter, again named self, which refers to the object on which the method has been invoked. When Python executes “p.move_to(45)”, it enters move_to with p for the first argument self, 4 for the second argument new_x, and 5 for the third argument new_y. In this case, move_to should update p's x-coordinate and y-coordinate, so you see the body updating the x and y attributes of self — that is, p.

Notice how distance_to ends up taking another point as its argument. When we call “o.distance_to(p)”, the first parameter self would be o, while the second parameter other_point would be p. Inside, we want to retrieve the x-coordinates of both points, so you see self.x to get o's x-coordinate and other_point.y to get p's x-coordinate. The difference is saved into dx, which is simply a temporary variable to help with the computation (since we didn't write self.dx), and so it disappears as soon as the function returns.