O módulo shelve permite persistir objetos Python em um arquivo semelhante a um dicionário. Ele é útil quando você precisa guardar pequenas quantidades de dados entre execuções sem instalar um banco de dados. Embora simples, exige cuidados com segurança, concorrência e compatibilidade.
Quando usar shelve
Use shelve em scripts locais, protótipos, ferramentas pessoais, caches pequenos e aplicações de linha de comando. Ele oferece uma interface familiar: você abre uma prateleira, grava valores por chave e fecha o arquivo. Para sistemas web, múltiplos processos ou dados críticos, prefira SQLite, PostgreSQL ou outro banco transacional.
Exemplo básico
import shelve
with shelve.open("dados_app") as db:
db["usuario"] = {"nome": "Ana", "nivel": 3}
db["tema"] = "escuro"
with shelve.open("dados_app") as db:
print(db["usuario"])
O contexto with garante o fechamento correto. Dependendo do sistema, vários arquivos podem ser criados com extensões diferentes. Por isso, trate o nome passado ao open como uma base, e não como um arquivo único.
Como funciona por dentro
shelve combina um banco chave-valor fornecido pelo módulo dbm com serialização via pickle. As chaves são strings. Os valores podem ser listas, dicionários, classes e outros objetos serializáveis. Essa flexibilidade é conveniente, mas significa que o conteúdo não é legível como JSON e não deve ser aberto a partir de fontes não confiáveis.
Atualização de objetos mutáveis
Uma armadilha comum ocorre ao alterar listas ou dicionários recuperados. Em geral, você deve atribuir o objeto de volta para que a mudança seja gravada.
with shelve.open("dados_app") as db:
perfil = db["usuario"]
perfil["nivel"] += 1
db["usuario"] = perfil
Também existe a opção writeback=True, que mantém objetos acessados em cache e grava alterações ao fechar. Ela simplifica o código, mas pode consumir muita memória e aumentar o tempo de fechamento.
with shelve.open("dados_app", writeback=True) as db:
db["usuario"]["nivel"] += 1
Use writeback apenas quando o volume for pequeno e você entender o custo. Em aplicações maiores, a atribuição explícita é mais previsível.
Operações úteis
A prateleira implementa operações de dicionário como keys(), values(), items(), get(), pop() e o operador in. Ainda assim, iterar por toda a base pode ser caro, porque cada valor precisa ser desserializado.
with shelve.open("dados_app") as db:
if "contador" not in db:
db["contador"] = 0
db["contador"] = db.get("contador", 0) + 1
for chave in db.keys():
print(chave)
Flags de abertura
O parâmetro flag controla o comportamento de abertura. O valor padrão c abre para leitura e escrita, criando a base se necessário. r abre somente para leitura e exige que a base exista. w abre para leitura e escrita sem criar uma nova base. n sempre cria uma base vazia, apagando a anterior.
Em rotinas de leitura, prefira r para evitar criação acidental. Use n apenas em processos de reconstrução ou testes.
Segurança
Nunca abra uma prateleira recebida de usuários, baixada da internet ou modificada por terceiros. Como os valores usam pickle, dados maliciosos podem executar código durante a leitura. Mantenha os arquivos em diretórios controlados, com permissões restritas e fora de pastas públicas.
Também não armazene segredos sem criptografia. shelve não cifra o conteúdo. Tokens, senhas e chaves de API devem ficar em um gerenciador de segredos ou em armazenamento protegido.
Concorrência e integridade
O módulo não oferece uma estratégia portátil de bloqueio para múltiplos escritores. Duas instâncias gravando ao mesmo tempo podem corromper a base. A regra mais segura é ter apenas um processo escritor. Se precisar compartilhar dados entre processos, use SQLite ou um serviço de banco de dados.
Mesmo com um único escritor, interrupções bruscas podem deixar arquivos inconsistentes. Faça cópias de segurança antes de alterações importantes e considere gravar em uma base temporária antes de substituir a versão principal.
Compatibilidade e migração
A implementação dbm varia entre sistemas operacionais. Um conjunto de arquivos criado em uma máquina pode não abrir em outra. Além disso, objetos pickle dependem de classes, módulos e versões compatíveis. Se você renomear uma classe ou alterar sua estrutura, valores antigos podem falhar.
Para dados que precisam durar anos, viajar entre plataformas ou ser lidos por outras linguagens, use formatos como JSON, CSV ou um banco estável. shelve é melhor como solução local e controlada.
Padrão de repositório simples
from pathlib import Path
import shelve
class Repositorio:
def __init__(self, caminho: Path):
self.caminho = str(caminho)
def salvar(self, chave: str, valor) -> None:
with shelve.open(self.caminho) as db:
db[chave] = valor
def obter(self, chave: str, padrao=None):
with shelve.open(self.caminho, flag="c") as db:
return db.get(chave, padrao)
def remover(self, chave: str) -> bool:
with shelve.open(self.caminho) as db:
if chave not in db:
return False
del db[chave]
return True
Encapsular o acesso reduz erros, centraliza o caminho e facilita uma futura migração para SQLite. Também permite adicionar validação, logs e cópias de segurança sem espalhar detalhes pelo projeto.
Testes
Nos testes, use um diretório temporário com tempfile.TemporaryDirectory. Assim você evita poluir o projeto e garante isolamento.
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as pasta:
repo = Repositorio(Path(pasta) / "teste")
repo.salvar("x", {"valor": 10})
assert repo.obter("x")["valor"] == 10
Boas práticas
Use chaves estáveis e documentadas. Valide os objetos antes de gravar. Feche sempre a base com with. Evite writeback em conjuntos grandes. Não compartilhe um escritor entre processos. Mantenha backups quando os dados tiverem valor. Planeje uma migração quando o projeto crescer.
Alternativas
JSON é melhor para dados simples e portáveis. SQLite oferece transações, consultas e concorrência mais segura. dbm armazena bytes e exige serialização manual. Bibliotecas como SQLModel e SQLAlchemy são adequadas quando o domínio cresce.
Conclusão
shelve é uma ferramenta prática para persistência local com uma API de dicionário. Ela reduz código em pequenos projetos, mas não substitui um banco de dados. Use-a em ambientes controlados, nunca confie em arquivos externos e migre para uma solução transacional quando surgirem múltiplos processos, consultas ou requisitos fortes de durabilidade.
Leia também zipfile no Python, tempfile no Python, tomllib no Python e copy no Python. Consulte a documentação oficial de shelve e o aviso de segurança do pickle.
Antes de adotar a solução, registre quais dados serão persistidos, por quanto tempo e como serão recuperados em caso de falha. Essa pequena decisão arquitetural evita que um protótipo vire dependência crítica sem monitoramento. Inclua uma rotina de exportação para JSON e teste periodicamente a restauração em outra máquina.







