Até aqui trabalhamos com funções e estruturas de dados separadas. A Orientação a Objetos (OO) propõe uma forma diferente de organizar o código: agrupando dados e comportamentos relacionados em uma única unidade chamada objeto. É o paradigma dominante no desenvolvimento de software moderno e Python o suporta de forma elegante e completa.
O que é uma Classe?
Uma classe é um molde — uma definição de como um objeto deve ser estruturado. O objeto é uma instância concreta criada a partir desse molde.
A analogia clássica: a planta de uma casa é a classe; cada casa construída a partir dela é um objeto.
class Cachorro:
"""Representa um cachorro."""
def __init__(self, nome, raca, idade):
self.nome = nome
self.raca = raca
self.idade = idade
def latir(self):
return f"{self.nome} diz: Au au!"
def apresentar(self):
return f"{self.nome} é um {self.raca} de {self.idade} ano(s)."
# Criando objetos (instâncias)
rex = Cachorro("Rex", "Pastor Alemão", 3)
bolinha = Cachorro("Bolinha", "Poodle", 1)
print(rex.latir()) # Rex diz: Au au!
print(bolinha.apresentar()) # Bolinha é um Poodle de 1 ano(s).
print(rex.nome) # Rex
O Método init
O método __init__ é o construtor da classe — executado automaticamente quando um objeto é criado. Ele inicializa os atributos do objeto.
O parâmetro self representa o próprio objeto sendo criado. É sempre o primeiro parâmetro de qualquer método de instância — Python o passa automaticamente.
class Produto:
def __init__(self, nome, preco, estoque=0):
self.nome = nome
self.preco = preco
self.estoque = estoque
def disponivel(self):
return self.estoque > 0
def aplicar_desconto(self, percentual):
self.preco *= (1 - percentual / 100)
def __repr__(self):
return f"Produto('{self.nome}', R${self.preco:.2f}, estoque={self.estoque})"
teclado = Produto("Teclado Mecânico", 450.00, 15)
mouse = Produto("Mouse Gamer", 189.90)
print(teclado.disponivel()) # True
print(mouse.disponivel()) # False
teclado.aplicar_desconto(10)
print(teclado) # Produto('Teclado Mecânico', R$405.00, estoque=15)
Atributos de Instância vs. Atributos de Classe
Atributos de instância pertencem a cada objeto individualmente. Atributos de classe são compartilhados por todas as instâncias:
class Funcionario:
empresa = "TechCorp" # atributo de classe — compartilhado
_contador = 0 # conta quantos funcionários foram criados
def __init__(self, nome, salario):
self.nome = nome # atributo de instância
self.salario = salario
Funcionario._contador += 1
@classmethod
def total_funcionarios(cls):
return cls._contador
@staticmethod
def categoria_salarial(salario):
if salario < 3000:
return "Júnior"
elif salario < 7000:
return "Pleno"
return "Sênior"
f1 = Funcionario("Ana", 4500)
f2 = Funcionario("Bruno", 8200)
f3 = Funcionario("Carla", 2800)
print(Funcionario.empresa) # TechCorp
print(Funcionario.total_funcionarios()) # 3
print(Funcionario.categoria_salarial(4500)) # Pleno
print(f1.empresa) # TechCorp — acessível via instância
Métodos Especiais (Dunder Methods)
Python usa métodos com duplo underscore para integrar objetos ao comportamento nativo da linguagem:
class Vetor:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
"""Representação técnica — usada no console."""
return f"Vetor({self.x}, {self.y})"
def __str__(self):
"""Representação legível — usada por print()."""
return f"({self.x}, {self.y})"
def __add__(self, outro):
"""Permite usar o operador +."""
return Vetor(self.x + outro.x, self.y + outro.y)
def __mul__(self, escalar):
"""Permite multiplicar por um número."""
return Vetor(self.x * escalar, self.y * escalar)
def __eq__(self, outro):
"""Permite comparar com ==."""
return self.x == outro.x and self.y == outro.y
def __len__(self):
"""Permite usar len()."""
return 2
def magnitude(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
v1 = Vetor(3, 4)
v2 = Vetor(1, 2)
print(v1) # (3, 4)
print(repr(v1)) # Vetor(3, 4)
print(v1 + v2) # (4, 6)
print(v1 * 3) # (9, 12)
print(v1 == Vetor(3, 4)) # True
print(v1.magnitude()) # 5.0
Encapsulamento
Encapsulamento é o princípio de proteger os dados internos de um objeto, expondo apenas o que é necessário. Python usa convenções de nomenclatura:
class ContaBancaria:
def __init__(self, titular, saldo_inicial=0):
self.titular = titular
self._saldo = saldo_inicial # _ indica "protegido" — convenção
self.__historico = [] # __ indica "privado" — name mangling
@property
def saldo(self):
"""Propriedade — acesso de leitura ao saldo."""
return self._saldo
def depositar(self, valor):
if valor <= 0:
raise ValueError("Valor de depósito deve ser positivo.")
self._saldo += valor
self.__historico.append(f"Depósito: +R${valor:.2f}")
def sacar(self, valor):
if valor <= 0:
raise ValueError("Valor de saque deve ser positivo.")
if valor > self._saldo:
raise ValueError("Saldo insuficiente.")
self._saldo -= valor
self.__historico.append(f"Saque: -R${valor:.2f}")
def extrato(self):
print(f"Titular: {self.titular}")
print(f"Saldo: R${self._saldo:.2f}")
print("Histórico:")
for item in self.__historico:
print(f" {item}")
conta = ContaBancaria("Ricardo", 1000)
conta.depositar(500)
conta.sacar(200)
conta.extrato()
print(conta.saldo) # 1300.0 — acesso via property
# conta._saldo = 999999 # possível, mas viola a convenção
# conta.__historico # AttributeError — name mangling protege
Exemplo Completo: Sistema de Biblioteca
class Livro:
def __init__(self, titulo, autor, isbn):
self.titulo = titulo
self.autor = autor
self.isbn = isbn
self._emprestado = False
@property
def disponivel(self):
return not self._emprestado
def __repr__(self):
status = "disponível" if self.disponivel else "emprestado"
return f"'{self.titulo}' por {self.autor} [{status}]"
class Biblioteca:
def __init__(self, nome):
self.nome = nome
self._acervo = {}
def adicionar(self, livro):
self._acervo[livro.isbn] = livro
print(f"Livro adicionado: {livro.titulo}")
def emprestar(self, isbn, usuario):
livro = self._acervo.get(isbn)
if not livro:
print("Livro não encontrado.")
return
if not livro.disponivel:
print(f"'{livro.titulo}' já está emprestado.")
return
livro._emprestado = True
print(f"'{livro.titulo}' emprestado para {usuario}.")
def devolver(self, isbn):
livro = self._acervo.get(isbn)
if livro and not livro.disponivel:
livro._emprestado = False
print(f"'{livro.titulo}' devolvido com sucesso.")
def listar(self):
print(f"\nAcervo — {self.nome}")
print("-" * 40)
for livro in self._acervo.values():
print(f" {livro}")
biblioteca = Biblioteca("Biblioteca Central")
l1 = Livro("Python Fluente", "Luciano Ramalho", "978-1492056355")
l2 = Livro("Python Crash Course", "Eric Matthes", "978-1593279288")
l3 = Livro("Grokking Algorithms", "Aditya Bhargava", "978-1617292231")
biblioteca.adicionar(l1)
biblioteca.adicionar(l2)
biblioteca.adicionar(l3)
biblioteca.emprestar("978-1492056355", "Ana")
biblioteca.emprestar("978-1492056355", "Bruno") # já emprestado
biblioteca.listar()
biblioteca.devolver("978-1492056355")
biblioteca.listar()
Resumo
- Classes são moldes; objetos são instâncias concretas criadas a partir delas
__init__inicializa os atributos do objeto;selfreferencia a instância atual- Atributos de classe são compartilhados; atributos de instância são individuais
@classmethodrecebe a classe como primeiro argumento;@staticmethodnão recebe nenhum- Métodos dunder integram objetos ao comportamento nativo do Python (
+,==,str(),len()) - Encapsulamento usa
_para protegido e__para privado por convenção @propertytransforma um método em atributo de leitura, controlando o acesso
Referências e Leituras Complementares
- Classes — documentação oficial — https://docs.python.org/3/tutorial/classes.html
- Modelo de dados e métodos especiais — https://docs.python.org/3/reference/datamodel.html
- Property e descritores — https://docs.python.org/3/howto/descriptor.html
- PEP 8 — convenções para classes — https://peps.python.org/pep-0008/#class-names
- RAMALHO, Luciano. Fluent Python. 2. ed. O'Reilly Media, 2022. Cap. 1 e 11 — modelo de dados e interfaces de objetos Python.
- MATTHES, Eric. Python Crash Course. 3. ed. No Starch Press, 2023. Cap. 9 — introdução prática a classes.
- PHILLIPS, Dusty; LOTT, Steven F. Python Object-Oriented Programming. 4. ed. Packt, 2021. Cap. 1–3 — OO em Python do básico ao avançado.