Skip to content

    Chapter 26, Intermediate

    Classes and objects

    A class groups data and behavior that belong together. I show the essentials and, mainly, when **not** to create one.

    Code for this chapter: intermediario/cap26_classes.py

    The class is the mold, the object is the thing

    A class describes what objects of a type are like and what they do. Each object created from it is an instance, with its own data:

    intermediario/cap26_classes.pylinhas 10 a 22
    class Cachorro:
        def __init__(self, nome, idade):
            self.nome = nome
            self.idade = idade
    
        def latir(self):
            return f"{self.nome} diz: au au"
    
    
    rex = Cachorro("Rex", 3)
    mel = Cachorro("Mel", 5)
    print(rex.nome, mel.nome)
    print(rex.latir())
    
    Output
    Rex Mel
    Rex diz: au au
    

    The __init__ is the initializer: it runs right after the object is created and prepares the initial data. (The one who actually creates the object is the __new__, which you rarely write.) The word self is the object itself, and it is through it that each method knows whose data it is.

    What self really is

    A call like rex.latir() is just sugar for Cachorro.latir(rex). Python passes the object as the first argument. Seeing this once unravels the mystery of self:

    intermediario/cap26_classes.pylinhas 27 a 28
    print(Cachorro.latir(mel))
    print(type(rex).__name__, isinstance(rex, Cachorro))
    
    Output
    Mel diz: au au
    Cachorro True
    

    A representation that helps with debugging

    Printing an object without __repr__ shows something like <Ponto object at 0x...>, which helps no one. Define the __repr__ in almost every class: it should show the data and preferably look like the code that recreates the object:

    intermediario/cap26_classes.pylinhas 33 a 44
    class Ponto:
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
        def __repr__(self):
            return f"Ponto(x={self.x}, y={self.y})"
    
    
    p = Ponto(1, 2)
    print(p)
    print(vars(p))
    
    Output
    Ponto(x=1, y=2)
    {'x': 1, 'y': 2}
    

    The vars() shows the object's attributes like a dictionary. This is how Python stores the data of each instance.

    When to create a class

    I create a class when I have state and behavior that go together, like a bank account, which has a balance and knows how to deposit and withdraw. If the class only has methods and no data, it probably should be a function (or a module). A Calculadora with somar(a, b) and subtrair(a, b) without any attributes is the way to write Java inside Python.

    Exercise 1

    A bank account

    Create ContaBancaria with holder and initial balance (default 0), and the methods depositar and sacar. Invalid values and withdrawals above the balance raise ValueError.