Trilha de aprendizado · Nível 7 · Tutorial 7

Criar exceções para erros do domínio

Representar falhas específicas da aplicação com classes de exceção próprias e permitir que o código chamador trate cada situação no nível adequado.

  • Nível: Intermediário
  • Duração: 18 min
  • 7 passos
Criar exceções para erros do domínio

O que você vai percorrer

  1. Reconhecer falhas que pertencem ao domínio Identifique quais recusas de uma operação representam situações próprias da aplicação e devem ser distinguíveis por tipo. 2 min
  2. Definir uma exceção própria Crie um tipo de exceção para identificar uma sessão encerrada e use a inicialização herdada para fornecer uma mensagem. 2 min
  3. Agrupar falhas relacionadas em uma hierarquia Organize falhas específicas de uma reserva sob uma classe base comum, preservando a identidade de cada situação. 2 min
  4. Acrescentar contexto sem perder a mensagem Personalize uma exceção para manter uma mensagem legível e também oferecer dados estruturados ao código chamador. 3 min
  5. Sinalizar falhas nas operações dos objetos Integre exceções de domínio ao método de reserva, verificando recusas antes de alterar o estado do objeto. 3 min
  6. Tratar pelo tipo, do específico ao geral Organize capturas específicas e gerais para responder a falhas de reserva pelo tipo, sem depender da mensagem. 3 min
  7. Aplicar e revisar as exceções de reserva Integre as exceções de domínio em um script Python, execute três cenários e observe como tipo, contexto e estado orientam o tratamento. 5 min

O que você vai aprender

  • Definir uma exceção de domínio derivada de Exception.
  • Organizar exceções específicas sob uma classe base comum quando houver necessidade de tratamento conjunto.
  • Acrescentar informações úteis a uma exceção sem perder sua mensagem.
  • Sinalizar falhas de operações dos objetos e capturá-las pela classe apropriada, sem depender do texto da mensagem.

Antes de começar

  • Especializar classes com herança e super
  • Preservar regras de validade em objetos
  • Tratar exceções com try, except, else e finally
  • Validar entradas e sinalizar falhas com raise

Passo 1 de 7

Reconhecer falhas que pertencem ao domínio

Identifique quais recusas de uma operação representam situações próprias da aplicação e devem ser distinguíveis por tipo.

Falhas previstas da reserva

Quando a falha pertence ao domínio

Imagine uma operação para reservar vagas em uma sessão. Ela pode recusar o pedido porque a sessão já foi encerrada ou porque não há vagas suficientes.

Essas são falhas previstas pelas regras da aplicação. Cada uma pode merecer um tipo próprio de exceção, pois o código chamador talvez precise responder de maneira diferente a cada situação.

Dois motivos de recusa

A mesma solicitação de reserva pode encontrar condições diferentes do domínio.

Diagrama visual de uma solicitação de reserva seguindo para sucesso, sessão encerrada ou vagas insuficientes.

A recusa por sessão encerrada e a recusa por falta de vagas são resultados previstos e distintos da operação.

Domínio, entrada ou defeito?

Nem todo erro precisa de um tipo próprio

Use uma exceção de domínio quando a falha representa uma situação relevante nas regras da aplicação.

Uma restrição genérica de argumento, como receber uma quantidade negativa, ainda pode ser comunicada por uma exceção embutida adequada. Já um nome de atributo digitado incorretamente é um defeito inesperado do programa, não uma recusa prevista da reserva.

Exemplo

Tipo identifica; mensagem explica

“Vagas insuficientes” identifica programaticamente qual falha ocorreu. Uma mensagem como “Foram solicitadas 5 vagas, mas há apenas 2 disponíveis” oferece uma explicação legível para pessoas.

O tratamento deve poder reconhecer a situação pelo tipo, sem precisar comparar ou interpretar o texto da mensagem.

Dica

Critério prático

Pergunte: “O chamador pode oferecer uma resposta específica para esta situação prevista?” Se sim, um tipo próprio pode tornar o comportamento da operação mais claro.

Classifique as situações

Que tipo de falha é esta?

Associe cada situação à classificação mais adequada.

Toque em um item e depois no par correspondente.

Passo 2 de 7

Definir uma exceção própria

Crie um tipo de exceção para identificar uma sessão encerrada e use a inicialização herdada para fornecer uma mensagem.

Um tipo próprio para a falha

Declare a falha pelo tipo

