1
resposta

[Desafio] Hora da prática (5-7)

Abaixo, compartilho como estruturei as soluções da seção "Aplicando a projetos", sugestões de melhorias são bem vindas!

  1. Validação de Notas e Alternativas (Lançando Exceções Customizadas)

A grande sacada desse exercício é usar a palavra reservada raise. Em vez de apenas dar um print de erro, o raise força o programa a entender que uma regra de negócio foi violada e interrompe o fluxo imediatamente, repassando o erro para quem chamou a função.

def avalia_testes(testes, gabarito):
    opcoes_validas = ['A', 'B', 'C', 'D']
    notas_finais = []
    
    for teste in testes:
        nota_aluno = 0
        for i in range(len(teste)):
            # Se a resposta não for válida, lançamos a exceção e paramos o processo
            if teste[i] not in opcoes_validas:
                raise ValueError(f"A alternativa {teste[i]} não é uma opção de alternativa válida")
            
            # Se for válida e bater com o gabarito, soma 1 ponto
            if teste[i] == gabarito[i]:
                nota_aluno += 1
                
        notas_finais.append(nota_aluno)
        
    return notas_finais

# --- Dados de Teste ---
gabarito = ['D', 'A', 'B', 'C', 'A']
testes_sem_ex = [['D', 'A', 'B', 'C', 'A'], ['C', 'A', 'A', 'C', 'A'], ['D', 'B', 'A', 'C', 'A']]
testes_com_ex = [['D', 'A', 'B', 'C', 'A'], ['C', 'A', 'A', 'E', 'A'], ['D', 'B', 'A', 'C', 'A']]

# Executando com Try/Except para capturar o erro de forma elegante
try:
    print("Resultado da turma 1 (Sem Exceção):", avalia_testes(testes_sem_ex, gabarito))
    print("Resultado da turma 2 (Com Exceção):", avalia_testes(testes_com_ex, gabarito))
except ValueError as e:
    print(f"Erro detectado na validação: {e}")

  1. NLP e Higienização de Texto

Aqui, usamos o laço para iterar tanto pelas palavras quanto pelas pontuações. Assim que a primeira pontuação irregular é detectada, o raise ValueError entra em ação, paralisando a análise.

def verifica_pontuacao(lista_palavras):
    pontuacoes = [',', '.', '!', '?']
    
    for palavra in lista_palavras:
        for p in pontuacoes:
            if p in palavra:
                raise ValueError(f'O texto apresenta pontuações na palavra "{palavra}".')
                
    return "Texto validado com sucesso! Nenhuma pontuação encontrada."

# --- Dados de Teste ---
lista_tratada = ['Python', 'é', 'uma', 'linguagem', 'de', 'programação', 'poderosa', 'versátil']
lista_nao_tratada = ['Python', 'é', 'uma', 'linguagem', 'poderosa,', 'versátil!']

try:
    print(verifica_pontuacao(lista_tratada))
    print(verifica_pontuacao(lista_nao_tratada)) # Essa linha vai acionar a exceção
except ValueError as e:
    print(f"Falha de Higienização NLP: {e}")

  1. Monitoramento da Cultura de Fungos (Múltiplas Exceções)

Esse é um cenário clássico: cruzar listas de dados (zip) sujeitas a inconsistências de tamanho e a cálculos impossíveis (divisão por zero). Agrupar múltiplos except permite que o programa dê uma mensagem específica para cada tipo de problema.

def divide_colunas(pressoes, temperaturas):
    try:
        # 1ª Validação: Tamanhos diferentes
        if len(pressoes) != len(temperaturas):
            raise ValueError("A quantidade de dados de pressão e temperatura é diferente.")
        
        resultados = []
        
        # 2ª Validação: O próprio Python lançará ZeroDivisionError se t == 0
        for p, t in zip(pressoes, temperaturas):
            razao = round(p / t, 2)
            resultados.append(razao)
            
        return resultados

    except ValueError as ve:
        print(f"Erro de Integridade dos Dados (ValueError): {ve}")
    except ZeroDivisionError:
        print("Erro de Cálculo (ZeroDivisionError): Tentativa de divisão por zero na temperatura.")
    except Exception as e:
        # Boa prática: capturar qualquer outro erro imprevisto
        print(f"Erro inesperado: {e}")

# --- Dados de Teste ---
pressoes_ok = [100, 120, 140, 160, 180]
temperaturas_ok = [20, 25, 30, 35, 40]

pressoes_zero = [60, 120, 140, 160, 180]
temperaturas_zero = [0, 25, 30, 35, 40]

pressoes_tam_dif = [100, 120, 140, 160]
temperaturas_tam_dif = [20, 25, 30, 35, 40]

print("--- Teste 1: Dados Corretos ---")
print(divide_colunas(pressoes_ok, temperaturas_ok))

