Como Verificar se uma Exceção foi Capturada em Python

PythonBeginner
Pratique Agora

Introdução

Neste laboratório, você aprenderá como verificar se uma exceção foi capturada em Python usando diferentes técnicas. O laboratório se concentra no tratamento de exceções (exception handling), um aspecto crucial da escrita de código Python robusto e confiável.

O laboratório o guiará através da compreensão do fluxo de tratamento de exceções usando blocos try...except para lidar graciosamente com erros como ZeroDivisionError. Você aprenderá como definir um sinalizador (flag) dentro do bloco except para indicar que uma exceção foi capturada. Finalmente, o laboratório apresentará o conceito de uso de gerenciadores de contexto (context managers) para um tratamento de exceções mais controlado.

Aprenda o Fluxo do Tratamento de Exceções

Nesta etapa, você aprenderá sobre o tratamento de exceções em Python. O tratamento de exceções (exception handling) é uma parte crucial da escrita de código robusto e confiável. Ele permite que você lide graciosamente com erros que podem ocorrer durante a execução do seu programa, impedindo que ele trave e proporcionando uma experiência mais amigável ao usuário.

Vamos começar com um exemplo simples. Imagine que você deseja dividir dois números, mas o segundo número pode ser zero. Dividir por zero é uma operação indefinida e gerará um ZeroDivisionError em Python.

  1. Abra o editor VS Code no ambiente LabEx.

  2. Crie um novo arquivo chamado division.py no diretório ~/project.

    touch ~/project/division.py
    
  3. Edite o arquivo division.py e adicione o seguinte código:

    ## division.py
    numerator = 10
    denominator = 0
    
    result = numerator / denominator
    
    print(result)
    
  4. Execute o script usando o comando python:

    python ~/project/division.py
    

Você verá uma mensagem de erro semelhante a esta:

Traceback (most recent call last):
  File "/home/labex/project/division.py", line 4, in <module>
    result = numerator / denominator
ZeroDivisionError: division by zero

Esta mensagem de erro indica que um ZeroDivisionError ocorreu porque tentamos dividir por zero. Sem tratamento de exceções, o programa termina abruptamente.

Agora, vamos usar o tratamento de exceções para lidar com este erro graciosamente.

  1. Modifique o arquivo division.py para incluir um bloco try...except:

    ## division.py
    numerator = 10
    denominator = 0
    
    try:
        result = numerator / denominator
        print(result)
    except ZeroDivisionError:
        print("Error: Cannot divide by zero.")
    

    Neste código, o bloco try contém o código que pode gerar uma exceção. Se um ZeroDivisionError ocorrer dentro do bloco try, o código no bloco except será executado.

  2. Execute o script novamente:

    python ~/project/division.py
    

Agora, em vez de travar, o programa exibirá:

Error: Cannot divide by zero.

Isso demonstra a estrutura básica do tratamento de exceções:

  • O bloco try envolve o código que pode gerar uma exceção.
  • O bloco except especifica o tipo de exceção a ser capturada e o código a ser executado se essa exceção ocorrer.

Você também pode capturar vários tipos de exceções usando vários blocos except:

## division.py
numerator = 10
denominator = "abc"

try:
    result = numerator / int(denominator)
    print(result)
except ZeroDivisionError:
    print("Error: Cannot divide by zero.")
except ValueError:
    print("Error: Invalid input. Please enter a number.")

Neste exemplo, adicionamos um manipulador de exceção ValueError. Se o denominator não puder ser convertido em um inteiro (por exemplo, se for uma string como "abc"), um ValueError será gerado e o bloco except correspondente será executado.

Execute o script:

python ~/project/division.py

Saída:

Error: Invalid input. Please enter a number.

O tratamento de exceções permite que você escreva programas mais robustos e amigáveis ao usuário, antecipando e lidando com erros potenciais.

Defina uma Flag no Bloco except

Nesta etapa, você aprenderá como usar um sinalizador (flag) dentro de um bloco except para controlar o fluxo do seu programa com base em se uma exceção ocorreu ou não. Esta é uma técnica útil quando você precisa realizar ações diferentes dependendo do resultado do bloco try.

Com base no exemplo anterior, digamos que você queira calcular o recíproco de um número, mas somente se a divisão for bem-sucedida. Você pode usar um sinalizador para indicar se a divisão foi bem-sucedida e, em seguida, calcular o recíproco de acordo.

  1. Abra o editor VS Code no ambiente LabEx.

  2. Modifique o arquivo division.py no diretório ~/project para incluir um sinalizador:

    ## division.py
    numerator = 10
    denominator = 2
    success = True  ## Inicialize o sinalizador para True
    
    try:
        result = numerator / denominator
        print("Result of division:", result)
    except ZeroDivisionError:
        print("Error: Cannot divide by zero.")
        success = False  ## Defina o sinalizador para False se ocorrer um erro
    except ValueError:
        print("Error: Invalid input. Please enter a number.")
        success = False ## Defina o sinalizador para False se ocorrer um erro
    else:
        print("No exception occurred.")
    
    if success:
        try:
            reciprocal = 1 / result
            print("Reciprocal:", reciprocal)
        except ZeroDivisionError:
            print("Cannot calculate reciprocal of zero.")
    else:
        print("Reciprocal calculation skipped due to error.")
    

    Neste código:

    • Inicializamos um sinalizador chamado success para True antes do bloco try.
    • Se um ZeroDivisionError ou ValueError ocorrer, definimos o sinalizador success como False dentro do bloco except correspondente.
    • Após o bloco try...except, verificamos o valor do sinalizador success. Se ainda for True, significa que nenhuma exceção ocorreu e podemos calcular com segurança o recíproco. Caso contrário, ignoramos o cálculo do recíproco.
    • Também adicionamos um bloco else, que será executado se nenhuma exceção ocorrer no bloco try.
  3. Execute o script com um denominador válido:

    python ~/project/division.py
    

    Saída:

    Result of division: 5.0
    No exception occurred.
    Reciprocal: 0.2
    
  4. Agora, altere o denominator para 0 e execute o script novamente:

    ## division.py
    numerator = 10
    denominator = 0 ## Change the denominator to 0
    success = True
    
    try:
        result = numerator / denominator
        print("Result of division:", result)
    except ZeroDivisionError:
        print("Error: Cannot divide by zero.")
        success = False
    except ValueError:
        print("Error: Invalid input. Please enter a number.")
        success = False
    else:
        print("No exception occurred.")
    
    if success:
        try:
            reciprocal = 1 / result
            print("Reciprocal:", reciprocal)
        except ZeroDivisionError:
            print("Cannot calculate reciprocal of zero.")
    else:
        print("Reciprocal calculation skipped due to error.")
    

    Execute o script:

    python ~/project/division.py
    

    Saída:

    Error: Cannot divide by zero.
    Reciprocal calculation skipped due to error.
    