Quando uma tentativa de reserva encontra uma sessão encerrada, podemos representar essa situação com um tipo próprio. Como você já conhece herança simples, basta especializar Exception:

SessaoEncerrada identifica programaticamente a falha. Se ela não precisa guardar dados adicionais nem mudar comportamentos, seu corpo pode conter apenas pass.

Para erros usuais da aplicação, derive de Exception, não de BaseException. A classe BaseException também abrange sinais de controle como KeyboardInterrupt e SystemExit, que normalmente não devem ser tratados como falhas do domínio.

Especialização direta

A nova classe herda de Exception tanto o comportamento de uma exceção quanto a capacidade de receber uma mensagem.

Diagrama com Exception acima e uma exceção de sessão encerrada abaixo, ligadas por uma seta de herança; a capacidade de carregar uma mensagem passa para a classe especializada.

SessaoEncerrada é um tipo distinto, mas reaproveita o comportamento de Exception.

Forneça a mensagem ao criar a exceção

Definição, sinalização e captura

Este exemplo mínimo cria o tipo, sinaliza a falha e a captura pelo novo tipo.

python
class SessaoEncerrada(Exception):
    pass


try:
    raise SessaoEncerrada(
        "Não é possível reservar: a sessão está encerrada."
    )
except SessaoEncerrada as erro:
    print(type(erro).__name__)
    print(str(erro))

Exemplo

Saída esperada

SessaoEncerrada
Não é possível reservar: a sessão está encerrada.

Dica

Nenhum `__init__` é necessário

A chamada SessaoEncerrada("mensagem") usa a inicialização herdada de Exception. str(erro) recupera essa mensagem, enquanto o tipo SessaoEncerrada permite reconhecer a situação sem analisar seu texto.

Complete a definição

Escolha a classe base

Complete a definição sem acrescentar um inicializador desnecessário:

class SessaoEncerrada(_):
pass

raise SessaoEncerrada("A sessão está encerrada.")

Passo 3 de 7

Agrupar falhas relacionadas em uma hierarquia

Organize falhas específicas de uma reserva sob uma classe base comum, preservando a identidade de cada situação.

Uma família de falhas de reserva

Específicas, mas relacionadas

SessaoEncerrada e VagasInsuficientes representam recusas diferentes. Ainda assim, ambas pertencem à mesma família: são falhas previstas de uma operação de reserva.

Uma classe base chamada ErroDeReserva expressa essa relação. Cada subclasse mantém sua identidade específica e, ao mesmo tempo, também pode ser reconhecida como um erro de reserva.

Estrutura da hierarquia

A hierarquia parte de Exception, passa pela família ErroDeReserva e se divide nas duas falhas específicas.

Diagrama de herança no qual Exception é a base de ErroDeReserva, que por sua vez é a base de SessaoEncerrada e VagasInsuficientes.

As duas classes específicas são especializações de ErroDeReserva.

Definir a hierarquia

Uma base comum e duas especializações

A classe base deriva de Exception, pois representa uma falha usual da aplicação. As duas situações específicas passam a derivar de ErroDeReserva.

Exceções da reserva

As classes precisam apenas identificar os tipos de falha neste momento, por isso podem usar o comportamento herdado.

python
class ErroDeReserva(Exception):
    pass


class SessaoEncerrada(ErroDeReserva):
    pass


class VagasInsuficientes(ErroDeReserva):
    pass

Dica

Mantenha a hierarquia pequena

Crie uma classe quando o tipo representar uma distinção útil para o código chamador. Não é necessário criar uma subclasse para cada variação de mensagem. Nesta modelagem, a base permite reconhecer a família, enquanto as subclasses distinguem as duas causas de recusa.

Escolha uma estrutura adequada

Modele as falhas

Qual hierarquia permite identificar separadamente uma sessão encerrada e a falta de vagas, além de reconhecer ambas como falhas de reserva?

Passo 4 de 7

Acrescentar contexto sem perder a mensagem

Personalize uma exceção para manter uma mensagem legível e também oferecer dados estruturados ao código chamador.

Mensagem e dados no mesmo objeto

Dois caminhos de consulta

A exceção VagasInsuficientes pode servir a pessoas e ao código ao mesmo tempo. Sua mensagem explica a falha de forma legível, enquanto os atributos solicitadas e disponiveis preservam as quantidades como dados separados.

Assim, o código chamador não precisa recortar ou interpretar palavras da mensagem para descobrir os valores envolvidos.

