Instale dois projetos Python na mesma máquina e o segundo pode quebrar o primeiro. Um precisa de requests==2.28, o outro de requests==2.32, e o pip tem exatamente um diretório site-packages global para escrever. A última instalação vence, em silêncio. Você descobre quando o outro projeto começa a lançar erros que não têm nada a ver com o código que você acabou de alterar.
Um ambiente virtual dá a cada projeto o seu próprio site-packages. Nada global é tocado, e dois projetos podem depender de versões incompatíveis da mesma biblioteca sem que nenhum perceba o outro.
O que é um ambiente virtual Python?
Um venv é um diretório com três coisas: um interpretador Python (um symlink para o do sistema no Linux e macOS), um arquivo pyvenv.cfg que aponta para essa instalação base, e uma pasta site-packages onde o pip install grava os pacotes. Ativá-lo coloca o diretório bin do venv (Scripts no Windows) no início do PATH do seu shell, de forma que python e pip resolvam para as cópias do venv, não para as do sistema.
Esse é todo o mecanismo. Não há isolamento de sistema de arquivos nem uma segunda cópia do Python. Um venv só muda onde o shell encontra dois binários e de onde os imports são resolvidos, por isso criar um leva um segundo e apagar um é rm -rf .venv. Se você precisa isolar também o sistema operacional e o sistema de arquivos, esse é trabalho para um container, não para um venv.
Como criar e ativar um venv?
A biblioteca padrão já inclui o módulo venv, então não há nada para instalar:
| |
.venv é o nome convencional, e o uv e a maioria dos editores procuram por ele por padrão. A ativação depende do seu shell:
| OS / shell | Comando |
|---|---|
| Linux / macOS (bash, zsh) | source .venv/bin/activate |
| Windows (cmd.exe) | .venv\Scripts\activate.bat |
| Windows (PowerShell) | .venv\Scripts\Activate.ps1 |
Uma instalação nova do Windows bloqueia scripts do PowerShell, incluindo o de ativação. Permita scripts criados localmente uma vez por usuário:
| |
Com o venv ativo, seu prompt ganha o prefixo (.venv) e which python (where python no Windows) aponta para dentro de .venv. Execute deactivate para voltar. É uma função do shell definida pelo script de ativação, não um programa separado.
A ativação é opcional. .venv/bin/python script.py e .venv/bin/pip install requests funcionam sem ela, e é assim que pipelines de CI e editores chamam o venv por baixo dos panos.
Com mais de uma versão do Python instalada, python -m venv usa o primeiro python que o shell encontrar, que muitas vezes não é o que você queria. Especifique a versão: python3.12 -m venv .venv no Linux e macOS. No Windows, use o launcher py que vem com o Python, que escolhe uma versão pelo número independente do PATH: py -3.12 -m venv .venv.
Como instalar pacotes com pip e requirements.txt?
Dentro de um venv ativo, pip install <package> faz o que você espera. Para reprodutibilidade, o que importa é registrar o que foi instalado:
| |
pip freeze grava cada pacote instalado com sua versão exata, incluindo as dependências das suas dependências. Faça commit do requirements.txt, e qualquer pessoa que clonar o projeto executa:
| |
e obtém as mesmas versões que você tinha. O ponto fraco: o arquivo mistura o que você pediu com tudo que o pip trouxe de carona. Seis meses depois, separar “eu preciso do Flask” de “o Flask precisava do Werkzeug 3.1.3” significa ler a lista inteira e adivinhar.
pyproject.toml ou requirements.txt: qual é a diferença?
O pyproject.toml registra apenas suas dependências diretas, com faixas de versão:
| |
pip install . lê esse arquivo e resolve toda a árvore de dependências no momento da instalação. pip install ".[dev]" adiciona o grupo dev por cima. O mesmo arquivo guarda os metadados de empacotamento caso você publique o projeto um dia, algo para o qual o requirements.txt nunca foi pensado.
O que ele não faz é fixar versões exatas. flask>=3.0 resolve para a versão mais recente disponível no dia em que você instala. Fixar versões é trabalho de um lock file, e venv + pip não entregam um.
Em uma build de container, esse é o arquivo que você copia antes do resto do código-fonte, para que a camada de dependências só seja reconstruída quando pyproject.toml mudar. O guia de boas práticas de Dockerfile explica por que essa ordem importa para o cache de build.
Como usar uv no lugar de venv e pip?
uv é um único binário em Rust que cobre o trabalho de venv, pip, pip-tools e pyenv: uv python install 3.12 baixa e gerencia versões do Python da mesma forma que o pyenv, então uv venv --python 3.12 funciona mesmo que essa versão ainda não esteja na máquina. Também é rápido o suficiente para notar: uma resolução que leva vários segundos no pip termina em uma fração desse tempo.
Instale-o uma vez (a documentação do astral.sh lista o comando para cada plataforma). Depois você pode usá-lo de duas formas.
Como substituto direto de venv e pip, se tudo que você quer é velocidade:
| |
uv venv cria .venv da mesma forma que python -m venv .venv, e uv pip install aceita os mesmos argumentos que o pip. Você ativa o resultado como qualquer outro venv.
No modo projeto, que adiciona locking:
| |
uv init monta a estrutura de um pyproject.toml. uv add grava a dependência nele, resolve toda a árvore e escreve um arquivo uv.lock que fixa cada versão resolvida, incluindo as transitivas, com hashes. Esse lock file é a peça que o pip não produz. Faça commit de pyproject.toml e de uv.lock, e um colega executa:
| |
Isso cria .venv se estiver faltando e instala exatamente o que o lock file diz, não o que as faixas de versão em pyproject.toml resolvem naquele dia. uv run script.py roda a mesma verificação de sincronia antes de executar, então seu ambiente não consegue se desviar do lock file sem você notar.
Como corrigir os erros mais comuns de ambiente virtual?
error: externally-managed-environment. Você executou pip install fora de um venv no Debian 12+, Ubuntu 23.04+, Fedora 38+ ou distro parecida, e o Python do sistema recusou. A PEP 668 introduziu um arquivo marcador que o gerenciador de pacotes do sistema operacional coloca na sua instalação do Python para impedir que o pip sobrescreva pacotes que o apt ou o dnf também gerenciam. Antes disso, um sudo pip install por descuido podia quebrar as próprias ferramentas do sistema operacional de formas horríveis de depurar. Alguns guias mais antigos dizem para adicionar --break-system-packages. Não faça isso: isso traz de volta exatamente o estrago que o marcador evita. Crie um venv e instale ali.
O editor executa o interpretador errado. VS Code, PyCharm e ferramentas parecidas usam o que python resolveu quando o projeto foi aberto, que não é necessariamente o do seu venv. Os sintomas: imports que funcionam no terminal aparecem como não resolvidos no editor, ou um pacote instalado cinco minutos atrás não é encontrado. Defina explicitamente o interpretador do editor como .venv/bin/python (.venv\Scripts\python.exe no Windows).
O venv foi commitado no git. São algumas centenas de megabytes de binários mais caminhos absolutos no pyvenv.cfg e nos scripts de ativação, então quebra assim que alguém clona o repositório para um caminho ou sistema operacional diferente. Adicione .venv/ ao .gitignore. Desde o Python 3.13, o venv também grava um .gitignore dentro do novo ambiente que ignora seu conteúdo, mas em versões mais antigas você precisa fazer isso você mesmo. Faça commit de requirements.txt, ou de pyproject.toml com uv.lock. Esses arquivos reconstroem o ambiente em qualquer lugar. O diretório em si nunca pertence ao repositório.
venv, pyproject.toml ou uv: qual usar?
Fique com venv + pip + requirements.txt para um projeto pequeno com poucas dependências, onde você não quer ferramental extra. Migre para pyproject.toml assim que tiver dependências só de desenvolvimento, como test runners e linters que não devem ir para produção, ou quando planejar empacotar o projeto.
Adicione o uv quando a velocidade de resolução começar a pesar ou você quiser um lock file commitado sem manter um à mão com pip-tools. Ele não força nenhuma reescrita: execute uv init em um projeto novo, ou uv venv e uv pip install -r requirements.txt em um existente, e migre para uv add quando estiver pronto para o lock file.