Skip to content

Latest commit

 

History

History

15-context-manager

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

README.md

Context Managers (Gerenciadores de Contexto)

Bem-vindo ao Módulo 15!

Introdução

Durante a execução de um programa, diversos recursos precisam ser abertos e posteriormente liberados. Arquivos devem ser fechados, conexões com bancos de dados precisam ser encerradas, conexões de rede devem ser finalizadas e bloqueios (locks) precisam ser liberados para evitar problemas de concorrência.

Fazer esse gerenciamento manualmente pode levar a erros, principalmente quando exceções ocorrem durante a execução do código. Para solucionar esse problema, o Python oferece os Context Managers (Gerenciadores de Contexto).

Um Context Manager garante que um recurso seja preparado antes de sua utilização e liberado corretamente ao final do bloco de código, independentemente de o processamento terminar normalmente ou devido a uma exceção.

Esse mecanismo é amplamente utilizado em aplicações profissionais para garantir segurança, organização e gerenciamento eficiente de recursos.

Neste capítulo aprenderemos como utilizar Context Managers, compreenderemos seu funcionamento interno, criaremos nossos próprios gerenciadores de contexto e conheceremos seus principais casos de uso.

Base Teórica

O que é um Context Manager?

Um Context Manager é um objeto responsável por controlar a entrada e a saída de um bloco de código.

Ele garante que determinados recursos sejam inicializados antes da execução e liberados automaticamente ao final.

A sintaxe utilizada é a instrução with.

Exemplo:

with open("dados.txt", "r") as arquivo:
    conteudo = arquivo.read()

print(conteudo)

Ao sair do bloco with, o arquivo é fechado automaticamente.

Por que Utilizar Context Managers?

Sem Context Manager:

arquivo = open("dados.txt", "r")

conteudo = arquivo.read()

arquivo.close()

Problema:

Se ocorrer uma exceção antes de close(), o arquivo permanecerá aberto.

Exemplo:

arquivo = open("dados.txt", "r")

raise Exception("Erro!")

arquivo.close()

Nesse caso, o arquivo nunca será fechado.

O Bloco with

O with é utilizado para trabalhar com Context Managers.

Sintaxe:

with contexto as recurso:
    # bloco protegido

Fluxo:

Entrar no contexto

↓

Executar bloco

↓

Sair do contexto

↓

Liberar recurso

Exemplo com Arquivos

with open("mensagem.txt", "w") as arquivo:
    arquivo.write("Olá, Python!")

Mesmo que ocorra um erro durante a escrita, o arquivo será fechado corretamente.

Exemplo com Leitura

with open("mensagem.txt") as arquivo:
    for linha in arquivo:
        print(linha.strip())

Ao final do bloco, o arquivo é fechado automaticamente.

Funcionamento Interno

Todo Context Manager implementa dois métodos especiais:

__enter__()

Executado ao entrar no bloco with.

E:

__exit__()

Executado ao sair do bloco.

Fluxo interno:

__enter__()

↓

Código dentro do with

↓

__exit__()

Criando um Context Manager

class Arquivo:

    def __enter__(self):
        print("Abrindo recurso")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("Fechando recurso")

Utilização:

with Arquivo():
    print("Utilizando recurso")

Saída:

Abrindo recurso
Utilizando recurso
Fechando recurso

O Método __enter__()

É executado antes do bloco.

class Exemplo:

    def __enter__(self):
        print("Entrou")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        pass

O valor retornado por __enter__() é atribuído à variável após o as.

with Exemplo() as objeto:
    print(objeto)

O Método __exit__()

Recebe três parâmetros relacionados às exceções:

def __exit__(self, exc_type, exc_value, traceback):
    ...

Parâmetros:

  • exc_type → tipo da exceção;
  • exc_value → objeto da exceção;
  • traceback → rastreamento da pilha.

Caso nenhuma exceção ocorra:

exc_type = None
exc_value = None
traceback = None

Tratando Exceções

class Exemplo:

    def __enter__(self):
        print("Início")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("Fim")
with Exemplo():
    raise ValueError("Erro")

Saída:

Início
Fim
Traceback...

O método __exit__() é executado mesmo quando ocorre uma exceção.

Suprimindo Exceções

Se __exit__() retornar True, a exceção será considerada tratada.

class Exemplo:

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("Erro tratado")
        return True

Context Managers com contextlib

Também é possível criar Context Managers utilizando um decorador.

from contextlib import contextmanager

@contextmanager
def recurso():
    print("Abrindo")

    yield

    print("Fechando")

Utilização:

with recurso():
    print("Executando")

Saída:

Abrindo
Executando
Fechando

Essa abordagem costuma ser mais simples quando o gerenciamento de contexto é pequeno.

Comparação

Utilizando classe:

class Contexto:

    def __enter__(self):
        ...

    def __exit__(self, *args):
        ...

