Bibliotecas são componentes essenciais na programação, pois permitem que desenvolvedores reutilizem código já escrito, economizando tempo e esforço. Elas fornecem abstrações de alto nível, promovendo um desenvolvimento mais rápido e protegendo os usuários de detalhes complexos de implementação — o que permite que os desenvolvedores foquem na lógica principal da aplicação.
Para facilitar a distribuição e o compartilhamento dessas bibliotecas, temos repositórios online como o GitHub (para código-fonte em geral), o npm (para bibliotecas JavaScript) e o PyPI (para pacotes Python). No PyPI podemos encontrar bibliotecas como requests (para lidar com chamadas HTTP), pandas (para análise de dados), pyspark (para processamento de dados em larga escala) e muitas outras.

Com bibliotecas, não precisamos reinventar a roda — e elas são distribuídas gratuitamente pela internet! Mas surge a pergunta: como podemos usar essas bibliotecas?

Usando bibliotecas Python

Quando estamos começando na programação, é comum pensar que usar uma biblioteca é algo bem direto: basta copiar o código-fonte para a sua pasta e começar a usar no seu código.
Isso não está errado, mas seria uma tarefa cansativa ter que procurar no PyPI a biblioteca desejada, baixar o código-fonte, identificar as pastas necessárias e colá-las no diretório do seu projeto…
Lembre-se de que isso teria que ser feito várias vezes no mesmo projeto, já que normalmente trabalhamos com várias dependências.
Além disso, você teria que atualizar manualmente o código de cada biblioteca toda vez que o mantenedor publicasse uma nova versão no PyPI para corrigir bugs ou adicionar recursos.

Não é só copiar e colar

Copiar e colar código-fonte no seu workspace traria outros problemas além do esforço manual.
Um deles é que estaríamos duplicando código de forma desorganizada, pois a mesma biblioteca seria copiada e espalhada em vários projetos diferentes pela internet.

Outro problema é o gerenciamento de dependências.
Imagine o seguinte cenário: você quer criar uma nova biblioteca, mas quer usar recursos de outras bibliotecas (dependências) disponíveis no PyPI.
Agora, alguém vai usar a sua biblioteca — e essa pessoa também usa outra biblioteca que é exatamente a mesma que está nas suas dependências, mas em uma versão mais recente.
Se fosse tudo apenas “copiar e colar”, como esse desenvolvedor lidaria com essa situação?
Mesmo que ambas as versões fossem copiadas (uma para sua dependência e outra para o projeto dele), como o Python saberia qual delas deve ser usada no import?

É aqui que entra o pip, o instalador de pacotes do Python.

Instalador de pacotes do Python

Agora você já entende por que precisamos de um gerenciador de pacotes.
No caso do Python, temos o pip — uma ferramenta de linha de comando para instalar pacotes Python — que é o gerenciador de pacotes “oficial” da linguagem (embora existam alternativas).
Ele simplifica o processo de baixar e instalar pacotes do PyPI e de outros repositórios — no fim das contas, é como um “copiar e colar” automatizado e inteligente.
Bibliotecas instaladas com o pip (ou outros gerenciadores de pacotes) ficam disponíveis para serem importadas em seus módulos e subpacotes.

Veja algumas das suas principais funcionalidades:

  • Instalação fácil de pacotes: instale bibliotecas Python com a CLI do pip
  • Resolução de dependências: resolve e instala automaticamente as dependências necessárias de um pacote, garantindo um processo de instalação fluido.
  • Desinstalação de pacotes: com o pip, você pode remover pacotes e suas dependências associadas de forma limpa, evitando bagunça no ambiente.
  • Gerenciamento de versões: o pip permite instalar versões específicas ou as mais recentes de cada pacote.

Como usar o pip

Normalmente, o pip já vem instalado junto com o Python.
Porém, se por algum motivo você não o tiver, é possível obtê-lo baixando o script get-pip.py e executando-o com o interpretador Python (veja aqui as instruções completas de instalação).

