Fundamentos do Python para Iniciantes

0/31 aulas0%
teoria

Tipos Comuns de Exceções e Como Lidar com Elas

Aprenda sobre tipos comuns de exceções e como lidar com elas

30 min
Aula 2 de 6

Tipos Comuns de Exceções e Como Lidar com Elas

Olá, futuros desenvolvedores Python! 👋 Nesta aula, vamos mergulhar em um aspecto crucial da programação robusta: o tratamento de exceções. Entender e saber lidar com erros que podem ocorrer durante a execução do seu código é o que diferencia um programa que falha inesperadamente de um que se recupera elegantemente.

1. Introdução: O Que São Exceções e Por Que Importam?

Imagine que você está escrevendo um programa que pede ao usuário para digitar um número e, em seguida, divide outro número por ele. E se o usuário digitar texto em vez de um número? Ou pior, e se ele digitar 0? Seu programa pode "quebrar", exibindo uma mensagem de erro assustadora e parando de funcionar. 😱

É exatamente para evitar esses cenários que o tratamento de exceções existe.

Uma exceção é um evento que interrompe o fluxo normal de um programa. Em Python, quando ocorre um erro que não é tratado, o interpretador "levanta" (ou raises) uma exceção. Se essa exceção não for "capturada" (ou handled), o programa termina.

Por que é importante lidar com elas?

  • Robustez: Seu programa não trava por entradas inválidas ou condições inesperadas.
  • Experiência do Usuário (UX): Em vez de uma falha abrupta, o usuário recebe uma mensagem clara e pode tentar novamente.
  • Manutenibilidade: Facilita a depuração e o entendimento de onde e por que os erros ocorrem.

Python nos oferece a estrutura try-except para lidar com exceções. Vamos explorar os tipos mais comuns!

2. Explicação Detalhada com Exemplos

A sintaxe básica para lidar com exceções é:

try:
    # Código que pode levantar uma exceção
    pass
except TipoDeExcecao:
    # Código que será executado se TipoDeExcecao ocorrer
    pass

Vamos ver alguns dos tipos de exceções mais frequentes e como você pode gerenciá-los.


2.1. NameError: Nome Não Definido

Esta exceção ocorre quando você tenta usar uma variável ou função que não foi definida (ou está fora do escopo atual).

Exemplo sem tratamento:

print(minha_variavel_inexistente)

Saída esperada:

NameError: name 'minha_variavel_inexistente' is not defined

Como lidar:

try:
    print(minha_variavel_inexistente)
except NameError:
    print("Erro: Você tentou usar uma variável ou função que não foi definida.")
    # Você pode definir a variável aqui ou pedir ao usuário para corrigir
    minha_variavel_inexistente = "definida agora!"
    print(f"Variável agora: {minha_variavel_inexistente}")

2.2. TypeError: Operação de Tipo Inválido

Acontece quando uma operação ou função é aplicada a um objeto de um tipo inadequado. Por exemplo, tentar somar um número com uma string.

Exemplo sem tratamento:

resultado = "Olá" + 5

Saída esperada:

TypeError: can only concatenate str (not "int") to str

Como lidar:

try:
    resultado = "Olá" + 5
except TypeError as e: # 'as e' captura a mensagem de erro
    print(f"Erro de Tipo: Não é possível combinar tipos incompatíveis. Detalhes: {e}")
    resultado = "Olá" + str(5) # Converte para um tipo compatível
    print(f"Resultado corrigido: {resultado}")

2.3. ValueError: Valor Inapropriado

Ocorre quando uma operação ou função recebe um argumento do tipo correto, mas com um valor inadequado. Um exemplo clássico é tentar converter uma string não numérica para um inteiro.

Exemplo sem tratamento:

numero = int("abc")

Saída esperada:

ValueError: invalid literal for int() with base 10: 'abc'

Como lidar:

while True:
    try:
        entrada = input("Digite um número inteiro: ")
        numero = int(entrada)
        print(f"Você digitou o número: {numero}")
        break # Sai do loop se a conversão for bem-sucedida
    except ValueError:
        print("Erro de Valor: Entrada inválida. Por favor, digite um número inteiro.")

2.4. ZeroDivisionError: Divisão por Zero

Como o próprio nome sugere, esta exceção é levantada quando você tenta dividir um número por zero. 🤯

Exemplo sem tratamento:

resultado = 10 / 0

Saída esperada:

ZeroDivisionError: division by zero

Como lidar:

try:
    dividendo = float(input("Digite o dividendo: "))
    divisor = float(input("Digite o divisor: "))
    resultado = dividendo / divisor
    print(f"O resultado da divisão é: {resultado}")
