Guia de estudo

15/04/2026 · Aula 05

Construtor __init__

Composição e Agregação

Programação Orientada a Objetos com Python
Tecnologia em Análise e Desenvolvimento de Sistemas

4 horas Teoria + Prática Objetos dentro de objetos Composição

agenda de hoje

O que vamos ver nessa aula

01

01 · __init__ em detalhes

Parâmetros opcionais, valores padrão e validação direto no construtor.

02

02 · Composição

Objetos que possuem outros objetos. A relação mais forte entre classes.

03

03 · Agregação

Relacionamento fraco — objetos que se referenciam sem se possuir.

revisão rápida

O que já sabemos sobre __init__

Nas aulas anteriores usamos o construtor para definir atributos. Hoje vamos ver o que mais ele pode fazer.

revisao.py
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 opcionais

Parâmetros com valor padrão se tornam opcionais — você pode passar ou não ao criar o objeto.

opcional.py
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
Parâmetros obrigatórios sempre vêm antes dos opcionais. Você não pode ter um obrigatório depois de um opcional.

__init__ em detalhes

Usando 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".

none_padrao.py
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

Validação no construtor

O __init__ é o melhor lugar para garantir que um objeto nasce em estado válido.

validacao.py
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
Valide antes de atribuir. Assim o objeto ou nasce certo ou não nasce — nunca fica em estado inválido.

__init__ + encapsulamento

Refatorando com Setters

Centralize as regras: a atribuição no __init__ dispara os setters automaticamente.

produto_v2.py
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
Agora o __init__ está limpo e as regras de proteção valem tanto para a criação quanto para alterações futuras.

composição

Objetos dentro de objetos

  • 📦 Um Pedido possui uma lista de ItensPedido
  • 🏠 Uma Casa possui Cômodos — sem a casa, não existem
  • 📱 Um Celular possui uma Bateria — partes internas
  • Relação "tem um" e "faz parte de"

Relação forte

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

Pedido com ItensPedido

composicao.py
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

Relacionamento fraco

  • 🎓 Um Professor leciona em uma Turma — mas existe fora dela
  • 🎵 Uma Playlist contém Músicas — mas as músicas existem sem a playlist
  • 👥 Um Departamento tem Funcionários — mas o funcionário não some com o depto
  • Relação "usa um" ou "conhece um"

Relação fraca

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

Turma com Professor

agregacao.py
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

Composição vs Agregaçã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.

Composição

  • · Filho é criado dentro do pai
  • · Ciclo de vida compartilhado
  • · Pai possui o filho
  • · Relação: "tem um"

Pedido cria seus ItensPedido

Agregação

  • · Filho é criado fora e passado
  • · Ciclo de vida independente
  • · Pai referencia o filho
  • · Relação: "usa um"

Turma recebe Professor pronto

exercício

Sistema de Biblioteca

Vamos modelar um sistema usando composição e agregação juntos.

Parte 1 · classes base

Criar Autor com nome e nacionalidade. Criar Livro com titulo, isbn e autor (agregação — recebe Autor pronto).

Parte 2 · composição

Criar Biblioteca com nome e lista de livros. Método adicionar_livro(titulo, isbn, autor) que cria o Livro internamente (composição).

Parte 3 · consultas

Métodos listar() que imprime todos os livros e buscar_por_autor(nome) que retorna os livros daquele autor.

O Autor é criado fora da biblioteca (agregação). O Livro é criado dentro dela (composição).

exercício · partes 1 e 2

Classes base e composição

biblioteca.py
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

Consultas e teste completo

biblioteca_completa.py
    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

O que vimos hoje

  • Parâmetros opcionais no __init__ com valores padrão
  • None como padrão quando o dado é verdadeiramente opcional
  • Validação no construtor — o objeto nasce certo ou não nasce
  • Composição — pai cria o filho, ciclo de vida compartilhado
  • Agregação — pai recebe o filho pronto, ciclo de vida independente

para casa

15 exercícios gradativos

Faça o máximo que conseguir e anote suas dúvidas para a próxima aula.

4

Fácil

Exercícios: #1 ao #4

5

Médio

Exercícios: #5 ao #9

4

Difícil

Exercícios: #10 ao #13

2

Avançado

Exercícios: #14 e #15

exercícios para casa · nível fácil

Nível Fácil

01

Carro com cor padrão

Crie Carro com marca, modelo e cor='Branco' (opcional). Crie três instâncias: uma sem cor, duas com cores diferentes. Adicione __str__.

02

Cliente com telefone opcional

Crie Cliente com nome, email e telefone=None. Método tem_telefone() retorna True/False. Crie dois clientes e teste.

03

Produto com validação básica

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.

04

Endereço simples

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

Nível Médio

05

Pedido com ItensPedido

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.

06

Turma com Professor

Implemente Professor e Turma do slide 10. Adicione matricular(aluno) e listar_alunos(). O professor deve existir antes da turma.

07

Nota Fiscal

Crie ItemNF com descricao, quantidade e valor_unit. Crie NotaFiscal com numero e lista de itens criados internamente. Métodos adicionar_item e total().

08

Pessoa com Endereço

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.

09

Playlist com Músicas

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

Nível Difícil

10

Sistema de Biblioteca

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().

11

Hospital e Médicos

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).

12

Escola com Turmas e Alunos

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.

13

Carrinho de Compras

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

Nível Avançado

14

Sistema de RH

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.

15

E-commerce simplificado

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.

próxima aula

Aula 6 · 22/04

Aula 06 · 22/04

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.

classes e objetos encapsulamento composição mini projeto