Practical uses of Classes
What's the point of a point?
We've built a point class, which we could use to display elements on a grid, but we don't have the tools to do that quite yet.
However, we can still think of other uses for our class. What can we build now that we have a definition of a point? We could define a line using two points, or a triangle using three points, a circle using a point and a radius. We could define an arbitrary shape as a list of points. Old school video game programmers relied on this a lot!
Let's see how we can build a simple Rectangle
class. What we want out of objects of this class is to:
- Display enough information that we can easily picture where our rectangle is on a grid.
- Move the rectangle around.
- Check if a point is within the rectangle or not.
Let's start with our data. How would you define the attributes of Rectangle
objects? Think about it for a few minutes before reading ahead.
There is no one way to make this work, you could provide your constructor with:
- The coordinate of the top-left corner of the rectangle, it's width, and it's length.
- The coordinate of the top-left corner and bottom-right corner of the
Rectangle
Luckily, we already have a good way of storing coordinates by using our Point
class. Let's try and implement the first strategy.
The Rectangle class
class Rectangle:
def __init__(self, corner_point, width, height):
self.top_left_corner = corner_point
self.width = width
self.height = height
So how would we represent this rectangle?

We can provide our constructor with a Point
object, and two numbers for the width and height
This is an example of composition: You can use objects as attributes of other, more complex objects. This helps us leverage all the code we have writen before, as we will see in just a moment.
For example if we wanted to have a move_up
method for our rectangle, we could just leverage the fact that we can move a point:
class Rectangle:
def __init__(self, corner_point, width, height):
self.top_left_corner = corner_point
self.width = width
self.height = height
def move_up(self):
self.top_left_corner.move_up()
So we can now move up the entire rectangle:
Notice here that the Point
class has a method called move_up
, and so does the Rectangle
class. This is not an issue though, as Python can tell which definition to use based on the type of the object the method is called on.
When we call the move_up
method on our rectangle, it will then call the move_up
method of the Point
class on the specific point that represents our top left corner of the rectangle.
Displaying information about classes
We've been accessing attributes of our top left corner using lines like print(my_rectangle.top_left_corner.x)
What would happen if we just tried to print our top_left_corner directly?
print(my_rectangle.top_left_corner)
# outputs <__main__.Point object at 0x7f97f49937c0>
That's odd! we see that we are dealing with a Point
object, which is sensiblle, but then we get this mess of characters. We will dig into what they mean next week, but for now let's focus on how to better present the data.
Turns out, this is such a common problem that python already has a solution for it. the __str__
method is a special method of a class that lets you define how to convert it to a String. This means that you can define what shows up when you print an instance of that class.
Let's define an __str__
method for the point class:
With this change, we reap the benefit not only when interacting with Point
objects directly, but also when using other classes that use Point
objects.
print(my_rectangle.top_left_corner) # outputs (1, 4)
Check for understanding:
How would you modify the Rectangle
class so that we can print a Rectangle
object directly and see meaningful information?
Solution:
It's really up to you how to describe a rectangle, as long as you can easily identify it. Your solution should be something along the lines of:
def __str__(self):
return f"A rectangle of width {self.width}, height {self.height}, and with a top left corner positioned at {self.top_left_corner}"
What's next?
In the next section you will find multiple practice exercises, as well as your first assignment! You will be asked to create increasingly more complex classes and methods.
Next week, we will keep working with classes and objects, unveil the mystery of the numbers that showed up when we print an object, and learn better techniques to debug our code.