🌗 ← Índice
Data: 30/07/2026
Nível: Intermediário (Início do Mês 2 — POO)
Até aqui tu escreveu código procedural: funções que recebem dados e devolvem resultados. Hoje começa o Mês 2 com Programação Orientada a Objetos (POO) — o jeito de organizar código que o Python usa por dentro em tudo: listas, arquivos, requests. Entender classes é entender a linguagem.
Uma classe é uma forma (molde). Um objeto é o bolo que você faz com ela.
# CLASSE = molde (define o que TODO bolo tem e faz)
class Bolo:
def __init__(self, sabor, cobertura):
self.sabor = sabor
self.cobertura = cobertura
def descrever(self):
return f"Bolo de {self.sabor} com {self.cobertura}"
# OBJETOS = bolos concretos criados do molde
bolo1 = Bolo("chocolate", "brigadeiro")
bolo2 = Bolo("cenoura", "chocolate")
print(bolo1.descrever()) # Bolo de chocolate com brigadeiro
print(bolo2.descrever()) # Bolo de cenoura com chocolate
Mesmo molde, objetos diferentes — cada um guarda os seus valores. Isso é POO na essência.
`__init__` é o construtor: roda automaticamente quando você cria um objeto. É ali que você "inicializa" o objeto.
`self` é a referência ao próprio objeto. Ele é sempre o primeiro parâmetro de todo método — é assim que o método sabe em qual objeto está mexendo.
class Usuario:
def __init__(self, nome, email):
self.nome = nome # atributo de instância
self.email = email # atributo de instância
self.logado = False # valor padrão
def login(self):
self.logado = True
print(f"{self.nome} entrou!")
def logout(self):
self.logado = False
print(f"{self.nome} saiu.")
u = Usuario("Hugo", "hugo@hbstudio.com.br")
print(u.nome) # Hugo
print(u.logado) # False
u.login() # Hugo entrou!
print(u.logado) # True
💡 Sem `self` o método não sabe QUAL usuário está logando. Com `self`, cada objeto mexe nos próprios dados.
Instância (com `self.`): cada objeto tem o seu — valor muda de um pra outro.
Classe (sem `self.`): é compartilhado por todos os objetos — igual a um cartaz na parede da fábrica.
class Contador:
total_instancias = 0 # atributo de CLASSE (compartilhado)
def __init__(self, nome):
self.nome = nome # atributo de INSTÂNCIA (individual)
Contador.total_instancias += 1
c1 = Contador("primeiro")
c2 = Contador("segundo")
print(Contador.total_instancias) # 2 (a classe conta todos)
print(c1.nome) # primeiro
print(c2.nome) # segundo
Método é só uma função que vive dentro da classe — pode receber parâmetros e retornar valores normalmente.
class Calculadora:
def somar(self, a, b):
return a + b
def dividir(self, a, b):
if b == 0:
return "Erro: divisão por zero"
return a / b
calc = Calculadora()
print(calc.somar(5, 3)) # 8
print(calc.dividir(10, 2)) # 5.0
print(calc.dividir(1, 0)) # Erro: divisão por zero
Em Python não existe `private` de verdade (isso é Java). Usa-se convenção: atributos com `_` na frente são "internos" — ninguém deve mexer de fora.
class ContaBancaria:
def __init__(self, titular, saldo):
self.titular = titular
self._saldo = saldo # "privado" por convenção
def depositar(self, valor):
if valor > 0:
self._saldo += valor
def sacar(self, valor):
if valor > 0 and valor <= self._saldo:
self._saldo -= valor
return True
return False
def ver_saldo(self):
return self._saldo
conta = ContaBancaria("Hugo", 1000)
conta.depositar(500)
print(conta.ver_saldo()) # 1500
print(conta.sacar(2000)) # False (não tem saldo)
print(conta.ver_saldo()) # 1500
💡 Acessar `conta._saldo` direto funciona, mas é quebra de contrato. A regra: métodos mudam dados, atributos privados não são tocados de fora.
Crie uma classe `Banco` que gerencia várias contas:
Teste: crie 2 contas, transfira R$ 200 da conta 1 pra conta 2 e mostre os saldos e o total.
class Livro:
def __init__(self, titulo, autor, paginas):
self.titulo = titulo
self.autor = autor
self.paginas = paginas
def resumo(self):
return f"{self.titulo} por {self.autor} — {self.paginas} páginas"
livro1 = Livro("O Alquimista", "Paulo Coelho", 208)
livro2 = Livro("Clean Code", "Robert C. Martin", 431)
print(livro1.resumo())
print(livro2.resumo())
class Cachorro:
especie = "Canis familiaris" # atributo de classe
def __init__(self, nome, idade):
self.nome = nome
self.idade = idade
def latir(self):
print(f"{self.nome}: Au au!")
rex = Cachorro("Rex", 3)
rex.idade = 4 # atualiza a idade do objeto
rex.latir() # Rex: Au au!
print(rex.especie) # Canis familiaris
print(Cachorro.especie) # Canis familiaris
class Conta:
def __init__(self, numero, titular, saldo):
self.numero = numero
self.titular = titular
self._saldo = saldo
def depositar(self, valor):
if valor > 0:
self._saldo += valor
def sacar(self, valor):
if valor > 0 and valor <= self._saldo:
self._saldo -= valor
return True
return False
def ver_saldo(self):
return self._saldo
class Banco:
def __init__(self, nome):
self.nome = nome
self.contas = []
def criar_conta(self, numero, titular, saldo_inicial):
conta = Conta(numero, titular, saldo_inicial)
self.contas.append(conta)
return conta
def transferir(self, origem, destino, valor):
if origem.sacar(valor):
destino.depositar(valor)
return True
return False
def total_banco(self):
return sum(c.ver_saldo() for c in self.contas)
banco = Banco("HB Bank")
c1 = banco.criar_conta(1, "Hugo", 1000)
c2 = banco.criar_conta(2, "Maria", 500)
print(banco.transferir(c1, c2, 200)) # True
print(c1.ver_saldo()) # 800
print(c2.ver_saldo()) # 700
print(banco.total_banco()) # 1500
| Conceito | O que é | Exemplo |
|---|---|---|
| Classe | Molde / definição | class Conta: |
| Objeto | Instância criada do molde | conta = Conta(1, "Hugo", 1000) |
| __init__ | Construtor, roda na criação | def __init__(self, ...): |
| self | Referência ao próprio objeto | self._saldo = saldo |
| Atributo | Dado do objeto | self.titular |
| Método | Função do objeto | def sacar(self, valor) |
| Encapsulamento | Convenção `_` = interno | self._saldo |
Próxima aula: Herança e Polimorfismo — `super()`, reutilizar e especializar classes. 🧬