Isso demonstra como você pode usar um sinalizador no bloco except para controlar o fluxo do seu programa e realizar ações diferentes com base em se uma exceção ocorreu ou não.

Use Gerenciadores de Contexto para Controle

Nesta etapa, você aprenderá como usar gerenciadores de contexto (context managers) em Python para simplificar o tratamento de exceções e garantir que os recursos sejam gerenciados adequadamente, mesmo que ocorram exceções. Gerenciadores de contexto são particularmente úteis ao trabalhar com arquivos, conexões de rede e outros recursos que precisam ser explicitamente fechados ou liberados.

Um gerenciador de contexto é um objeto que define os métodos __enter__ e __exit__. A instrução with é usada para executar um bloco de código dentro do contexto de um gerenciador de contexto. O método __enter__ é chamado quando o bloco with é inserido, e o método __exit__ é chamado quando o bloco with é saído, independentemente de uma exceção ter ocorrido.

Vamos começar com um exemplo simples de leitura de um arquivo usando um gerenciador de contexto.

  1. Abra o editor VS Code no ambiente LabEx.

  2. Crie um novo arquivo chamado file_handling.py no diretório ~/project.

    touch ~/project/file_handling.py
    
  3. Edite o arquivo file_handling.py e adicione o seguinte código:

    ## file_handling.py
    
    filename = "my_file.txt"
    
    try:
        with open(filename, "w") as f:
            f.write("Hello, LabEx!\n")
            f.write("This is a test file.\n")
    except IOError as e:
        print(f"An I/O error occurred: {e}")
    
    try:
        with open(filename, "r") as f:
            content = f.read()
            print("File content:")
            print(content)
    except FileNotFoundError:
        print(f"The file '{filename}' was not found.")
    except IOError as e:
        print(f"An I/O error occurred: {e}")
    

    Neste código:

    • Usamos a instrução with open(filename, "w") as f: para abrir o arquivo my_file.txt no modo de escrita ("w"). A função open() retorna um objeto de arquivo, que é atribuído à variável f.
    • A instrução with garante que o arquivo seja fechado automaticamente quando o bloco é saído, mesmo que ocorra uma exceção.
    • Escrevemos duas linhas de texto no arquivo usando o método f.write().
    • Em seguida, usamos a instrução with open(filename, "r") as f: para abrir o mesmo arquivo no modo de leitura ("r").
    • Lemos todo o conteúdo do arquivo usando o método f.read() e o imprimimos no console.
    • Envolvemos as operações de arquivo em blocos try...except para lidar com possíveis exceções FileNotFoundError e IOError.
  4. Execute o script:

    python ~/project/file_handling.py
    

    Saída:

    File content:
    Hello, LabEx!
    This is a test file.
    

    Se o arquivo my_file.txt não existir, o script o criará e escreverá o conteúdo especificado. Se o arquivo já existir, seu conteúdo será substituído. A instrução with garante que o arquivo seja fechado corretamente em ambos os casos.

Vamos considerar outro exemplo em que uma exceção pode ocorrer durante o processamento do arquivo:

## file_handling.py
filename = "my_file.txt"

try:
    with open(filename, "r") as f:
        for line in f:
            ## Simulate an error during processing
            if "test" in line:
                raise ValueError("Simulated error during processing")
            print("Line:", line.strip())
except FileNotFoundError:
    print(f"The file '{filename}' was not found.")
except ValueError as e:
    print(f"A value error occurred: {e}")
except IOError as e:
    print(f"An I/O error occurred: {e}")

Neste exemplo, simulamos um erro gerando um ValueError se a linha contiver a palavra "test". Mesmo que esse erro ocorra, a instrução with garante que o arquivo seja fechado corretamente.

Execute o script:

python ~/project/file_handling.py

Saída:

Line: Hello, LabEx!
A value error occurred: Simulated error during processing

Gerenciadores de contexto fornecem uma maneira limpa e confiável de gerenciar recursos e lidar com exceções em Python. Eles garantem que os recursos sejam liberados corretamente, mesmo que ocorram erros, tornando seu código mais robusto e fácil de manter.

Resumo

Neste laboratório, você começa aprendendo sobre tratamento de exceções (exception handling) em Python, uma técnica crucial para escrever código robusto. Você começa com um exemplo simples de divisão que lança um ZeroDivisionError ao dividir por zero, fazendo com que o programa seja encerrado. Em seguida, você implementa um bloco try...except para lidar graciosamente com o ZeroDivisionError, impedindo que o programa trave e imprimindo, em vez disso, uma mensagem de erro informativa.