Python - Object

An object is a data structure (state) with a set of functions specific to the object that give a different result depending on the state of the object (its attributes).

Introduction

Object-oriented programming was born with Simula in 1962, and its origin is to simulate objects and interactions between objects.

An object is a data structure (state) with a set of functions specific to the object that give a different result depending on the state of the object (its attributes).

Finally, remember that object-oriented programming is invaluable in creating user interfaces, real object simulation programs (such as 3D design), etc., but not in managing processes that process data such as artificial intelligence, web services, etc.

Object

Data structure

Every programming language is composed of simple data structures that allow you to represent anything, for example, a rectangle:

rectangle_length = 3
rectangle_width = 6

But if we have two rectangles, how can we represent them?

rectangle_a_length = 3
rectangle_a_width = 6
rectangle_b_length = 5
rectangle_b_width = 10

It’s not a good solution. What if we use a list?

rectangle_a = [3, 6]
rectangle_b = [5, 10]

In this way, we have the properties of a rectangle grouped in a single data structure under a single reference.

Functions

What if we want to calculate the area of a rectangle?

def rectangle_area(length, width):
return length * width
def test():
assert rectangle_area(3, 4) == 12

Let’s test if it works.

We create a new project:

Terminal window
uv init test
cd test
uv add --dev pytest

We install the pytest dependency in a development environment since we don’t need it in a production environment (—group dev).

We can now run pytest:

Terminal window
uv run pytest

But didn’t we say we were using a list to represent a rectangle?

def rectangle_area(rect):
return rect[0] * rect[1]
def test():
rectangle = [3, 6]
assert rectangle_area(rectangle) == 18

We have a function that calculates the area of a rectangle… only if the list it receives as a parameter represents a rectangle.

The truth is that in somewhat complex code many unforeseen things can happen, because you can say that a list of two values is a rectangle, but for Python it’s just a list.

def rectangle_area(rect):
return rect[0] * rect[1]
def test():
rectangle = [3, 6]
assert rectangle_area(rectangle) == 18
triangle = [3, 6] # base, height
assert rectangle_area(triangle) == 9

And it might seem obvious because it’s only a few lines, but after a hundred it’s not fun at all.

Classes

That’s why classes exist in Python:

class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def rectangle_area(rect):
return rect.length * rect.width
def test():
# a cube is not a rectangle
rectangle = Rectangle(3, 6)
assert rectangle_area(rectangle) == 18
triangle = [3, 6] # base, height
assert rectangle_area(triangle) == 9
Terminal window
$ pytest test.py
rect = [3, 6]
def rectangle_area(rect):
> return rect.length * rect.width
E AttributeError: 'list' object has no attribute 'length'

Python uses duck typing. Therefore, the rectangle_area function accepts any object that has two attributes named length and width.

class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
class Cube:
def __init__(self, length, width, height):
self.length = length
self.width = width
self.height = height
def rectangle_area(rect):
return rect.length * rect.width
def test():
# a cube is not a rectangle
rectangle = Rectangle(3, 6)
assert rectangle_area(rectangle) == 18
cube = Cube(3, 6, 2)
assert rectangle_area(cube) == 36

Since a cube has the attributes length and width, the rectangle_area function has no problem calculating its area:

When I see a bird that walks like a duck, swims like a duck, and sounds like a duck, I call that bird a duck.

Languages that force you to declare the parameter type do not have this problem.

We can modify the rectangle_area function to verify that the parameter is an instance of the Rectangle class with the isinstance function:

class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
class Cube:
def __init__(self, length, width, height):
self.length = length
self.width = width
self.height = height
def rectangle_area(rect):
assert isinstance(rect, Rectangle)
return rect.length * rect.width
def test():
rectangle = Rectangle(3, 6)
assert rectangle_area(rectangle) == 18
cube = Cube(3, 6, 2)
assert rectangle_area(cube) == 36

Now, instead of giving us erroneous data, the function generates a runtime error (in languages like Java the error would be at compile time).

Anyway, if the rectangle_area function should only work with objects of the Rectangle class, why not put it all together?

class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
def test():
rectangle = Rectangle(3, 6)
assert rectangle.length == 3
assert rectangle.area() == 18

By the way, in UML the Rectangle class is represented like this:

Rectangle

width

height

area()

To facilitate the creation of diagrams, you can use our guide to use the Mermaid plugin in VSCode.

Exercises

Below is a UML representation of classes, and you must write the corresponding Python code.

Equilateral triangle

Triangle

base

height

area()

Button

Let’s represent a button in a graphical interface:

Button

bool active

String message

String click()

And its Python coding would be this:

Window

One of the main characteristics of object-oriented programming is object composition.

For example, a window of a graphical interface can have a button:

Window

String title

int width

int height

resize(width: double, height: double)

Button

bool active

String message

String click()

And its Python coding would be this:

We can create an instance of Window and verify its operation:

def test():
window = Window("DAW", 600, 300, "Some content", Button(True, "hello"))
message = window.button.click()
assert message == "hello"
window.button.active = False
message = window.button.click()
assert message == ""
window.resize(200, 400)
assert window.width == 200
assert window.height == 400

Inheritance

TODO