Utilizando contextmanager:

@contextmanager
def contexto():
    ...
    yield
    ...

Context Managers Aninhados

with open("entrada.txt") as entrada:
    with open("saida.txt", "w") as saida:
        saida.write(entrada.read())

Também é possível escrever:

with open("entrada.txt") as entrada, open("saida.txt", "w") as saida:
    saida.write(entrada.read())

Exemplos de Uso

Arquivos

with open("dados.csv") as arquivo:
    ...

Banco de Dados

with conexao:
    ...

Locks

with lock:
    ...

Threads

with ThreadPoolExecutor() as executor:
    ...

Conexões HTTP

Diversas bibliotecas oferecem Context Managers.

with requests.Session() as sessao:
    ...

Vantagens

  • Liberação automática de recursos.
  • Código mais limpo.
  • Menor risco de vazamento de recursos.
  • Tratamento adequado de exceções.
  • Facilita manutenção.

Limitações

  • Requer implementação de __enter__() e __exit__() quando criado manualmente.
  • Nem todo objeto pode ser utilizado com with.
  • O contexto existe apenas durante o bloco.

Erros Comuns

Esquecer o with

arquivo = open("dados.txt")

Pode deixar arquivos abertos.

Não implementar __exit__()

Um Context Manager precisa implementar ambos os métodos especiais.

Suprimir exceções sem necessidade

Retornar True em __exit__() impede que exceções sejam propagadas. Isso deve ser feito apenas quando realmente desejado.

Boas Práticas

  • Utilize with sempre que trabalhar com arquivos.
  • Prefira Context Managers para recursos que precisam ser liberados.
  • Utilize contextlib.contextmanager para casos simples.
  • Evite deixar recursos abertos manualmente.
  • Não sufoque exceções sem motivo.

Casos de Uso no Mercado

Desenvolvimento Web

Gerenciamento de conexões com bancos de dados e sessões HTTP.

Ciência de Dados

Leitura de arquivos CSV, JSON e Parquet.

Engenharia de Dados

Processamento de grandes arquivos utilizando recursos temporários.

Sistemas Distribuídos

Gerenciamento de conexões com filas e serviços externos.

Computação Paralela

Controle de locks, semáforos e thread pools.

Resumo

Os Context Managers são um mecanismo que garante a correta aquisição e liberação de recursos durante a execução de um programa. Utilizando a instrução with, é possível escrever código mais seguro, limpo e robusto, evitando vazamentos de recursos mesmo quando exceções ocorrem. Eles são amplamente utilizados em operações com arquivos, bancos de dados, conexões de rede, processamento paralelo e diversas outras situações em que o gerenciamento automático de recursos é essencial.

Exercícios Propostos

  1. Utilize with para abrir um arquivo e imprimir seu conteúdo.
  2. Escreva um programa que copie o conteúdo de um arquivo para outro utilizando Context Managers.
  3. Crie uma classe que implemente __enter__() e __exit__().
  4. Modifique o Context Manager criado para exibir mensagens ao entrar e sair do contexto.
  5. Implemente um Context Manager que registre o horário de início e fim de um bloco de código.
  6. Crie um Context Manager utilizando o decorador @contextmanager.
  7. Faça um Context Manager que trate exceções e registre mensagens de erro.
  8. Utilize dois Context Managers no mesmo bloco with.
  9. Explique a diferença entre um Context Manager baseado em classe e outro baseado em contextlib.
  10. Pesquise uma biblioteca Python que implemente Context Managers e descreva um exemplo de uso.

Desafio

Desenvolva um programa chamado Monitor de Recursos utilizando Context Managers.

O sistema deverá controlar o acesso a arquivos de log e medir o tempo de execução de operações.

Funcionalidades

  1. Criar um Context Manager que registre o horário de início e término de uma operação.
  2. Abrir um arquivo de log utilizando with.
  3. Registrar automaticamente todas as mensagens produzidas durante a execução.
  4. Capturar possíveis exceções e registrar informações sobre elas.
  5. Fechar corretamente todos os recursos ao final do processamento.

Requisitos

  • Utilize a instrução with em todas as operações com arquivos.
  • Implemente um Context Manager personalizado utilizando uma classe.
  • Crie outro Context Manager utilizando @contextmanager.
  • Garanta que os recursos sejam liberados mesmo quando ocorrerem exceções.
  • Documente o funcionamento dos métodos __enter__() e __exit__().

Desafio Extra

Expanda o projeto para monitorar conexões com um banco de dados SQLite. Implemente um Context Manager responsável por abrir a conexão, iniciar uma transação, realizar operações de leitura e escrita e, ao final, efetuar commit em caso de sucesso ou rollback em caso de erro. Compare essa abordagem com o gerenciamento manual de conexões, destacando as vantagens dos Context Managers em aplicações profissionais.