Part 8

# More examples of classes

## Example 1: the Rectangle class

Let's have a look at a class which models a rectangle in two-dimensional space:

``````class Rectangle:
def __init__(self, left_upper: tuple, right_lower: tuple):
self.left_upper = left_upper
self.right_lower = right_lower
self.width = right_lower-left_upper
self.height = right_lower-left_upper

def area(self):
return self.width * self.height

def perimeter(self):
return self.width * 2 + self.height * 2

def move(self, x_change: int, y_change: int):
corner = self.left_upper
self.left_upper = (corner+x_change, corner+y_change)
corner = self.right_lower
self.right_lower = (corner+x_change, corner+y_change)``````

A new `Rectangle` is created with two tuples as arguments. These tuples contain the x and y coordinates of the upper left corner and the lower right corner. The constructor calculates the height and width of the rectangle based on these values.

The methods `area` and `perimeter` calculate the area and perimeter of the rectangle based on the height and width. The method `move` moves the rectangle by the x and y values given as arguments.

The rectanlge is represented in a coordinate system where the x coordinates increase from left to right, and the y coordinates increase from top to bottom. This is a common way of handling coordinates in programming because it is often easier and more natural to consider the top left corner of the computer screen as the point where x and y equal zero.

The following program tests the `Rectangle` class:

``````rectangle = Rectangle((1, 1), (4, 3))
print(rectangle.left_upper)
print(rectangle.right_lower)
print(rectangle.width)
print(rectangle.height)
print(rectangle.perimeter())
print(rectangle.area())

rectangle.move(3, 3)
print(rectangle.left_upper)
print(rectangle.right_lower)``````
Sample output

(1, 1) (4, 3) 3 2 10 6 (4, 4) (7, 6)

## Printing an object

When you have an object created from a class defined by yourself, the default reaction to calling the `print` command with that object as its argument is not very informative:

``````rectangle = Rectangle((1, 1), (4, 3))
print(rectangle)``````

The printout should look a bit like this:

Sample output

<main.Rectangle object at 0x000002D7BF148A90>

Obviously, we want more control over what is printed out. The easiest way to do this is to add a special `__str__` method to the class definition. Its purpose is to return a snapshot of the state of the object in string format. If the class definition contains a `__str__` method, the value returned by the method is the one printed out when the `print` command is executed.

So, let's add a `__str__` method definition to our `Rectangle` class:

``````class Rectangle:

# ...the rest of the class goes here the same as above...

# This method returns the state of the object in string format
def __str__(self):
return f"rectangle {self.left_upper} ... {self.right_lower}"``````

Now the `print` command produces something more user-friendly:

``````rectangle = Rectangle((1, 1), (4, 3))
print(rectangle)``````
Sample output

rectangle (1, 1) ... (4, 3)

The `__str__` method is perhaps more often used for formulating a string representation of the object with the `str` function, as seen in the following program:

``````rectangle = Rectangle((1, 1), (4, 3))
str_rep = str(rectangle)
print(str_rep)``````
Sample output

rectangle (1, 1) ... (4, 3)

There are many more special underscored methods which can be defined for classes. One rather similar to the `__str__` method is the `__repr__` method. Its purpose is to provide a technical representation of the state of the object. We will come across this method later.

The following class `TaskList` models a list of tasks:

``````class TaskList:
def __init__(self):

def get_next(self):
# The list method pop removes and returns the last item in a list
# Return the name of the task (the second item in the tuple)

The method `add_task` adds a new task to the list. Each task also has a priority attached, which is used for sorting the tasks. The method `get_next` removes and returns the task with the highest priority on the list. There is also the `number_of_tasks` method, which returns the number of tasks on the list, and finally the method `clear_tasks`, which clears the task list.

Within the object, the tasks are stored in a list. Each task is of a tuple containing the priority of the task and its name. The priority value is stored first, so that when the list is sorted, the task with the highest priority is the last item on the list. This is why we can then simply use the `pop` method to retrieve and remove the highest priority item.

Please have a look at the following program with the task list in action:

``````tasks = TaskList()
Sample output

3 exercise 2 3 date studying 1 0