Skip to main content

Section 13.5 User Defined Classes

We’ve already seen classes like str, int, float and Turtle. These were defined by Python and made available for us to use. However, in many cases when we are solving problems, we need to create data objects that are related to the problem we are trying to solve. We need to create our own classes.
As an example, consider the concept of a rectangle. In two dimensions, a rectangle is two numbers (width and height) that are treated collectively as a single object.
Thinking about our diagram above, we could draw a Rectangle object as shown here.
Some of the typical operations that one associates with rectangles might be to ask the rectangle for its area or its perimeter. You may also wish to calculate the length of the diagonal of the rectangle, or determine the ratio of the width to height. We’ll shortly see how we can organize these together with the data.
Now that we understand what a Rectangle object might look like, we can define a new class. We’ll want our rectangles to each have a width and a height attribute, so our first class definition looks like this.
class Rectangle:
    """ Rectangle class for representing and manipulating width and height. """

    def __init__(self):
        """ Create a new 1 by 1 rectangle """
        self.width = 1
        self.height = 1
Class definitions can appear anywhere in a program, but they are usually near the beginning (after the import statements). The syntax rules for a class definition are the same as for other compound statements. There is a header which begins with the keyword class, followed by the name of the class, and ending with a colon.
If the first line after the class header is a string, it becomes the docstring of the class, and will be recognized by various tools. (This is also the way docstrings work in functions, as described in Section 6.12.)
Every class should have a method with the special name __init__. This initializer method, often referred to as the constructor, is automatically called whenever a new instance of Rectangle is created. It gives the programmer the opportunity to set up the attributes required within the new instance by giving them their initial state values. The self parameter is automatically set to reference the newly-created object. You can think of self as meaning “the object we are currently working with” (in this case, creating). There is nothing special about the parameter name self, but it’s an unwritten rule that all Python programmers use that variable name in object constructors.

Note 13.5.1.

Even though initializer methods create objects, they do not have a return statement in them.
Let’s use our new Rectangle class now.
During the initialization of the objects, we created two attributes called width and height for each, and gave them both the value 1.

Note 13.5.2.

The asignments are not to width and height, but to self.width and self.height. The attributes width and height are always attached to a particular instance. The instance is always explicitly referenced with dot notation.
You will note that when you run the program, nothing seems to happen. It turns out that this is not quite the case. In fact, two Rectangles have been created, each having a width and height with value 1. However, because we have not asked the rectangles to do anything, we don’t see any other result.
You can see this for yourself, via codelens:
The following program adds a few print statements. Run it, and then we’ll analyze the output.
The variables r1 and r2 are assigned references to two new Rectangle objects. A function like Turtle or Rectangle that creates a new object instance is called a constructor. Every class automatically uses the name of the class as the name of the constructor function. Using the construtor implicity calls the __init__ method.
The output suggests that r1 and r2 are Rectangle objects. If you run this program with a Python interpreter, the output will look like this:
Nothing seems to have happened with the rectangles
r1 is <__main__.Rectangle object at 0x7f3048cd6320>
r2 is <__main__.Rectangle object at 0x7f3048cd7880>
Are r1 and r2 the same object? False
The number following at in the output shows the memory address for the object reference. You can see that r1 and r2 refer to different locations. Even though both r1 and r2 are instances of Rectangle, they aren’t the same object, and the is operator confirms this by returning False.
It may be helpful to think of a class as a factory for making objects. The class itself isn’t an instance of a rectangle, but it contains the machinery to make point instances. Every time you call the constructor, you’re asking the factory to make you a new object.As the object comes off the production line, its initialization method is executed to get the object properly set up with its factory default settings.
The combined process of “make me a new object” and “get its settings initialized to the factory default settings” is called instantiation.
Check Your Understanding

Checkpoint 13.5.3.

    What is the the output of the following print code?
    class Car:
    
        def __init__(self):
            self.color = "Red"
            self.miles_per_gallon = 27
    
    bmw = Car()
    ford = Car()
    
    test1 = bmw is ford
    test2 = type(bmw) == type(ford)
    
    print(test1, test2)
    
  • True True
  • They are the same class, but are they the same object?
  • True False
  • Are they the same object? Are they the same class?
  • False True
  • Correct, the BMW object is not the Ford object, but they are of the same type.
  • False False
  • Look closer at types of the objects.