Programação Orientada a Objetos em Python
Introdução
A Programação Orientada a Objetos (POO) é um paradigma de programação baseado no conceito de "objetos", que podem conter dados na forma de atributos e código na forma de métodos. Em Python, tudo é um objeto - até mesmo tipos básicos como inteiros e strings.
Objetivos de Aprendizado
- Compreender os fundamentos da Programação Orientada a Objetos
- Aprender a criar e utilizar classes e objetos em Python
- Dominar os conceitos de atributos e métodos de instância e de classe
- Entender o funcionamento de métodos especiais (dunder methods)
- Aplicar encapsulamento, getters e setters
- Explorar a composição de objetos
Conceitos Fundamentais da POO
A Programação Orientada a Objetos se baseia em quatro pilares principais:
- Encapsulamento: agrupar dados e métodos em uma única unidade (objeto)
- Abstração: expor apenas o necessário e esconder a implementação
- Herança: criar novas classes a partir de classes existentes
- Polimorfismo: usar a mesma interface para diferentes tipos de objetos
# Uma classe é um modelo para criar objetos
class Pessoa:
# O método __init__ é chamado quando um objeto é criado
def __init__(self, nome, idade):
self.nome = nome # Atributo de instância
self.idade = idade # Atributo de instância
# Métodos definem comportamentos que os objetos podem ter
def apresentar(self):
return f"Olá, meu nome é {self.nome} e tenho {self.idade} anos."
def fazer_aniversario(self):
self.idade += 1
return f"Parabéns! Agora {self.nome} tem {self.idade} anos."
# Criando objetos (instâncias) da classe Pessoa
pessoa1 = Pessoa("Maria", 30)
pessoa2 = Pessoa("João", 25)
# Chamando métodos nos objetos
print(pessoa1.apresentar()) # Olá, meu nome é Maria e tenho 30 anos.
print(pessoa2.apresentar()) # Olá, meu nome é João e tenho 25 anos.
# Acessando e modificando atributos
print(pessoa1.nome) # Maria
pessoa1.nome = "Maria Silva"
print(pessoa1.apresentar()) # Olá, meu nome é Maria Silva e tenho 30 anos.
# Chamando outro método
print(pessoa1.fazer_aniversario()) # Parabéns! Agora Maria Silva tem 31 anos.
class Exemplo:
def __init__(self, valor):
# self se refere à instância atual
self.valor = valor
def metodo_de_instancia(self, multiplicador):
# self permite acessar os atributos e outros métodos da instância
return self.valor * multiplicador
def outro_metodo(self):
# Chamando outro método da mesma classe usando self
return self.metodo_de_instancia(2)
# Criando uma instância
ex = Exemplo(10)
# Chamando métodos
print(ex.metodo_de_instancia(3)) # 30
print(ex.outro_metodo()) # 20
# O que acontece por baixo dos panos:
# ex.metodo_de_instancia(3) é equivalente a Exemplo.metodo_de_instancia(ex, 3)
print(Exemplo.metodo_de_instancia(ex, 3)) # 30
class Contador:
# Variável de classe - compartilhada por todas as instâncias
contagem = 0
def __init__(self, nome):
self.nome = nome # Variável de instância - única para cada instância
# Incrementando a variável de classe
Contador.contagem += 1
@classmethod
def mostrar_contagem(cls):
# cls é uma referência à classe
return f"Existem {cls.contagem} contadores."
# Criando instâncias
c1 = Contador("Primeiro")
print(Contador.mostrar_contagem()) # Existem 1 contadores.
c2 = Contador("Segundo")
print(Contador.mostrar_contagem()) # Existem 2 contadores.
# Acessando a variável de classe
print(Contador.contagem) # 2
print(c1.contagem) # 2 (acessa a mesma variável)
print(c2.contagem) # 2 (acessa a mesma variável)
# Cuidado com atribuições diretas em instâncias!
c1.contagem = 10 # Isso cria uma variável de instância com o mesmo nome
print(Contador.contagem) # 2 (a variável de classe não foi alterada)
print(c1.contagem) # 10 (agora c1 tem sua própria variável contagem)
print(c2.contagem) # 2 (ainda referencia a variável de classe)
class Utilidades:
valor_padrao = 100
def __init__(self, valor):
self.valor = valor
# Método de instância - recebe self
def metodo_instancia(self):
return f"Método de instância: {self.valor}"
# Método de classe - recebe cls
@classmethod
def metodo_classe(cls, fator):
# Pode acessar/modificar atributos da classe
return f"Método de classe: {cls.valor_padrao * fator}"
# Método estático - não recebe nem self nem cls
@staticmethod
def metodo_estatico(x, y):
# Não pode acessar atributos de instância ou classe diretamente
return f"Método estático: {x + y}"
# Usando método de instância
u = Utilidades(5)
print(u.metodo_instancia()) # Método de instância: 5
# Usando método de classe
print(Utilidades.metodo_classe(2)) # Método de classe: 200
print(u.metodo_classe(2)) # Método de classe: 200 (também pode ser chamado por instâncias)
# Usando método estático
print(Utilidades.metodo_estatico(10, 20)) # Método estático: 30
print(u.metodo_estatico(10, 20)) # Método estático: 30 (também pode ser chamado por instâncias)
Métodos Especiais (Dunder Methods)
Python oferece métodos especiais (também chamados de "dunder methods" ou "magic methods") que permitem que classes definam comportamentos para operações built-in.
class Produto:
def __init__(self, nome, preco):
self.nome = nome
self.preco = preco
# Chamado quando str(objeto) ou print(objeto)
def __str__(self):
return f"{self.nome} - R${self.preco:.2f}"
# Chamado quando repr(objeto) ou na interpretação interativa
def __repr__(self):
return f"Produto('{self.nome}', {self.preco})"
# Criando um produto
p = Produto("Laptop", 3499.99)
# __str__ é usado quando convertemos para string ou imprimimos
print(p) # Laptop - R$3499.99
# __repr__ é usado para representação "oficial" (útil para debugging)
print(repr(p)) # Produto('Laptop', 3499.99)
# Em uma lista, __repr__ é usado por padrão
produtos = [Produto("Mouse", 29.99), Produto("Teclado", 89.90)]
print(produtos) # [Produto('Mouse', 29.99), Produto('Teclado', 89.9)]
class Vetor2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"({self.x}, {self.y})"
# Soma: v1 + v2
def __add__(self, outro):
return Vetor2D(self.x + outro.x, self.y + outro.y)
# Subtração: v1 - v2
def __sub__(self, outro):
return Vetor2D(self.x - outro.x, self.y - outro.y)
# Multiplicação por escalar: v1 * 3
def __mul__(self, escalar):
return Vetor2D(self.x * escalar, self.y * escalar)
# Multiplicação por escalar: 3 * v1
def __rmul__(self, escalar):
return self.__mul__(escalar)
# Comprimento do vetor: len(v1)
def __abs__(self):
import math
return math.sqrt(self.x ** 2 + self.y ** 2)
# Igualdade: v1 == v2
def __eq__(self, outro):
return self.x == outro.x and self.y == outro.y
# Criando vetores
v1 = Vetor2D(3, 4)
v2 = Vetor2D(1, 2)
# Usando operadores
print(f"v1 = {v1}") # v1 = (3, 4)
print(f"v2 = {v2}") # v2 = (1, 2)
print(f"v1 + v2 = {v1 + v2}") # v1 + v2 = (4, 6)
print(f"v1 - v2 = {v1 - v2}") # v1 - v2 = (2, 2)
print(f"v1 * 2 = {v1 * 2}") # v1 * 2 = (6, 8)
print(f"3 * v2 = {3 * v2}") # 3 * v2 = (3, 6)
print(f"|v1| = {abs(v1)}") # |v1| = 5.0
# Comparação
v3 = Vetor2D(3, 4)
print(f"v1 == v3: {v1 == v3}") # v1 == v3: True
print(f"v1 == v2: {v1 == v2}") # v1 == v2: False
class Playlist:
def __init__(self, nome, *musicas):
self.nome = nome
self.musicas = list(musicas)
def __str__(self):
return f"Playlist {self.nome} com {len(self.musicas)} músicas"
# Torna a classe iterável
def __iter__(self):
return iter(self.musicas)
# Permite acessar com notação de colchetes
def __getitem__(self, indice):
return self.musicas[indice]
# Permite atribuir com notação de colchetes
def __setitem__(self, indice, valor):
self.musicas[indice] = valor
# Permite usar a função len()
def __len__(self):
return len(self.musicas)
# Permite verificar se um item está contido: "in"
def __contains__(self, item):
return item in self.musicas
# Criando uma playlist
pl = Playlist("Meus Favoritos", "Yesterday", "Bohemian Rhapsody", "Imagine")
# Usando métodos de container
print(pl) # Playlist Meus Favoritos com 3 músicas
print(len(pl)) # 3
print(pl[1]) # Bohemian Rhapsody
# Modificando um item
pl[0] = "Hey Jude"
print(pl[0]) # Hey Jude
# Verificando se contém
print("Imagine" in pl) # True
print("Let It Be" in pl) # False
# Iterando
print("Músicas na playlist:")
for musica in pl:
print(f"- {musica}")
class Calculadora:
def __init__(self, valor_inicial=0):
self.valor = valor_inicial
# Permite que o objeto seja chamado como uma função
def __call__(self, x, operacao='+'):
if operacao == '+':
self.valor += x
elif operacao == '-':
self.valor -= x
elif operacao == '*':
self.valor *= x
elif operacao == '/':
self.valor /= x
return self.valor
# Criando uma calculadora
calc = Calculadora(10)
# Chamando o objeto como uma função
print(calc(5)) # 15 (10 + 5)
print(calc(3, '*')) # 45 (15 * 3)
print(calc(9, '/')) # 5.0 (45 / 9)
print(calc(2, '-')) # 3.0 (5.0 - 2)
# Este padrão é útil para funções que mantêm estado
counter = Calculadora()
for i in range(1, 6):
print(f"Contagem: {counter(1)}") # Incrementa em 1 a cada chamada
Encapsulamento, Getters e Setters
Em Python, o encapsulamento é mais uma convenção do que uma imposição.
class Conta:
def __init__(self, titular, saldo=0):
self.titular = titular # Público (acessível diretamente)
self._saldo = saldo # "Protegido" (convenção: não acesse diretamente)
self.__numero = self.__gerar_numero() # Privado (name mangling)
def __gerar_numero(self):
import random
return random.randint(10000, 99999)
def depositar(self, valor):
if valor > 0:
self._saldo += valor
return True
return False
def sacar(self, valor):
if 0 < valor <= self._saldo:
self._saldo -= valor
return True
return False
def consultar_saldo(self):
return self._saldo
def consultar_dados(self):
# Método para acessar atributo privado
return f"Conta nº {self.__numero}, Titular: {self.titular}, Saldo: R${self._saldo:.2f}"
# Criando uma conta
conta = Conta("Ana Silva", 1000)
# Acessando atributos
print(conta.titular) # Ana Silva
# Convenções:
# '_saldo' é protegido, mas ainda acessível (apenas uma convenção)
print(conta._saldo) # 1000
# Não se deve acessar '__numero' diretamente
# print(conta.__numero) # Erro: AttributeError
# Name mangling: Python renomeia atributos privados
print(conta._Conta__numero) # Funciona, mas não deve ser feito
# O modo correto é usar métodos
print(conta.consultar_dados())
class Temperatura:
def __init__(self, celsius=0):
self._celsius = celsius
# Getter
@property
def celsius(self):
return self._celsius
# Setter
@celsius.setter
def celsius(self, valor):
if valor < -273.15: # Validação: zero absoluto
raise ValueError("Temperatura abaixo do zero absoluto!")
self._celsius = valor
# Property calculada (apenas getter)
@property
def fahrenheit(self):
return (self._celsius * 9/5) + 32
# Setter para propriedade calculada
@fahrenheit.setter
def fahrenheit(self, valor):
self.celsius = (valor - 32) * 5/9
# Outra property calculada
@property
def kelvin(self):
return self._celsius + 273.15
@kelvin.setter
def kelvin(self, valor):
self.celsius = valor - 273.15
# Criando um objeto temperatura
temp = Temperatura(25)
# Acessando properties como se fossem atributos
print(f"Celsius: {temp.celsius}°C") # Celsius: 25°C
print(f"Fahrenheit: {temp.fahrenheit}°F") # Fahrenheit: 77.0°F
print(f"Kelvin: {temp.kelvin}K") # Kelvin: 298.15K
# Usando setters
temp.celsius = 30
print(f"Celsius: {temp.celsius}°C") # Celsius: 30°C
print(f"Fahrenheit: {temp.fahrenheit}°F") # Fahrenheit: 86.0°F
temp.fahrenheit = 68
print(f"Celsius: {temp.celsius}°C") # Celsius: 20.0°C
temp.kelvin = 300
print(f"Celsius: {temp.celsius}°C") # Celsius: 26.85°C
try:
temp.celsius = -300 # Abaixo do zero absoluto
except ValueError as e:
print(f"Erro: {e}") # Erro: Temperatura abaixo do zero absoluto!
class Pessoa:
def __init__(self, nome, idade):
self._nome = nome
self._idade = idade
self._adulto = None # Valor armazenado em cache
@property
def nome(self):
return self._nome
@property
def idade(self):
return self._idade
@idade.setter
def idade(self, valor):
if valor < 0:
raise ValueError("Idade não pode ser negativa")
# Quando a idade muda, invalidamos o cache
self._idade = valor
self._adulto = None
@property
def adulto(self):
# Lazy evaluation: só calcula quando necessário
if self._adulto is None:
self._adulto = self._idade >= 18
return self._adulto
# Usando a classe
p = Pessoa("Carlos", 16)
print(f"{p.nome} tem {p.idade} anos e é adulto? {p.adulto}")
# Carlos tem 16 anos e é adulto? False
p.idade = 20
print(f"{p.nome} tem {p.idade} anos e é adulto? {p.adulto}")
# Carlos tem 20 anos e é adulto? True
# Erro ao tentar atribuir a uma propriedade somente-leitura
try:
p.nome = "Carlos Silva"
except AttributeError as e:
print(f"Erro: {e}") # Erro: can't set attribute
Composição e Agregação
A composição é uma maneira de reutilizar código criando relações entre objetos.
class Motor:
def __init__(self, cilindradas):
self.cilindradas = cilindradas
self.ligado = False
def ligar(self):
self.ligado = True
return "Motor ligado!"
def desligar(self):
self.ligado = False
return "Motor desligado."
def esta_ligado(self):
return self.ligado
class Carro:
def __init__(self, modelo, cor, cilindradas):
self.modelo = modelo
self.cor = cor
# Composição: o carro possui um motor
self.motor = Motor(cilindradas)
self.velocidade = 0
def ligar(self):
return f"{self.modelo}: {self.motor.ligar()}"
def desligar(self):
if self.velocidade > 0:
return f"{self.modelo}: Pare o carro antes de desligar!"
return f"{self.modelo}: {self.motor.desligar()}"
def acelerar(self, valor):
if not self.motor.esta_ligado():
return f"{self.modelo}: Ligue o carro primeiro!"
self.velocidade += valor
return f"{self.modelo}: Velocidade atual: {self.velocidade} km/h"
def frear(self, valor):
if self.velocidade - valor < 0:
self.velocidade = 0
else:
self.velocidade -= valor
return f"{self.modelo}: Velocidade atual: {self.velocidade} km/h"
# Criando e usando um carro
meu_carro = Carro("Fusca", "Azul", 1300)
print(meu_carro.acelerar(20)) # Fusca: Ligue o carro primeiro!
print(meu_carro.ligar()) # Fusca: Motor ligado!
print(meu_carro.acelerar(20)) # Fusca: Velocidade atual: 20 km/h
print(meu_carro.acelerar(30)) # Fusca: Velocidade atual: 50 km/h
print(meu_carro.desligar()) # Fusca: Pare o carro antes de desligar!
print(meu_carro.frear(30)) # Fusca: Velocidade atual: 20 km/h
print(meu_carro.frear(30)) # Fusca: Velocidade atual: 0 km/h
print(meu_carro.desligar()) # Fusca: Motor desligado.
class Autor:
def __init__(self, nome, nacionalidade):
self.nome = nome
self.nacionalidade = nacionalidade
def __str__(self):
return f"{self.nome} ({self.nacionalidade})"
class Livro:
def __init__(self, titulo, autor, ano):
self.titulo = titulo
# Agregação: o livro tem um autor, mas o autor existe independentemente
self.autor = autor
self.ano = ano
def __str__(self):
return f'"{self.titulo}" por {self.autor}, {self.ano}'
class Biblioteca:
def __init__(self, nome):
self.nome = nome
self.livros = []
def adicionar_livro(self, livro):
self.livros.append(livro)
return f'"{livro.titulo}" adicionado à {self.nome}'
def listar_livros(self):
return [str(livro) for livro in self.livros]
# Criando autores independentes
machado = Autor("Machado de Assis", "Brasileiro")
tolkien = Autor("J.R.R. Tolkien", "Britânico")
# Criando livros com os autores existentes
livro1 = Livro("Dom Casmurro", machado, 1899)
livro2 = Livro("O Senhor dos Anéis", tolkien, 1954)
livro3 = Livro("Memórias Póstumas de Brás Cubas", machado, 1881)
# Criando uma biblioteca e adicionando livros
biblioteca = Biblioteca("Biblioteca Municipal")
print(biblioteca.adicionar_livro(livro1))
print(biblioteca.adicionar_livro(livro2))
print(biblioteca.adicionar_livro(livro3))
# Listando os livros
print(f"\nLivros disponíveis na {biblioteca.nome}:")
for livro in biblioteca.listar_livros():
print(f"- {livro}")
# Observe que os autores existem independentemente dos livros
print(f"\nAutores:")
print(f"- {machado}")
print(f"- {tolkien}")
from datetime import datetime, timedelta
class Usuario:
def __init__(self, nome, email):
self.nome = nome
self.email = email
self.data_cadastro = datetime.now()
self.carrinho = Carrinho()
def __str__(self):
return f"Usuário: {self.nome} ({self.email})"
class Produto:
def __init__(self, nome, preco, codigo):
self.nome = nome
self.preco = preco
self.codigo = codigo
def __str__(self):
return f"{self.nome} - R${self.preco:.2f}"
class ItemCarrinho:
def __init__(self, produto, quantidade=1):
self.produto = produto
self.quantidade = quantidade
@property
def subtotal(self):
return self.produto.preco * self.quantidade
def __str__(self):
return f"{self.produto.nome} x {self.quantidade} = R${self.subtotal:.2f}"
class Carrinho:
def __init__(self):
self.itens = []
def adicionar_item(self, produto, quantidade=1):
# Verifica se o produto já está no carrinho
for item in self.itens:
if item.produto.codigo == produto.codigo:
item.quantidade += quantidade
return f"{produto.nome} adicionado. Nova quantidade: {item.quantidade}"
# Adiciona novo item
novo_item = ItemCarrinho(produto, quantidade)
self.itens.append(novo_item)
return f"{produto.nome} adicionado ao carrinho."
def remover_item(self, codigo_produto):
for i, item in enumerate(self.itens):
if item.produto.codigo == codigo_produto:
removido = self.itens.pop(i)
return f"{removido.produto.nome} removido do carrinho."
return "Produto não encontrado no carrinho."
@property
def total(self):
return sum(item.subtotal for item in self.itens)
def __str__(self):
if not self.itens:
return "Carrinho vazio"
resultado = "Itens no carrinho:\n"
for item in self.itens:
resultado += f"- {item}\n"
resultado += f"Total: R${self.total:.2f}"
return resultado
class Pedido:
contador = 0
def __init__(self, usuario, itens_carrinho):
Pedido.contador += 1
self.numero = Pedido.contador
self.usuario = usuario
self.itens = list(itens_carrinho) # Cópia dos itens
self.data = datetime.now()
self.total = sum(item.subtotal for item in self.itens)
@property
def data_entrega_estimada(self):
return self.data + timedelta(days=7)
def __str__(self):
resultado = f"Pedido #{self.numero} - {self.usuario.nome}\n"
resultado += f"Data: {self.data.strftime('%d/%m/%Y %H:%M')}\n"
resultado += "Itens:\n"
for item in self.itens:
resultado += f"- {item}\n"
resultado += f"Total: R${self.total:.2f}\n"
resultado += f"Entrega estimada: {self.data_entrega_estimada.strftime('%d/%m/%Y')}"
return resultado
# Exemplo de uso do modelo
# Criando produtos
p1 = Produto("Camiseta", 29.90, "CAM001")
p2 = Produto("Calça Jeans", 99.90, "CAL001")
p3 = Produto("Tênis", 149.90, "TEN001")
# Criando usuário
usuario = Usuario("Ana Silva", "[email protected]")
print(usuario)
# Adicionando itens ao carrinho
print(usuario.carrinho.adicionar_item(p1, 2))
print(usuario.carrinho.adicionar_item(p2))
print(usuario.carrinho.adicionar_item(p3))
print(usuario.carrinho.adicionar_item(p1)) # Aumenta a quantidade
# Exibindo o carrinho
print("\nCarrinho atual:")
print(usuario.carrinho)
# Removendo um item
print("\n" + usuario.carrinho.remover_item("TEN001"))
# Criando um pedido com os itens do carrinho
pedido = Pedido(usuario, usuario.carrinho.itens)
# Exibindo o pedido
print("\nPedido criado:")
print(pedido)
Exemplo Completo: Aplicação Simples
A seguir, temos um exemplo completo de uma aplicação simples modelada com orientação a objetos.
class Tarefa:
"""Representa uma tarefa a ser realizada."""
def __init__(self, titulo, descricao="", concluida=False, prioridade=1):
self.titulo = titulo
self.descricao = descricao
self.concluida = concluida
self.prioridade = prioridade
def concluir(self):
self.concluida = True
return f"Tarefa '{self.titulo}' marcada como concluída."
def reabrir(self):
self.concluida = False
return f"Tarefa '{self.titulo}' reaberta."
def __str__(self):
status = "✓" if self.concluida else " "
prioridade = "!" * self.prioridade
return f"[{status}] {self.titulo} {prioridade}"
def __repr__(self):
return f"Tarefa('{self.titulo}', '{self.descricao}', {self.concluida}, {self.prioridade})"
class ListaTarefas:
"""Gerencia uma coleção de tarefas."""
def __init__(self, nome):
self.nome = nome
self.tarefas = []
def adicionar_tarefa(self, titulo, descricao="", prioridade=1):
tarefa = Tarefa(titulo, descricao, prioridade=prioridade)
self.tarefas.append(tarefa)
return f"Tarefa '{titulo}' adicionada à lista '{self.nome}'."
def concluir_tarefa(self, indice):
if 0 <= indice < len(self.tarefas):
return self.tarefas[indice].concluir()
return "Índice de tarefa inválido."
def reabrir_tarefa(self, indice):
if 0 <= indice < len(self.tarefas):
return self.tarefas[indice].reabrir()
return "Índice de tarefa inválido."
def remover_tarefa(self, indice):
if 0 <= indice < len(self.tarefas):
tarefa = self.tarefas.pop(indice)
return f"Tarefa '{tarefa.titulo}' removida."
return "Índice de tarefa inválido."
def listar_tarefas(self, apenas_pendentes=False):
if not self.tarefas:
return "Não há tarefas nesta lista."
resultado = f"Lista de Tarefas: {self.nome}\n"
tarefas_filtradas = [t for t in self.tarefas if not apenas_pendentes or not t.concluida]
if not tarefas_filtradas:
return resultado + "Não há tarefas pendentes."
# Ordenar por prioridade (decrescente) e por conclusão
tarefas_ordenadas = sorted(
tarefas_filtradas,
key=lambda t: (-t.prioridade, t.concluida)
)
for i, tarefa in enumerate(tarefas_ordenadas):
resultado += f"{i}. {tarefa}\n"
if tarefa.descricao:
resultado += f" {tarefa.descricao}\n"
return resultado
class GerenciadorTarefas:
"""Sistema para gerenciar múltiplas listas de tarefas."""
def __init__(self):
self.listas = {}
def criar_lista(self, nome):
if nome in self.listas:
return f"Lista '{nome}' já existe."
self.listas[nome] = ListaTarefas(nome)
return f"Lista '{nome}' criada com sucesso."
def remover_lista(self, nome):
if nome in self.listas:
del self.listas[nome]
return f"Lista '{nome}' removida com sucesso."
return f"Lista '{nome}' não encontrada."
def listar_listas(self):
if not self.listas:
return "Não há listas de tarefas."
resultado = "Listas de Tarefas:\n"
for nome, lista in self.listas.items():
tarefas_pendentes = sum(1 for t in lista.tarefas if not t.concluida)
resultado += f"- {nome} ({tarefas_pendentes} pendentes)\n"
return resultado
def obter_lista(self, nome):
return self.listas.get(nome)
# Demonstração do uso
def demonstracao():
gerenciador = GerenciadorTarefas()
print(gerenciador.criar_lista("Trabalho"))
print(gerenciador.criar_lista("Pessoal"))
lista_trabalho = gerenciador.obter_lista("Trabalho")
lista_pessoal = gerenciador.obter_lista("Pessoal")
print(lista_trabalho.adicionar_tarefa("Enviar relatório", "Relatório mensal para o gerente", 3))
print(lista_trabalho.adicionar_tarefa("Reunião de projeto", "Videoconferência às 14h", 2))
print(lista_trabalho.adicionar_tarefa("Revisar código", "Verificar PR #123 no GitHub", 2))
print(lista_pessoal.adicionar_tarefa("Comprar mantimentos", "Leite, ovos, pão", 1))
print(lista_pessoal.adicionar_tarefa("Academia", "Treino de musculação", 2))
print("\n" + gerenciador.listar_listas())
print("\n" + lista_trabalho.listar_tarefas())
print("\nConcluindo tarefas:")
print(lista_trabalho.concluir_tarefa(1))
print(lista_pessoal.concluir_tarefa(0))
print("\nTarefas pendentes do trabalho:")
print(lista_trabalho.listar_tarefas(apenas_pendentes=True))
print("\nRemovendo lista:")
print(gerenciador.remover_lista("Pessoal"))
print(gerenciador.listar_listas())
if __name__ == "__main__":
demonstracao()
Resumo
Nesta aula, você aprendeu os fundamentos da Programação Orientada a Objetos em Python:
- Classes e Objetos: Como criar modelos (classes) e instâncias (objetos)
- Atributos e Métodos: Como definir dados e comportamentos
- Métodos Especiais: Como personalizar o comportamento dos objetos com métodos dunder
- Encapsulamento: Como proteger dados e fornecer interfaces controladas com properties
- Composição e Agregação: Como criar relações entre objetos
- Aplicações Práticas: Como modelar problemas do mundo real usando POO
Recursos de aprendizado
Próximos Passos
Na próxima aula, vamos explorar o conceito de herança e polimorfismo, expandindo nossos conhecimentos sobre Programação Orientada a Objetos e aprendendo como criar hierarquias de classes.