Parâmetros obrigatórios não têm valor padrão — quem criar o objeto precisa passá-los. Parâmetros opcionais têm um valor padrão e podem ser omitidos.
class Produto:
def __init__(self, nome, preco, estoque=0):
# ^^^^^^^^^
# opcional — tem padrão
p1 = Produto("Camiseta", 49.90) # ok — estoque=0
p2 = Produto("Tênis", 299.90, 15) # ok — estoque=15 Parâmetros obrigatórios sempre vêm antes dos opcionais. Isso é uma regra do Python:
# ERRADO — opcional antes de obrigatório
def __init__(self, estoque=0, nome, preco): # SyntaxError!
# CORRETO
def __init__(self, nome, preco, estoque=0): # ok Use None quando qualquer valor (inclusive 0, False ou string vazia) pode ser um dado legítimo e você precisa distinguir "não informado" de "informado como zero":
class Aluno:
def __init__(self, nome, nota=None):
self.nome = nome
self.nota = nota # None = ainda não avaliado
def foi_avaliado(self):
return self.nota is not None Valide antes de atribuir. Assim o objeto nasce em estado válido ou lança erro imediatamente:
class ContaBancaria:
def __init__(self, titular, saldo=0):
if not titular:
raise ValueError("Titular obrigatório")
if saldo < 0:
raise ValueError("Saldo inicial não pode ser negativo")
self.titular = titular
self.saldo = saldo Composição é um tipo de relacionamento entre classes onde um objeto cria e possui outros objetos. O objeto filho é criado dentro do pai e não existe de forma independente — se o pai for destruído, os filhos também são.
Pergunte: "O objeto filho faz sentido existir sem o pai?"
O pai cria o filho internamente, geralmente dentro de um método:
class Pedido:
def __init__(self, numero):
self.numero = numero
self.itens = [] # lista vazia — itens criados depois
def adicionar(self, produto, qtd, preco):
item = ItemPedido(produto, qtd, preco) # pai cria o filho
self.itens.append(item) Na UML, composição é representada por um losango preenchido (◆) no lado do pai: Pedido ◆——→ ItemPedido
Agregação é um tipo de relacionamento onde um objeto referencia outro, mas os dois existem de forma independente. O objeto filho é criado fora do pai e passado para ele.
Pergunte: "O objeto filho pode existir sem o pai?"
O filho é criado fora e passado para o pai como parâmetro:
class Turma:
def __init__(self, codigo, professor): # recebe pronto
self.codigo = codigo
self.professor = professor # apenas referencia
# Professor criado independentemente
leo = Professor("Leo AD", "POO")
turma = Turma("ADS-2026", leo) # passa para a turma
del turma # turma some
print(leo.nome) # Leo AD — professor continua existindo Na UML, agregação é representada por um losango vazio (◇) no lado do pai: Turma ◇——→ Professor
A diferença mais fácil de identificar no código é quem cria o objeto filho:
| Aspecto | Composição | Agregação |
|---|---|---|
| Quem cria o filho | O pai | O código externo |
| Como o filho entra | Criado internamente | Passado como parâmetro |
| Ciclo de vida | Compartilhado | Independente |
| Relação | "tem um" (forte) | "usa um" (fraco) |
| UML | ◆ losango cheio | ◇ losango vazio |
class Autor: # existe independentemente
def __init__(self, nome):
self.nome = nome
class Livro: # recebe Autor (agregação)
def __init__(self, titulo, autor):
self.titulo = titulo
self.autor = autor # referência
class Biblioteca: # cria seus Livros (composição)
def __init__(self):
self.livros = []
def adicionar(self, titulo, autor):
livro = Livro(titulo, autor) # cria aqui
self.livros.append(livro)
# Autor existe antes de qualquer biblioteca
machado = Autor("Machado de Assis")
bib = Biblioteca()
bib.adicionar("Dom Casmurro", machado) # composição + agregação __init__ Programação Orientada a Objetos com Python
Tecnologia em Análise e Desenvolvimento de Sistemas
agenda de hoje
Parâmetros opcionais, valores padrão e validação direto no construtor.
Objetos que possuem outros objetos. A relação mais forte entre classes.
Relacionamento fraco — objetos que se referenciam sem se possuir.
revisão rápida
__init__Nas aulas anteriores usamos o construtor para definir atributos. Hoje vamos ver o que mais ele pode fazer.
class Produto:
def __init__(self, nome, preco):
self.nome = nome
self.preco = preco
# Toda vez que criamos um Produto,
# precisamos passar nome E preco.
# Mas e se quisermos um valor padrão?
# E se quisermos validar no momento da criação?
p = Produto("Camiseta", 49.90) # ok
p = Produto("Camiseta") # TypeError! __init__ em detalhes
Parâmetros com valor padrão se tornam opcionais — você pode passar ou não ao criar o objeto.
class Produto:
def __init__(self, nome, preco, estoque=0, categoria="Geral"):
self.nome = nome
self.preco = preco
self.estoque = estoque # padrão: 0
self.categoria = categoria # padrão: "Geral"
# Mínimo necessário
p1 = Produto("Camiseta", 49.90)
print(p1.estoque) # 0
print(p1.categoria) # Geral
# Passando tudo
p2 = Produto("Tênis", 299.90, 15, "Calçados")
print(p2.categoria) # Calçados __init__ em detalhes
None como padrão Quando o valor padrão pode ser qualquer coisa — inclusive zero ou string vazia — use None para indicar "não foi informado".
class Cliente:
def __init__(self, nome, email, telefone=None):
self.nome = nome
self.email = email
self.telefone = telefone # None = não informado
def tem_telefone(self):
return self.telefone is not None
c1 = Cliente("Ana", "ana@email.com")
c2 = Cliente("Pedro", "pedro@email.com", "(62) 99999-0000")
print(c1.tem_telefone()) # False
print(c2.tem_telefone()) # True __init__ em detalhes
O __init__ é o melhor lugar para garantir que um objeto nasce em estado válido.
class Produto:
def __init__(self, nome, preco, estoque=0):
if not nome:
raise ValueError("Nome não pode ser vazio")
if preco < 0:
raise ValueError("Preço não pode ser negativo")
if estoque < 0:
raise ValueError("Estoque não pode ser negativo")
self.nome = nome
self.preco = preco
self.estoque = estoque
# Produto("", 10) # ValueError: Nome não pode ser vazio
# Produto("X", -5) # ValueError: Preço não pode ser negativo __init__ + encapsulamento
Centralize as regras: a atribuição no __init__ dispara os setters automaticamente.
class Produto:
def __init__(self, nome, preco, estoque=0):
self.nome = nome # Dispara @nome.setter
self.preco = preco # Dispara @preco.setter
self.estoque = estoque # Dispara @estoque.setter
@property
def nome(self): return self._nome
@nome.setter
def nome(self, v):
if not v: raise ValueError("Nome vazio")
self._nome = v
@property
def preco(self): return self._preco
@preco.setter
def preco(self, v):
if v < 0: raise ValueError("Preço negativo")
self._preco = v
@property
def estoque(self): return self._estoque
@estoque.setter
def estoque(self, v):
if v < 0: raise ValueError("Estoque negativo")
self._estoque = v __init__ está limpo e as regras de proteção valem tanto para a criação quanto para alterações futuras.composição
O objeto filho é criado dentro do pai e não existe de forma independente. O ciclo de vida é compartilhado. Pedido → ItensPedido Casa → Cômodo Livro → Capítulo
composição · código
class ItemPedido:
def __init__(self, produto, quantidade, preco_unit):
self.produto = produto
self.quantidade = quantidade
self.preco_unit = preco_unit
def subtotal(self):
return self.quantidade * self.preco_unit
class Pedido:
def __init__(self, numero):
self.numero = numero
self.itens = [] # lista de ItemPedido
def adicionar(self, produto, qtd, preco):
item = ItemPedido(produto, qtd, preco) # pai cria o filho
self.itens.append(item)
def total(self):
return sum(item.subtotal() for item in self.itens)
p = Pedido("001")
p.adicionar("Camiseta", 2, 49.90)
p.adicionar("Tênis", 1, 299.90)
print(p.total()) # 399.70 agregação
O objeto filho é criado fora e passado para o pai. O ciclo de vida é independente. Turma → Professor Playlist → Música Departamento → Funcionário
agregação · código
class Professor:
def __init__(self, nome, disciplina):
self.nome = nome
self.disciplina = disciplina
class Turma:
def __init__(self, codigo, professor): # recebe o objeto pronto
self.codigo = codigo
self.professor = professor # apenas referencia
self.alunos = []
# Professor existe independentemente
leo = Professor("Leo AD", "POO com Python")
# Turma recebe o professor já criado
turma = Turma("ADS-2026", leo)
print(turma.professor.nome) # Leo AD
# Se a turma acabar, leo ainda existe
del turma
print(leo.nome) # Leo AD — continua vivo! comparação
A diferença prática está em quem cria o objeto filho. Se o pai cria — composição. Se o pai recebe pronto — agregação.
Pedido cria seus ItensPedido
Turma recebe Professor pronto
exercício
Vamos modelar um sistema usando composição e agregação juntos.
Criar Autor com nome e nacionalidade. Criar Livro com titulo, isbn e autor (agregação — recebe Autor pronto).
Criar Biblioteca com nome e lista de livros. Método adicionar_livro(titulo, isbn, autor) que cria o Livro internamente (composição).
Métodos listar() que imprime todos os livros e buscar_por_autor(nome) que retorna os livros daquele autor.
Autor é criado fora da biblioteca (agregação). O Livro é criado dentro dela (composição).exercício · partes 1 e 2
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, isbn, autor): # autor: agregação
self.titulo = titulo
self.isbn = isbn
self.autor = autor
def __str__(self):
return f"{self.titulo} — {self.autor.nome}"
class Biblioteca:
def __init__(self, nome):
self.nome = nome
self.livros = [] # composição
def adicionar_livro(self, titulo, isbn, autor):
livro = Livro(titulo, isbn, autor) # criado aqui dentro
self.livros.append(livro) exercício · parte 3
def listar(self):
print(f"=== {self.nome} ===")
for livro in self.livros:
print(f" {livro}")
def buscar_por_autor(self, nome):
return [l for l in self.livros if l.autor.nome == nome]
# Teste completo
machado = Autor("Machado de Assis", "Brasileiro") # agregação
kafka = Autor("Franz Kafka", "Tcheco")
bib = Biblioteca("Biblioteca Central")
bib.adicionar_livro("Dom Casmurro", "978-0", machado)
bib.adicionar_livro("Memórias Póstumas", "978-1", machado)
bib.adicionar_livro("A Metamorfose", "978-2", kafka)
bib.listar()
print(bib.buscar_por_autor("Machado de Assis")) fechamento
__init__ com valores padrão None como padrão quando o dado é verdadeiramente opcional para casa
Faça o máximo que conseguir e anote suas dúvidas para a próxima aula.
Exercícios: #1 ao #4
Exercícios: #5 ao #9
Exercícios: #10 ao #13
Exercícios: #14 e #15
exercícios para casa · nível fácil
Crie Carro com marca, modelo e cor='Branco' (opcional). Crie três instâncias: uma sem cor, duas com cores diferentes. Adicione __str__.
Crie Cliente com nome, email e telefone=None. Método tem_telefone() retorna True/False. Crie dois clientes e teste.
Crie Produto com nome e preco. O construtor deve lançar ValueError se o preço for negativo ou o nome vazio. Teste com valores válidos e inválidos.
Crie Endereco com rua, numero, cidade e complemento=None. Adicione __str__ que exibe o endereço completo, omitindo o complemento se for None.
exercícios para casa · nível médio
Implemente as classes ItemPedido e Pedido do slide 8. Adicione método listar() que imprime cada item com subtotal e o total geral ao final.
Implemente Professor e Turma do slide 10. Adicione matricular(aluno) e listar_alunos(). O professor deve existir antes da turma.
Crie ItemNF com descricao, quantidade e valor_unit. Crie NotaFiscal com numero e lista de itens criados internamente. Métodos adicionar_item e total().
Crie Endereco (exercício 4). Crie Pessoa com nome, cpf e que receba um Endereco pronto (agregação). Adicione __str__ que exibe nome e endereço completo.
Crie Musica com titulo, artista e duracao_seg. Crie Playlist que recebe músicas já criadas (agregação). Métodos adicionar, remover e duracao_total() em minutos.
exercícios para casa · nível difícil
Implemente as classes Autor, Livro e Biblioteca do exercício da aula (slides 13 e 14). Adicione também buscar_por_titulo(termo) que faz busca parcial e total_livros().
Crie Medico com nome, crm e especialidade. Crie Hospital que agrega médicos (recebe prontos). Métodos contratar(medico), demitir(crm) e buscar_especialidade(esp).
Crie Aluno, Turma (que cria seus alunos — composição) e Escola (que agrega turmas — agregação). Escola deve ter método total_alunos() que soma alunos de todas as turmas.
Crie Produto com nome, preco e estoque. Crie ItemCarrinho com produto (agregação) e quantidade. Crie Carrinho que cria seus itens internamente (composição). Métodos adicionar, remover, total() e finalizar() que reduz o estoque dos produtos.
exercícios para casa · nível avançado
Crie Cargo com titulo e salario_base. Crie Funcionario que recebe um Cargo (agregação) e tem bonus=0.0. @property salario_total retorna base + bonus. Crie Departamento que agrega funcionários. Método folha_pagamento() soma todos os salários totais.
Crie Categoria, Produto (agrega Categoria), ItemPedido e Pedido (composição de itens). Pedido deve ter cliente (agregação de Cliente), status='aberto' e método fechar() que valida estoque antes de confirmar. Se qualquer item não tiver estoque suficiente, lança ValueError.
Vamos consolidar tudo que vimos até agora antes de entrar em herança. Exercícios de fixação, mini projeto e espaço aberto para dúvidas.