except ZeroDivisionError:
    print("Erro: Não é possível dividir por zero!")
except ValueError: # Também podemos pegar ValueError aqui!
    print("Erro: Por favor, digite apenas números.")

2.5. IndexError: Índice Fora do Alcance

Esta exceção ocorre quando você tenta acessar um índice que está fora dos limites de uma sequência (como uma lista, tupla ou string).

Exemplo sem tratamento:

minha_lista = [10, 20, 30]
print(minha_lista[3]) # A lista tem índices 0, 1, 2

Saída esperada:

IndexError: list index out of range

Como lidar:

minha_lista = [10, 20, 30]
try:
    indice = int(input("Digite um índice (0-2): "))
    print(f"Elemento no índice {indice}: {minha_lista[indice]}")
except IndexError:
    print(f"Erro de Índice: O índice {indice} está fora dos limites da lista.")
except ValueError:
    print("Erro: Por favor, digite um número inteiro para o índice.")

2.6. KeyError: Chave Não Encontrada em Dicionário

Acontece quando você tenta acessar uma chave que não existe em um dicionário.

Exemplo sem tratamento:

meu_dicionario = {"nome": "Alice", "idade": 30}
print(meu_dicionario["cidade"])

Saída esperada:

KeyError: 'cidade'

Como lidar:

meu_dicionario = {"nome": "Alice", "idade": 30}
try:
    chave = input("Qual informação você quer (nome/idade/cidade)? ")
    print(f"Valor para '{chave}': {meu_dicionario[chave]}")
except KeyError:
    print(f"Erro de Chave: A chave '{chave}' não foi encontrada no dicionário.")
    # Você pode usar .get() para evitar KeyError se um valor padrão for aceitável
    print(f"Usando .get(): {meu_dicionario.get(chave, 'Chave não existe')}")

2.7. FileNotFoundError: Arquivo Não Encontrado

Esta exceção é levantada quando um arquivo ou diretório especificado não pode ser encontrado.

Exemplo sem tratamento:

with open("arquivo_que_nao_existe.txt", "r") as f:
    conteudo = f.read()

Saída esperada:

FileNotFoundError: [Errno 2] No such file or directory: 'arquivo_que_nao_existe.txt'

Como lidar:

try:
    with open("meu_arquivo.txt", "r") as f:
        conteudo = f.read()
        print("Conteúdo do arquivo:")
        print(conteudo)
except FileNotFoundError:
    print("Erro: O arquivo 'meu_arquivo.txt' não foi encontrado.")
    print("Criando o arquivo e adicionando um conteúdo padrão...")
    with open("meu_arquivo.txt", "w") as f:
        f.write("Este é um arquivo de exemplo criado automaticamente.\n")
    print("Arquivo 'meu_arquivo.txt' criado com sucesso!")
except Exception as e: # Captura outras exceções que possam ocorrer ao abrir/ler
    print(f"Ocorreu um erro inesperado ao lidar com o arquivo: {e}")

Observação: Para testar o FileNotFoundError, certifique-se de que meu_arquivo.txt realmente não exista antes da primeira execução.


2.8. AttributeError: Atributo Inválido

Ocorre quando você tenta acessar um atributo ou método que não existe em um objeto.

Exemplo sem tratamento:

numero = 10
numero.append(5) # 'int' não tem o método 'append'

Saída esperada:

AttributeError: 'int' object has no attribute 'append'

Como lidar:

class MinhaClasse:
    def __init__(self, valor):
        self.valor = valor
 
    def mostrar_valor(self):
        print(f"O valor é: {self.valor}")
 
objeto = MinhaClasse(100)
try:
    objeto.metodo_inexistente()
except AttributeError as e:
    print(f"Erro de Atributo: Você tentou chamar um método ou acessar um atributo que não existe. Detalhes: {e}")
    objeto.mostrar_valor() # Chama um método existente como alternativa

2.9. Capturando Múltiplas Exceções

Você pode capturar várias exceções no mesmo bloco except, seja especificando-as individualmente ou usando uma tupla.

try:
    num1 = int(input("Digite o primeiro número: "))
    num2 = int(input("Digite o segundo número: "))
    resultado = num1 / num2
    print(f"Resultado: {resultado}")
except (ValueError, ZeroDivisionError) as e: # Captura ValueError OU ZeroDivisionError
    print(f"Ocorreu um erro: {e}")
    print("Verifique se você digitou números válidos e se o segundo número não é zero.")

Ou, de forma mais específica:

try:
    num1 = int(input("Digite o primeiro número: "))
    num2 = int(input("Digite o segundo número: "))
    resultado = num1 / num2
    print(f"Resultado: {resultado}")
except ValueError:
    print("Erro: Entrada inválida. Por favor, digite apenas números inteiros.")
except ZeroDivisionError:
    print("Erro: Não é possível dividir por zero!")
except Exception as e: # Captura qualquer outra exceção não tratada acima
    print(f"Ocorreu um erro inesperado: {e}")

⚠️ Importante: A ordem dos blocos except importa! Exceções mais específicas devem vir antes de exceções mais genéricas (Exception é a mais genérica de todas). Se Exception viesse primeiro, ela capturaria tudo e as exceções específicas nunca seriam alcançadas.


2.10. else e finally

Além do try e except, você pode usar os blocos else e finally para um controle ainda maior.

  • else: O código dentro do bloco else é executado apenas se o bloco try for concluído sem levantar nenhuma exceção. É útil para código que deve ser executado se e somente se o try for bem-sucedido.
  • finally: O código dentro do bloco finally é sempre executado, independentemente de uma exceção ter sido levantada ou não, e de ela ter sido tratada ou não. É ideal para operações de limpeza, como fechar arquivos ou conexões de banco de dados.

Exemplo com else e finally:

try:
    arquivo = open("dados.txt", "r")
    conteudo = arquivo.read()
except FileNotFoundError:
    print("Arquivo não encontrado. Criando um novo.")
    arquivo = open("dados.txt", "w")
    arquivo.write("Dados de teste\n")
    conteudo = "Dados de teste (recém-criado)"
except Exception as e:
    print(f"Ocorreu um erro inesperado: {e}")
else: # Executa se o bloco try for bem-sucedido (sem exceções)
    print("Leitura do arquivo bem-sucedida!")
    print(f"Conteúdo: {conteudo}")
finally: # SEMPRE executa
    if 'arquivo' in locals() and not arquivo.closed: # Verifica se 'arquivo' foi definido e está aberto
        arquivo.close()
        print("Arquivo fechado.")
    else:
        print("Nenhum arquivo foi aberto ou já estava fechado.")
 
print("Programa continua a execução.")

Para testar este exemplo, você pode rodar uma vez sem dados.txt e depois com ele.

3. Exercícios/Desafios Teóricos

Para consolidar seu aprendizado, responda às seguintes perguntas:

  1. Qual é a principal diferença entre ValueError e TypeError? Dê um exemplo para cada.
  2. Por que é considerado uma boa prática colocar blocos except mais específicos antes de blocos except mais genéricos (como except Exception)?
  3. Em qual cenário o bloco else de um try-except-else-finally é executado? E o bloco finally?
  4. Se você está lendo dados de um arquivo e quer garantir que ele seja fechado, mesmo que ocorra um erro de leitura, qual bloco você usaria para fechar o arquivo?
  5. Considere o seguinte código:
    def dividir_numeros(a, b):
        return a / b
     
    try:
        resultado = dividir_numeros(10, 0)
        print(resultado)
    except ZeroDivisionError:
        print("Divisão por zero não permitida.")
    except Exception as e:
        print(f"Ocorreu um erro inesperado: {e}")
    finally:
        print("Fim da tentativa de divisão.")
    Qual será a saída exata deste código?

4. Resumo e Próximos Passos

Parabéns! 🎉 Você deu um grande passo para escrever código Python mais robusto e confiável.

Nesta aula, vimos:

  • O que são exceções e por que o tratamento de exceções é fundamental.
  • Como usar o bloco try-except para capturar e lidar com erros.
  • Uma variedade de tipos de exceções comuns, como NameError, TypeError, ValueError, ZeroDivisionError, IndexError, KeyError, FileNotFoundError e AttributeError.
  • Como capturar múltiplas exceções e a importância da ordem dos blocos except.
  • O uso dos blocos else e finally para controle adicional do fluxo do programa.

Lembre-se, o tratamento de exceções não é apenas sobre "consertar" erros, mas sobre prever problemas e guiar seu programa e seu usuário através deles de forma elegante.

No nosso próximo módulo, vamos dar um salto para o mundo da Programação Orientada a Objetos (OOP), onde você aprenderá a estruturar seu código de uma forma ainda mais poderosa e organizada. Fique ligado! 🚀

© 2025 Escola All Dev. Todos os direitos reservados.

Tipos Comuns de Exceções e Como Lidar com Elas - Fundamentos do Python para Iniciantes | escola.all.dev.br