Anatomia da exceção

O mesmo objeto de exceção reúne uma mensagem legível e atributos estruturados.

Diagrama de um objeto de exceção ligado a uma mensagem e a dois compartimentos numéricos, representando vagas solicitadas e disponíveis.

A mensagem é consultada com str(erro); as quantidades são consultadas diretamente nos atributos do objeto.

Personalizar a inicialização

VagasInsuficientes com contexto

O __init__ recebe as quantidades, armazena cada uma em um atributo e inicializa a parte herdada com a mensagem construída.

python
class ErroDeReserva(Exception):
    pass


class VagasInsuficientes(ErroDeReserva):
    def __init__(self, solicitadas, disponiveis):
        self.solicitadas = solicitadas
        self.disponiveis = disponiveis

        mensagem = (
            f"Foram solicitadas {solicitadas} vagas, "
            f"mas apenas {disponiveis} estão disponíveis."
        )
        super().__init__(mensagem)


erro = VagasInsuficientes(5, 2)

print(str(erro))
print(erro.solicitadas)
print(erro.disponiveis)

Dica

Não extraia dados da mensagem

Use str(erro) quando precisar da explicação legível. Para tomar decisões com as quantidades, use erro.solicitadas e erro.disponiveis. A redação da mensagem poderá mudar sem quebrar esse código.

Exemplo

Resultado esperado

A execução imprime:

Foram solicitadas 5 vagas, mas apenas 2 estão disponíveis.

5

2

Complete e explique

Preserve a mensagem

Complete a última linha do inicializador:

class VagasInsuficientes(ErroDeReserva):
    def __init__(self, solicitadas, disponiveis):
        self.solicitadas = solicitadas
        self.disponiveis = disponiveis
        mensagem = f"Solicitadas: {solicitadas}; disponíveis: {disponiveis}."
        _____

Consulte cada informação pelo caminho adequado

Considere erro = VagasInsuficientes(5, 2). Como você obteria a mensagem legível, a quantidade solicitada e a quantidade disponível sem extrair números do texto?

Escreva pelo menos 40 caracteres (0/40).

Passo 5 de 7

Sinalizar falhas nas operações dos objetos

Integre exceções de domínio ao método de reserva, verificando recusas antes de alterar o estado do objeto.

Verificar antes de alterar

Cada recusa tem um tipo

O método reservar deve associar cada condição prevista ao tipo correspondente: uma sessão encerrada lança SessaoEncerrada; uma quantidade maior que as vagas disponíveis lança VagasInsuficientes com as quantidades envolvidas.

As verificações acontecem antes da alteração de vagas. Assim, quando a operação é recusada, o estado da sessão permanece igual.

Fluxo seguro da reserva

Fluxo no qual as condições de recusa são verificadas antes da redução das vagas.

Somente o caminho que passa por todas as verificações chega à alteração do estado.

Implementar a operação

Exceções e classe Sessao

A docstring registra as falhas previstas como parte do comportamento público de reservar.

python
class ErroDeReserva(Exception):
    pass


class SessaoEncerrada(ErroDeReserva):
    pass


class VagasInsuficientes(ErroDeReserva):
    def __init__(self, solicitadas, disponiveis):
        self.solicitadas = solicitadas
        self.disponiveis = disponiveis
        mensagem = (
            f"Foram solicitadas {solicitadas} vagas, "
            f"mas apenas {disponiveis} estão disponíveis."
        )
        super().__init__(mensagem)


class Sessao:
    def __init__(self, vagas):
        self.vagas = vagas
        self.encerrada = False

    def encerrar(self):
        self.encerrada = True

    def reservar(self, quantidade):
        """Reserva vagas na sessão.

        Raises:
            SessaoEncerrada: se a sessão não aceita mais reservas.
            VagasInsuficientes: se não há vagas para a quantidade pedida.
        """
        if self.encerrada:
            raise SessaoEncerrada("A sessão está encerrada.")

        if quantidade > self.vagas:
            raise VagasInsuficientes(quantidade, self.vagas)

        self.vagas -= quantidade

Dica

Estado preservado na recusa

Com self.vagas -= quantidade depois das duas verificações, nenhum dos raise ocorre após uma alteração parcial. Se uma sessão com 3 vagas recusar uma solicitação de 5, ela continua com 3 vagas.

Sinalizar não é apresentar

Responsabilidades separadas