print("\n--- Teste 2: Erro de Divisão por Zero ---")
divide_colunas(pressoes_zero, temperaturas_zero)

print("\n--- Teste 3: Erro de Tamanho das Listas ---")
divide_colunas(pressoes_tam_dif, temperaturas_tam_dif)

O que acharam das soluções? O uso do try/except é um caminho sem volta para criarmos aplicações que saibam conversar com o usuário quando algo dá errado, em vez de apenas travar a tela com um erro de terminal.

Bons estudos!

1 resposta

Olá, Weriton. Como vai?

Excelente contribuição e parabéns pela qualidade do seu código! Você demonstram um entendimento impecável sobre o tratamento de erros em Python. Como você bem mencionou, o uso do raise aliado a blocos de try/except estruturados muda completamente o patamar da sua aplicação, transformando códigos frágeis em softwares prontos para rodar em produção.

Suas três soluções cobriram perfeitamente cenários reais de engenharia e análise de dados: validação de regras de negócio internas, higienização de strings em NLP e controle de integridade de dados científicos com múltiplas exceções.

Como você abriu o espaço para sugestões de melhorias e boas práticas, separei alguns pontos de refatoração bem sutis que podem deixar seu código ainda mais performático, limpo e legível.

1. Validação de Notas: Evitando range(len())

No Python, uma prática altamente recomendada para manter o código elegante (conhecido como estilo pythônico) é evitar o uso de indexação numérica como for i in range(len(teste)) quando você precisa apenas do valor e da posição dos elementos ao mesmo tempo. Você pode utilizar a função nativa zip para iterar simultaneamente sobre a resposta do aluno e o gabarito:

def avalia_testes(testes, gabarito):
    opcoes_validas = ['A', 'B', 'C', 'D']
    notas_finais = []
    
    for teste in testes:
        nota_aluno = 0
        # Iterando diretamente sobre os dois elementos ao mesmo tempo
        for resposta, correta in zip(teste, gabarito):
            if resposta not in opcoes_validas:
                raise ValueError(f"A alternativa {resposta} não é uma opção de alternativa válida")
            
            if resposta == correta:
                nota_aluno += 1
                
        notas_finais.append(nota_aluno)
        
    return notas_finais

2. NLP e Higienização: Otimizando Busca em Strings

Na função verifica_pontuacao, você utilizou dois laços de repetição aninhados (um for dentro de outro for). Para listas ou textos gigantescos, isso pode afetar a performance do seu processamento de linguagem natural.

Uma alternativa elegante e rápida em Python é inverter a ordem da validação usando o operador any() ou checar se a interseção dos caracteres existe. Mas, mantendo a simplicidade, podemos apenas verificar se algum caractere de pontuação está contido na palavra invertendo a busca com o operador in, eliminando um nível de indentação:

def verifica_pontuacao(lista_palavras):
    pontuacoes = [',', '.', '!', '?']
    
    for palavra in lista_palavras:
        # Verifica se alguma das pontuações está presente na palavra
        if any(p in palavra for p in pontuacoes):
            raise ValueError(f'O texto apresenta pontuações na palavra "{palavra}".')
                
    return "Texto validado com sucesso! Nenhuma pontuação encontrada."

3. Monitoramento da Cultura: O Princípio da Responsabilidade Única (SOLID)

A sua terceira função está excelente e o uso de múltiplos except com a captura genérica Exception as e no final é uma grande prática recomendada na indústria.

A única sugestão conceitual aqui é separar o bloco de captura try/except de dentro do escopo da função matemática. O ideal é deixar a sua função divide_colunas apenas com a lógica do cálculo e o lançamento do raise, delegando a responsabilidade de "capturar" o erro para quem está chamando a função (exatamente como você fez no primeiro exercício). Isso deixa suas funções puras e fáceis de serem reutilizadas em outras partes do sistema.

Veja como essa separação de responsabilidades deixa a arquitetura do seu código limpa:

def divide_colunas(pressoes, temperaturas):
    # A função apenas valida e calcula. Se algo der errado, ela lança o erro.
    if len(pressoes) != len(temperaturas):
        raise ValueError("A quantidade de dados de pressão e temperatura é diferente.")
    
    resultados = []
    for p, t in zip(pressoes, temperaturas):
        razao = round(p / t, 2)
        resultados.append(razao)
        
    return resultados

# Quem chama a função é quem decide como tratar o erro (ex: exibindo no terminal)
try:
    print(divide_colunas(pressoes_zero, temperaturas_zero))
except ValueError as ve:
    print(f"Erro de Integridade: {ve}")
except ZeroDivisionError:
    print("Erro de Cálculo: Divisão por zero encontrada.")

Compartilhar códigos estruturados com essa qualidade enriquece muito o fórum e ajuda outros estudantes a entenderem a importância de criar códigos defensivos. Parabéns pelo nível de entrega!

Espero que possa ter lhe ajudado!