Programação Orientada a Objetos (POO) é um dos paradigmas mais poderosos da programação moderna. Em Python, POO permite criar código mais organizado, reutilizável e próximo do mundo real. Neste guia completo, você aprenderá desde os conceitos fundamentais até técnicas avançadas de POO em Python.
🎯 O Que É Programação Orientada a Objetos?
POO é um paradigma de programação que organiza o código em torno de objetos - estruturas que combinam dados (atributos) e comportamentos (métodos). Imagine cada objeto como uma nave espacial no Universo Python: cada nave tem suas características (velocidade, combustível) e ações que pode executar (acelerar, pousar).
Ao contrário da programação procedural que foca em funções isoladas, POO agrupa dados e funcionalidades relacionadas em entidades coesas.
📝 Classes e Objetos: A Base da POO
O Que São Classes?
Uma classe é como um projeto de nave espacial - define a estrutura e comportamento que todas as naves daquele tipo terão. É um molde, um blueprint.
# Definindo uma classe simples
class NaveEspacial:
"""Representa uma nave espacial no Universo Python"""
pass
# Criar um objeto (instância) da classe
enterprise = NaveEspacial()
millennium_falcon = NaveEspacial()
print(type(enterprise)) # <class '__main__.NaveEspacial'>
Atributos: As Características
Atributos são as propriedades que cada objeto possui. Em Python, definimos atributos no método especial __init__(), que funciona como um construtor:
class NaveEspacial:
def __init__(self, nome, velocidade_maxima, tripulacao):
"""Inicializa uma nova nave espacial"""
self.nome = nome
self.velocidade_maxima = velocidade_maxima
self.tripulacao = tripulacao
self.combustivel = 100 # Todas as naves começam com tanque cheio
# Criar objetos com atributos específicos
enterprise = NaveEspacial("USS Enterprise", 9.9, 430)
millennium = NaveEspacial("Millennium Falcon", 12.0, 4)
print(f"{enterprise.nome} - Velocidade: {enterprise.velocidade_maxima}")
print(f"{millennium.nome} - Tripulação: {millennium.tripulacao}")
O parâmetro self é uma referência ao próprio objeto e sempre é o primeiro parâmetro de métodos de instância.
Métodos: Os Comportamentos
Métodos são funções definidas dentro de uma classe que determinam o que os objetos podem fazer:
class NaveEspacial:
def __init__(self, nome, velocidade_maxima):
self.nome = nome
self.velocidade_maxima = velocidade_maxima
self.combustivel = 100
self.velocidade_atual = 0
def acelerar(self, incremento):
"""Aumenta a velocidade da nave"""
if self.combustivel > 0:
nova_velocidade = self.velocidade_atual + incremento
if nova_velocidade <= self.velocidade_maxima:
self.velocidade_atual = nova_velocidade
self.combustivel -= incremento * 0.5
print(f"🚀 {self.nome} acelerou para {self.velocidade_atual}")
else:
print(f"⚠️ Velocidade máxima atingida!")
else:
print(f"⛽ Sem combustível!")
def status(self):
"""Mostra o status atual da nave"""
print(f"\n{'='*50}")
print(f"🛸 NAVE: {self.nome}")
print(f"⚡ Velocidade: {self.velocidade_atual}/{self.velocidade_maxima}")
print(f"⛽ Combustível: {self.combustivel:.1f}%")
print(f"{'='*50}\n")
# Usando os métodos
enterprise = NaveEspacial("USS Enterprise", 9.9)
enterprise.acelerar(3)
enterprise.acelerar(4)
enterprise.status()
🎨 Encapsulamento: Protegendo os Dados
Encapsulamento é o conceito de "esconder" detalhes internos de implementação e expor apenas o necessário. Em Python, usamos convenções de nomenclatura:
class ContaBancaria:
def __init__(self, titular, saldo_inicial=0):
self.titular = titular # Público
self._numero = self._gerar_numero() # Protegido (convenção)
self.__saldo = saldo_inicial # Privado (name mangling)
def _gerar_numero(self):
"""Método protegido - convenção: não usar fora da classe"""
import random
return random.randint(10000, 99999)
def depositar(self, valor):
"""Método público para depositar"""
if valor > 0:
self.__saldo += valor
print(f"✅ Depósito de R$ {valor:.2f} realizado")
else:
print("❌ Valor inválido!")
def sacar(self, valor):
"""Método público para sacar"""
if 0 < valor <= self.__saldo:
self.__saldo -= valor
print(f"💰 Saque de R$ {valor:.2f} realizado")
else:
print("❌ Saldo insuficiente!")
def get_saldo(self):
"""Getter para acessar saldo privado"""
return self.__saldo
def extrato(self):
"""Mostra informações da conta"""
print(f"\n👤 Titular: {self.titular}")
print(f"🔢 Conta: {self._numero}")
print(f"💵 Saldo: R$ {self.__saldo:.2f}\n")
# Usando encapsulamento
conta = ContaBancaria("Ana Silva", 1000)
conta.depositar(500)
conta.sacar(200)
conta.extrato()
# Tentar acessar diretamente (não recomendado)
# print(conta.__saldo) # AttributeError
print(conta.get_saldo()) # Forma correta: 1300.0
Properties: Getters e Setters Pythônicos
class Temperatura:
def __init__(self, celsius=0):
self._celsius = celsius
@property
def celsius(self):
"""Getter para celsius"""
return self._celsius
@celsius.setter
def celsius(self, valor):
"""Setter com validação"""
if valor < -273.15:
raise ValueError("Temperatura abaixo do zero absoluto!")
self._celsius = valor
@property
def fahrenheit(self):
"""Propriedade calculada"""
return (self._celsius * 9/5) + 32
@fahrenheit.setter
def fahrenheit(self, valor):
self._celsius = (valor - 32) * 5/9
# Usando properties
temp = Temperatura(25)
print(f"Celsius: {temp.celsius}°C") # 25
print(f"Fahrenheit: {temp.fahrenheit}°F") # 77.0
temp.fahrenheit = 86
print(f"Nova temperatura: {temp.celsius}°C") # 30
🧬 Herança: Reutilizando Código
Herança permite criar classes "filhas" que herdam atributos e métodos de classes "pais", promovendo reutilização de código:
class Veiculo:
"""Classe base (pai)"""
def __init__(self, marca, modelo, ano):
self.marca = marca
self.modelo = modelo
self.ano = ano
self.ligado = False
def ligar(self):
if not self.ligado:
self.ligado = True
print(f"🔑 {self.marca} {self.modelo} ligado")
else:
print("⚠️ Já está ligado")
def desligar(self):
if self.ligado:
self.ligado = False
print(f"🔒 {self.marca} {self.modelo} desligado")
class Carro(Veiculo):
"""Classe filha de Veículo"""
def __init__(self, marca, modelo, ano, portas):
super().__init__(marca, modelo, ano) # Chama construtor do pai
self.portas = portas
def abrir_porta_malas(self):
print(f"🚗 Porta-malas do {self.modelo} aberto")
class Moto(Veiculo):
"""Outra classe filha"""
def __init__(self, marca, modelo, ano, cilindradas):
super().__init__(marca, modelo, ano)
self.cilindradas = cilindradas
def empinar(self):
if self.ligado:
print(f"🏍️ {self.modelo} empinando!")
else:
print("❌ Ligue a moto primeiro!")
# Usando herança
carro = Carro("Toyota", "Corolla", 2024, 4)
moto = Moto("Honda", "CB 500", 2024, 500)
carro.ligar() # Método herdado
carro.abrir_porta_malas() # Método próprio
moto.ligar() # Método herdado
moto.empinar() # Método próprio
Herança Múltipla
Python suporta herança múltipla, onde uma classe pode herdar de múltiplas classes pais:
class Voador:
def voar(self):
print("🦅 Voando...")
class Aquatico:
def nadar(self):
print("🐟 Nadando...")
class PatoRobo(Voador, Aquatico):
"""Herda de Voador e Aquatico"""
def quack(self):
print("🦆 Quack!")
pato = PatoRobo()
pato.voar() # De Voador
pato.nadar() # De Aquatico
pato.quack() # Próprio
🔄 Polimorfismo: Múltiplas Formas
Polimorfismo permite que objetos de classes diferentes respondam ao mesmo método de formas específicas:
class Animal:
def __init__(self, nome):
self.nome = nome
def fazer_som(self):
pass # Método abstrato
class Cachorro(Animal):
def fazer_som(self):
return f"{self.nome} faz: Au au! 🐕"
class Gato(Animal):
def fazer_som(self):
return f"{self.nome} faz: Miau! 🐱"
class Vaca(Animal):
def fazer_som(self):
return f"{self.nome} faz: Muuu! 🐄"
# Polimorfismo em ação
animais = [
Cachorro("Rex"),
Gato("Mimi"),
Vaca("Mimosa")
]
# Mesmo método, comportamentos diferentes
for animal in animais:
print(animal.fazer_som())
🎭 Métodos Especiais (Dunder Methods)
Python tem métodos especiais que começam e terminam com __ (double underscore/dunder) que permitem personalizar o comportamento dos objetos:
class Livro:
def __init__(self, titulo, autor, paginas):
self.titulo = titulo
self.autor = autor
self.paginas = paginas
def __str__(self):
"""Representação em string legível"""
return f"'{self.titulo}' por {self.autor}"
def __repr__(self):
"""Representação em string técnica"""
return f"Livro(titulo='{self.titulo}', autor='{self.autor}', paginas={self.paginas})"
def __len__(self):
"""Permite usar len() no objeto"""
return self.paginas
def __eq__(self, other):
"""Permite comparação com =="""
return self.titulo == other.titulo and self.autor == other.autor
def __lt__(self, other):
"""Permite comparação com <"""
return self.paginas < other.paginas
# Usando métodos especiais
livro1 = Livro("Python Fluente", "Luciano Ramalho", 800)
livro2 = Livro("Python Cookbook", "David Beazley", 600)
print(livro1) # Usa __str__
print(repr(livro2)) # Usa __repr__
print(f"Páginas: {len(livro1)}") # Usa __len__
print(livro1 == livro2) # Usa __eq__
print(livro1 > livro2) # Usa __lt__
Métodos Especiais Mais Comuns
| Método | Descrição | Uso |
|---|---|---|
__init__ |
Construtor | Inicializar objeto |
__str__ |
String legível | str(obj), print(obj) |
__repr__ |
Representação técnica | repr(obj) |
__len__ |
Tamanho | len(obj) |
__eq__ |
Igualdade | obj1 == obj2 |
__lt__ |
Menor que | obj1 < obj2 |
__add__ |
Adição | obj1 + obj2 |
__getitem__ |
Acesso por índice | obj[key] |
🏗️ Métodos de Classe e Estáticos
class Pessoa:
populacao = 0 # Atributo de classe (compartilhado)
def __init__(self, nome, idade):
self.nome = nome # Atributo de instância
self.idade = idade
Pessoa.populacao += 1
@classmethod
def criar_de_nascimento(cls, nome, ano_nascimento):
"""Método de classe - factory method"""
from datetime import datetime
idade = datetime.now().year - ano_nascimento
return cls(nome, idade)
@staticmethod
def eh_maior_idade(idade):
"""Método estático - não acessa self nem cls"""
return idade >= 18
def apresentar(self):
"""Método de instância normal"""
maioridade = "maior" if self.eh_maior_idade(self.idade) else "menor"
return f"{self.nome}, {self.idade} anos ({maioridade} de idade)"
# Usando diferentes tipos de métodos
pessoa1 = Pessoa("Ana", 25)
pessoa2 = Pessoa.criar_de_nascimento("Carlos", 2000) # Método de classe
print(pessoa1.apresentar())
print(pessoa2.apresentar())
print(f"População total: {Pessoa.populacao}")
print(f"É maior de idade? {Pessoa.eh_maior_idade(16)}") # Método estático
🎯 Projeto Prático: Sistema de RPG Espacial
Vamos criar um sistema completo de RPG usando POO, combinando conceitos de listas e dicionários:
from abc import ABC, abstractmethod
import random
class Personagem(ABC):
"""Classe abstrata base para personagens"""
def __init__(self, nome, vida, ataque, defesa):
self.nome = nome
self._vida_maxima = vida
self._vida_atual = vida
self.ataque = ataque
self.defesa = defesa
self.inventario = []
@property
def vida_atual(self):
return self._vida_atual
@vida_atual.setter
def vida_atual(self, valor):
self._vida_atual = max(0, min(valor, self._vida_maxima))
@abstractmethod
def habilidade_especial(self, alvo):
"""Cada classe tem sua habilidade única"""
pass
def atacar(self, alvo):
"""Ataque básico"""
dano_base = self.ataque
dano_real = max(1, dano_base - alvo.defesa + random.randint(-2, 2))
alvo.vida_atual -= dano_real
print(f"⚔️ {self.nome} atacou {alvo.nome}")
print(f" 💥 Dano: {dano_real}")
print(f" ❤️ {alvo.nome}: {alvo.vida_atual}/{alvo._vida_maxima} HP")
return dano_real
def curar(self, quantidade):
"""Recupera vida"""
cura = min(quantidade, self._vida_maxima - self._vida_atual)
self.vida_atual += cura
print(f"💚 {self.nome} recuperou {cura} HP")
def adicionar_item(self, item):
"""Adiciona item ao inventário"""
self.inventario.append(item)
print(f"📦 {self.nome} obteve: {item}")
def status(self):
"""Mostra status do personagem"""
barra_vida = "█" * int((self.vida_atual / self._vida_maxima) * 20)
print(f"\n{'='*50}")
print(f"👤 {self.nome} ({self.__class__.__name__})")
print(f"❤️ HP: [{barra_vida:<20}] {self.vida_atual}/{self._vida_maxima}")
print(f"⚔️ ATK: {self.ataque} | 🛡️ DEF: {self.defesa}")
print(f"🎒 Items: {len(self.inventario)}")
print(f"{'='*50}\n")
class Guerreiro(Personagem):
"""Classe de combate corpo a corpo"""
def __init__(self, nome):
super().__init__(nome, vida=150, ataque=20, defesa=15)
def habilidade_especial(self, alvo):
"""Golpe Devastador - dano crítico"""
dano = self.ataque * 2
alvo.vida_atual -= dano
print(f"💥 {self.nome} usou GOLPE DEVASTADOR!")
print(f" ⚡ Dano crítico: {dano}")
class Mago(Personagem):
"""Classe de magia"""
def __init__(self, nome):
super().__init__(nome, vida=80, ataque=30, defesa=5)
self.mana = 100
def habilidade_especial(self, alvo):
"""Bola de Fogo - ataque mágico"""
if self.mana >= 30:
self.mana -= 30
dano = self.ataque + random.randint(10, 20)
alvo.vida_atual -= dano
print(f"🔥 {self.nome} lançou BOLA DE FOGO!")
print(f" 🎯 Dano: {dano} | 💙 Mana: {self.mana}/100")
else:
print(f"❌ {self.nome} sem mana suficiente!")
class Arqueiro(Personagem):
"""Classe de ataque à distância"""
def __init__(self, nome):
super().__init__(nome, vida=100, ataque=25, defesa=10)
self.flechas = 20
def habilidade_especial(self, alvo):
"""Flecha Perfurante - ignora defesa"""
if self.flechas >= 3:
self.flechas -= 3
dano = self.ataque + 15
alvo.vida_atual -= dano
print(f"🏹 {self.nome} disparou FLECHA PERFURANTE!")
print(f" 🎯 Dano puro: {dano} | 🏹 Flechas: {self.flechas}/20")
else:
print(f"❌ {self.nome} sem flechas suficientes!")
class Combate:
"""Gerencia combates entre personagens"""
@staticmethod
def batalha(personagem1, personagem2):
"""Simula uma batalha"""
print(f"\n⚔️ ===== BATALHA INICIADA ===== ⚔️")
print(f"{personagem1.nome} VS {personagem2.nome}\n")
turno = 1
atacante = personagem1
defensor = personagem2
while personagem1.vida_atual > 0 and personagem2.vida_atual > 0:
print(f"--- Turno {turno} ---")
# Escolher ação aleatória
acao = random.choice(["ataque", "especial"])
if acao == "ataque":
atacante.atacar(defensor)
else:
atacante.habilidade_especial(defensor)
# Trocar atacante e defensor
atacante, defensor = defensor, atacante
turno += 1
if turno > 20: # Limite de segurança
print("⏱️ Combate muito longo! Empate técnico.")
break
# Declarar vencedor
if personagem1.vida_atual > 0:
print(f"\n🏆 {personagem1.nome} VENCEU!")
elif personagem2.vida_atual > 0:
print(f"\n🏆 {personagem2.nome} VENCEU!")
else:
print("\n🤝 EMPATE!")
# Criar personagens
guerreiro = Guerreiro("Thorin")
mago = Mago("Gandalf")
arqueiro = Arqueiro("Legolas")
# Mostrar status
guerreiro.status()
mago.status()
# Adicionar itens
guerreiro.adicionar_item("Espada Lendária")
mago.adicionar_item("Cajado Arcano")
# Simular combate
Combate.batalha(guerreiro, mago)
💡 Composição vs Herança
Composição é uma alternativa à herança onde um objeto "tem" outro objeto em vez de "ser" outro tipo:
# Herança
class MotorEletrico:
def ligar(self):
print("🔌 Motor elétrico ligado")
class CarroEletrico(MotorEletrico): # Herança
pass
# Composição (geralmente melhor!)
class Motor:
def ligar(self):
print("🚗 Motor ligado")
class Carro:
def __init__(self):
self.motor = Motor() # Composição
def ligar(self):
self.motor.ligar()
carro = Carro()
carro.ligar()
📚 Boas Práticas em POO
- SOLID Principles:
- Single Responsibility: Uma classe = uma responsabilidade
- Open/Closed: Aberta para extensão, fechada para modificação
- Liskov Substitution: Subclasses devem ser substituíveis
- Interface Segregation: Interfaces específicas > genéricas
- Dependency Inversion: Dependa de abstrações
- Nomeação Clara: Use nomes descritivos para classes e métodos, similar ao que fazemos com funções
- Docstrings: Documente classes e métodos importantes
- Prefira Composição: "Tem um" geralmente é melhor que "É um"
- Use Encapsulamento: Proteja dados internos
- Type Hints: Use anotações de tipo em Python 3.5+
from typing import List, Optional
class Playlist:
"""Gerencia uma playlist de músicas"""
def __init__(self, nome: str) -> None:
self.nome: str = nome
self._musicas: List[str] = []
def adicionar(self, musica: str) -> None:
"""Adiciona música à playlist"""
self._musicas.append(musica)
def buscar(self, termo: str) -> Optional[str]:
"""Busca música por termo"""
for musica in self._musicas:
if termo.lower() in musica.lower():
return musica
return None
🚀 Próximos Passos
Agora que você domina POO em Python, explore tópicos relacionados:
- Funções em Python - Base para entender métodos
- Dicionários - Entenda
__dict__e atributos de objetos - Listas - Trabalhe com coleções de objetos
- Strings - Implemente
__str__e__repr__ - Design Patterns - Padrões de projeto em POO
- Dataclasses - Simplificação de classes de dados
- Abstract Base Classes (ABC) - Classes abstratas avançadas
Quer se tornar um mestre em Python e POO? Confira nosso curso completo de Python do zero ao avançado com projetos práticos de jogos, APIs e sistemas reais!
📝 Resumo
Neste guia completo sobre POO em Python, você aprendeu:
- ✅ Classes e objetos - fundamentos da POO
- ✅ Atributos e métodos - dados e comportamentos
- ✅ Encapsulamento - proteção de dados
- ✅ Herança - reutilização de código
- ✅ Polimorfismo - múltiplas formas
- ✅ Métodos especiais - personalização de objetos
- ✅ Métodos de classe e estáticos
- ✅ Properties - getters e setters pythônicos
- ✅ Projeto completo: Sistema de RPG espacial
- ✅ Composição vs Herança
- ✅ Boas práticas e SOLID
POO é essencial para criar aplicações complexas e profissionais em Python. Domine este paradigma e você estará pronto para construir sistemas robustos, escaláveis e de fácil manutenção!