O objeto Sessao conhece suas regras e sinaliza por que a operação foi recusada. Ele não decide se a aplicação mostrará uma mensagem, solicitará outra quantidade ou cancelará o fluxo. Essa decisão pertence ao código chamador.

Por isso, o método lança a exceção apropriada, mas não imprime mensagens nem captura a própria falha de domínio.

Atenção

Não transforme qualquer defeito em erro de reserva

Evite envolver todo o método em except Exception para converter qualquer problema em ErroDeReserva. Um defeito inesperado, como acessar um atributo com nome incorreto, deve continuar visível como a falha original. Converta apenas situações que sejam realmente recusas previstas da operação.

Analise a ordem da operação

Reserva recusada

Uma sessão possui 3 vagas e recebe uma solicitação de 5. Qual sequência implementa corretamente essa parte de reservar?

Passo 6 de 7

Tratar pelo tipo, do específico ao geral

Organize capturas específicas e gerais para responder a falhas de reserva pelo tipo, sem depender da mensagem.

A primeira captura compatível vence

Da falha específica para a família

Um except ErroDeReserva também captura suas subclasses, como SessaoEncerrada e VagasInsuficientes. Como Python escolhe o primeiro bloco compatível, coloque a captura específica antes da captura da classe base.

Assim, o chamador pode usar os atributos de VagasInsuficientes quando essa distinção for útil e ainda oferecer uma resposta comum às demais falhas de reserva.

Ordem de seleção das capturas

A busca percorre os blocos de cima para baixo e para na primeira correspondência.

Diagrama da hierarquia de exceções ligado a uma sequência de capturas, com a captura específica antes da captura da classe base.

VagasInsuficientes encontra primeiro sua captura específica. SessaoEncerrada passa por ela e é recebida pela captura geral de ErroDeReserva.

Respostas diferentes sem analisar mensagens

Captura específica antes da base

O primeiro bloco usa dados estruturados da exceção. O segundo trata o restante da família de forma comum.

python
def confirmar_reserva(sessao, quantidade):
    try:
        sessao.reservar(quantidade)
    except VagasInsuficientes as erro:
        print(
            f"Não foi possível reservar {erro.solicitadas} vaga(s): "
            f"há apenas {erro.disponiveis} disponível(is)."
        )
    except ErroDeReserva as erro:
        print(f"Reserva recusada: {erro}")
    else:
        print("Reserva confirmada.")

Atenção

Capture somente o que você sabe tratar

Não acrescente except Exception apenas para impedir que o programa mostre uma falha. Isso também capturaria defeitos inesperados e poderia ocultar o problema. Aqui, o chamador trata somente as falhas previstas da família ErroDeReserva; outras exceções continuam se propagando.

Dica

Tipo decide; mensagem explica

Alterar a redação retornada por str(erro) não muda qual bloco será executado. Evite comparar mensagens: escolha o tratamento pelo tipo e consulte atributos quando precisar de detalhes.

Ordene as capturas

Do específico ao geral

Ordene os trechos para preservar o tratamento específico de vagas insuficientes e depois tratar as demais falhas de reserva.

  1. except ErroDeReserva as erro: informar_recusa(str(erro))
  2. except VagasInsuficientes as erro: informar_vagas(erro.solicitadas, erro.disponiveis)
  3. try: sessao.reservar(quantidade)

Preveja o destino de cada falha

Qual bloco será executado?

Considere a ordem except VagasInsuficientes e depois except ErroDeReserva. Associe cada exceção ao resultado.

Toque em um item e depois no par correspondente.

Passo 7 de 7

Aplicar e revisar as exceções de reserva

Integre as exceções de domínio em um script Python, execute três cenários e observe como tipo, contexto e estado orientam o tratamento.

Monte o script integrado

Execute no seu computador

Crie um arquivo chamado reservas.py, copie todo o código abaixo e execute-o com python reservas.py ou python3 reservas.py. O script usa apenas Python 3 e cria uma sessão independente para cada cenário, evitando que uma tentativa interfira nas demais.

reservas.py

O objeto sinaliza cada recusa; o código chamador escolhe como tratar cada tipo.

python
class ErroDeReserva(Exception):
    pass


class SessaoEncerrada(ErroDeReserva):
    pass


class VagasInsuficientes(ErroDeReserva):
    def __init__(self, solicitadas, disponiveis):
        self.solicitadas = solicitadas
        self.disponiveis = disponiveis
        mensagem = (
            f"Reserva de {solicitadas} vaga(s) recusada: "
            f"somente {disponiveis} disponível(is)."
        )
        super().__init__(mensagem)