Agora, vamos ver como realizar algumas operações comuns de gerenciamento de pacotes usando o pip.

Instalando pacotes

Use o comando pip install seguido do nome do pacote para instalar uma biblioteca.

# instala o pacote pandas do PyPI
pip install pandas

# instala múltiplos pacotes de uma vez — pytest, sphinx e mypy
pip install pytest sphinx mypy

# instala um pacote com opcionais
pip install "fastapi[all]"

# instala uma versão específica do pacote requests
pip install requests==2.26.0

# instala a partir de um repositório do GitHub (desde que o pacote seja instalável)
pip install 'git+https://github.com/pydantic/pydantic'

Atualizando pacotes

Para atualizar um pacote já instalado para a versão mais recente, use:

pip install --upgrade requests

Quando atualizamos um pacote, a versão antiga é removida e substituída pela nova. Não é possível ter múltiplas versões da mesma biblioteca instaladas no mesmo ambiente.

Desinstalando pacotes

Remova um pacote e suas dependências com:

pip uninstall pandas

Listando seus pacotes

Com o comando pip list, você pode listar todos os pacotes instalados no seu ambiente:

pip list

É interessante notar que provavelmente você terá uma lista grande de pacotes, mesmo que tenha instalado apenas uma única biblioteca, pois o pip também instala todas as dependências necessárias para que tudo funcione corretamente.

Assim é a aparência de um ambiente novo ao rodar pip list:

Package      Version
------------ -------
pip          22.0.2
setuptools   59.6.0

E assim ele fica depois de instalar apenas um pacote (pytest):

Package        Version
-------------- -------
exceptiongroup 1.1.2
iniconfig      2.0.0
packaging      23.1
pip            22.0.2
pluggy         1.2.0
pytest         7.4.0
setuptools     59.6.0
tomli          2.0.1

O pacote pytest, por exemplo, traz consigo 5 novas dependências.

Gerenciando dependências com requirements.txt

Quando você instala bibliotecas usando o pip, o código delas é colocado ao lado do interpretador Python no sistema de arquivos. Assim, conseguimos manter as dependências separadas do nosso código-fonte. Mas, se compartilharmos o código com colegas, eles terão erros de ImportError, certo? E como evitar conflitos entre versões diferentes da mesma biblioteca em projetos distintos? Para lidar com esses problemas, usamos arquivos de requisitos e ambientes virtuais. Se quiser se aprofundar nesse assunto, escrevi um artigo completo sobre Ambientes Virtuais Python aqui.

Em resumo, os ambientes virtuais nos ajudam a isolar as dependências por projeto. Assim, cada projeto pode ter seu próprio conjunto de bibliotecas e versões específicas.

Dentro do ambiente virtual, podemos instalar as dependências e usar arquivos de requirements para facilitar a instalação de tudo o que for necessário. Esses arquivos são textos simples que listam as dependências do projeto, suas versões e fontes. Podemos gerar um arquivo de requisitos com o comando:

pip freeze > requirements.txt

Depois, sempre que precisarmos compartilhar o projeto ou configurá-lo novamente, podemos instalar todas as dependências com:

pip install -r requirements.txt

Os arquivos de requirements devem ser incluídos no seu repositório Git, caso o projeto esteja sendo versionado. Por outro lado, o seu ambiente virtual não deve ser enviado ao repositório remoto — portanto, certifique-se de adicioná-lo ao seu arquivo .gitignore.

Conclusão

Compreender os fundamentos do gerenciamento de pacotes é essencial para qualquer desenvolvedor, já que é um componente básico em praticamente todos os ecossistemas de linguagens de programação. Desde a instalação de bibliotecas simples até o gerenciamento de dependências complexas, um gerenciador de pacotes simplifica o processo e garante uma experiência de desenvolvimento fluida.

Leitura adicional