Classes e Objetos

Nas últimas lições, organizamos código usando funções — uma abordagem "procedural" focada em "o que fazer." Programação Orientada a Objetos (POO) foca em "quem faz" — empacotando dados e os métodos que operam nesses dados em um objeto. Tudo em Python é um objeto; entender POO é um passo fundamental.


1. O que são Classes e Objetos

Uma classe é um projeto; um objeto é uma instância concreta criada a partir desse projeto.

PYTHON
# Define uma classe "Cachorro" — o projeto
class Cachorro:
    pass

# Cria objetos "Cachorro" específicos a partir do projeto
my_dog = Cachorro()
print(type(my_dog))         # <class '__main__.Cachorro'>
print(my_dog)               # <__main__.Cachorro object at 0x...>

Assim como "cachorro" como raça é um conceito (classe), o cachorro específico chamado Rex que você possui é uma instância (objeto). Uma classe pode criar inúmeros objetos.

💡 Analogia do mundo real: Uma classe é o "projeto," um objeto é a "coisa física construída a partir do projeto." Um projeto pode produzir muitas coisas físicas, cada uma com seu próprio estado (ex.: diferentes cachorros têm diferentes nomes e idades).


2. __init__: O Método Construtor

Ao criar um objeto, o método __init__ é automaticamente chamado para inicializar os atributos do objeto:

PYTHON
class Cachorro:
    """Classe Cachorro"""
    def __init__(self, nome, idade):
        self.nome = nome    # Atributo de instância
        self.idade = idade

# Passa parâmetros ao criar objetos
dog1 = Cachorro("Rex", 3)
dog2 = Cachorro("Bolinha", 1)

print(dog1.nome)            # Rex
print(dog2.nome)            # Bolinha
print(dog1.idade)           # 3
⚠️ Observação: __init__ não é o construtor — o objeto é criado antes de ser chamado. O construtor real é __new__, mas você quase nunca precisa tocá-lo. O primeiro parâmetro de __init__ é sempre self, representando a instância atual do objeto.

Exemplo: Definindo uma Classe Aluno (Dificuldade ⭐)

PYTHON
class Aluno:
    """Classe Aluno"""
    def __init__(self, nome, matricula, nota):
        self.nome = nome
        self.matricula = matricula
        self.nota = nota

    def apresentar(self):
        """Auto-apresentação"""
        return f"Meu nome é {self.nome}, matrícula: {self.matricula}, nota: {self.nota}."
    
    def is_aprovado(self):
        """Verifica se está aprovado"""
        return self.nota >= 60

# Cria objetos aluno
s1 = Aluno("João Silva", "2024001", 85)
s2 = Aluno("Maria Souza", "2024002", 45)

print(s1.apresentar())       # Meu nome é João Silva, matrícula: 2024001, nota: 85.
print(s2.apresentar())       # Meu nome é Maria Souza, matrícula: 2024002, nota: 45.
print(f"João está aprovado? {s1.is_aprovado()}")   # True
print(f"Maria está aprovada? {s2.is_aprovado()}")   # False
▶ Experimente

3. Métodos: Comportamentos dos Objetos

Funções definidas dentro de uma classe são chamadas de métodos. Seu primeiro parâmetro é sempre self — representando o próprio objeto que chama o método.

PYTHON
class Calculadora:
    """Calculadora simples"""
    def __init__(self):
        self.resultado = 0
    
    def add(self, valor):
        self.resultado += valor
        return self
    
    def subtrair(self, valor):
        self.resultado -= valor
        return self
    
    def mostrar(self):
        return self.resultado

calc = Calculadora()
calc.add(10)
calc.subtrair(3)
print(calc.mostrar())                  # 7

# Encadeamento de métodos — cada método retorna self
resultado = Calculadora().add(10).subtrair(3).add(5).mostrar()
print(resultado)                       # 12
💡 Truque de encadeamento de métodos: Métodos que retornam self podem ser encadeados. Muitas bibliotecas Python (como pandas) usam este padrão extensivamente.


4. Atributos de Instância vs. Atributos de Classe

Atributos podem ser definidos na instância (únicos para cada objeto) ou na classe (compartilhados por todos os objetos):

PYTHON
class Aluno:
    # Atributo de classe — compartilhado por todos os alunos
    escola = "Escola Primeiro Grau"
    total_count = 0
    
    def __init__(self, nome):
        # Atributo de instância — único para cada aluno
        self.nome = nome
        Aluno.total_count += 1

s1 = Aluno("João Silva")
s2 = Aluno("Maria Souza")

# Atributos de classe acessados via nome da classe
print(Aluno.escola)       # Escola Primeiro Grau
print(Aluno.total_count)  # 2

# Também acessíveis via instâncias (se não houver atributo de instância com o mesmo nome)
print(s1.escola)            # Escola Primeiro Grau
print(s2.escola)            # Escola Primeiro Grau

# Modificar um atributo de classe afeta todas as instâncias
Aluno.escola = "Escola Segundo Grau"
print(s1.escola)            # Escola Segundo Grau

Exemplo: Conta Bancária (Dificuldade ⭐⭐)

PYTHON
class ContaBancaria:
    """Conta bancária"""
    nome_banco = "Python Bank"          # Atributo de classe
    taxa_juros = 0.003                  # Taxa de juros anual 0.3%
    
    def __init__(self, titular, saldo=0):
        self.titular = titular            # Atributo de instância
        self.saldo = saldo                # Atributo de instância
    
    def depositar(self, valor):
        self.saldo += valor
        return f"Depositado {valor}, saldo: {self.saldo}"
    
    def sacar(self, valor):
        if valor > self.saldo:
            return "Saldo insuficiente!"
        self.saldo -= valor
        return f"Sacado {valor}, saldo: {self.saldo}"
    
    def juros_anuais(self):
        """Calcula juros anuais"""
        juros = self.saldo * ContaBancaria.taxa_juros
        return f"Juros anuais: {juros:.2f}"

acc1 = ContaBancaria("João Silva", 10000)
acc2 = ContaBancaria("Maria Souza", 5000)

print(acc1.depositar(5000))           # Depositado 5000, saldo: 15000
print(acc2.sacar(2000))              # Sacado 2000, saldo: 3000
print(acc1.juros_anuais())           # Juros anuais: 4.50

# Todas as contas compartilham a mesma taxa de juros
print(acc1.taxa_juros)           # 0.003
print(acc2.taxa_juros)           # 0.003
▶ Experimente

Casos de Uso Comuns


❓ Perguntas Frequentes

P: Qual a diferença entre um método e uma função? R: Um método é uma função definida dentro de uma classe, com self como primeiro parâmetro. Funções podem existir independentemente; métodos devem ser chamados via um objeto ou classe. Essencialmente, métodos são funções onde Python passa automaticamente o chamador como primeiro argumento. P: O self pode ser renomeado para outra coisa? R: Tecnicamente sim, mas nunca mude isso. self é uma regra rígida na comunidade Python. Mudar o nome funcionaria, mas qualquer um lendo seu código (incluindo você no futuro) acharia estranho. Sempre use self. P: Quando devo usar uma classe vs. uma função? R: Se você precisa manter estado (dados), use uma classe — como uma conta bancária rastreando saldo. Se você está fazendo um cálculo único (entrada -> processamento -> saída), use uma função — como conversão de temperatura. Uma regra simples: se você precisa apenas de 1-2 funções, funções bastam; se múltiplas funções relacionadas compartilham os mesmos dados, use uma classe.


📖 Resumo


📝 Atividades

  1. Iniciante (Dificuldade ⭐): Defina uma classe Carro com atributos marca, cor e velocidade. Inclua um método acelerar(valor) para aumentar a velocidade e um método mostrar_velocidade() para exibir a velocidade atual.

  2. Intermediário (Dificuldade ⭐⭐): Defina uma classe Aluno. Use um atributo de classe para rastrear o número total de alunos, e atributos de instância para registrar o nome e a nota de cada aluno. Implemente um método get_grade() que retorna uma letra (A, B, C, D).

  3. Avançado (Dificuldade ⭐⭐⭐): Defina uma classe Biblioteca e uma classe Livro. Livro tem título, autor e status de empréstimo. Biblioteca tem quatro métodos: adicionar_livro(livro), emprestar(titulo), devolver(titulo) e mostrar_livros().

100%