class Sessao:
    def __init__(self, vagas, encerrada=False):
        self.vagas = vagas
        self.encerrada = encerrada

    def reservar(self, quantidade):
        """Reserva vagas.

        Levanta SessaoEncerrada se a sessão estiver encerrada.
        Levanta VagasInsuficientes se não houver vagas suficientes.
        """
        if self.encerrada:
            raise SessaoEncerrada("A sessão já foi encerrada.")

        if quantidade > self.vagas:
            raise VagasInsuficientes(quantidade, self.vagas)

        self.vagas -= quantidade


def executar_cenario(nome, sessao, quantidade):
    print(f"\n--- {nome} ---")
    print(f"Vagas antes: {sessao.vagas}")

    try:
        sessao.reservar(quantidade)
    except VagasInsuficientes as erro:
        print(f"Captura específica: {type(erro).__name__}")
        print(f"Mensagem: {erro}")
        print(f"Solicitadas: {erro.solicitadas}")
        print(f"Disponíveis: {erro.disponiveis}")
    except ErroDeReserva as erro:
        print(f"Captura pela base: {type(erro).__name__}")
        print(f"Mensagem: {erro}")
    else:
        print("Reserva realizada com sucesso.")

    print(f"Vagas depois: {sessao.vagas}")


executar_cenario(
    "Reserva bem-sucedida",
    Sessao(vagas=5),
    quantidade=2,
)

executar_cenario(
    "Vagas insuficientes",
    Sessao(vagas=2),
    quantidade=4,
)

executar_cenario(
    "Sessão encerrada",
    Sessao(vagas=5, encerrada=True),
    quantidade=1,
)

Dica

Observe a ordem das capturas

VagasInsuficientes aparece antes de ErroDeReserva. Como a primeira é subclasse da segunda, inverter essa ordem faria a captura geral receber também a falta de vagas.

Acompanhe os três caminhos

Compare tipo, tratamento e estado

Na reserva bem-sucedida, duas vagas são descontadas. Nas duas recusas, a validação ocorre antes da alteração de estado: o número de vagas permanece igual. A falta de vagas aciona a captura específica e disponibiliza seus atributos; SessaoEncerrada é recebida pela captura da família ErroDeReserva.

Fluxo da tentativa de reserva

O mesmo método pode concluir a operação ou sinalizar uma de duas falhas previstas, deixando ao chamador a escolha da resposta.

Diagrama com uma tentativa de reserva que se divide em sucesso, vagas insuficientes e sessão encerrada; as recusas preservam o conjunto de vagas.

O tipo lançado determina o caminho de captura; a mensagem apenas explica a ocorrência.

Exemplo

O que você deve observar

Resultados essenciais: sucesso — vagas de 5 para 3; vagas insuficientes — tipo VagasInsuficientes, atributos solicitadas=4 e disponiveis=2, vagas de 2 para 2; sessão encerrada — tipo real SessaoEncerrada, captura por ErroDeReserva, vagas de 5 para 5.

Relate sua execução

Analise os resultados

Após executar o script, relate o que ocorreu nos três cenários. Inclua o tipo lançado, a captura acionada, os dados disponíveis e as vagas antes e depois. Explique também por que mudar apenas a mensagem das exceções não alteraria o tratamento.

Escreva pelo menos 120 caracteres (0/120).

Revisão final

Resumo

Competência consolidada

A solução integrada mantém separadas as responsabilidades do objeto e do chamador.

  • Uma exceção própria identifica programaticamente uma falha relevante do domínio.
  • Uma classe base permite tratar em conjunto falhas relacionadas, enquanto subclasses preservam respostas específicas.
  • A exceção pode oferecer uma mensagem legível e atributos estruturados de contexto ao mesmo tempo.
  • O método valida antes de alterar o estado e sinaliza a recusa sem decidir como ela será apresentada.
  • O chamador captura apenas as falhas previstas, do tipo mais específico para o mais geral, sem comparar mensagens.

Tutorial concluído

Parabéns! Você concluiu: Criar exceções para erros do domínio

Você concluiu “Criar exceções para erros do domínio”. Agora consegue representar falhas por tipos próprios, oferecer contexto útil e deixar a decisão de tratamento com o código chamador.

100 XP

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso