A manipulação de caminhos de arquivos é uma tarefa comum em qualquer programa Python. Tradicionalmente, usávamos funções como os.path.join e os.path.exists para lidar com strings de caminho. No entanto, desde o Python 3.4, a biblioteca pathlib oferece uma abordagem mais moderna e orientada a objetos, tornando o código mais legível e menos propenso a erros. Nesta aula, vamos mergulhar fundo em pathlib, explorando sua classe principal Path, as operações mais úteis, comparações com os.path e boas práticas para adotá-la em seus projetos.

O pathlib trata caminhos como objetos, permitindo que você realize operações como concatenação, verificação de existência e leitura de arquivos de forma intuitiva. Ele também é multiplataforma, lidando automaticamente com as diferenças entre sistemas operacionais (como barras em Windows e Linux). Vamos começar entendendo a classe Path.

Path

A classe Path é o coração do pathlib. Ela representa um caminho de arquivo ou diretório, independente do sistema operacional. Você pode criar uma instância de Path passando uma string com o caminho, ou usando métodos de classe como Path.cwd() e Path.home(). Uma vez criado, o objeto Path fornece diversos atributos e métodos para trabalhar com o caminho.

Um dos grandes benefícios é a sobrecarga de operadores: você pode usar / para unir caminhos, o que é muito mais elegante que os.path.join. Além disso, o objeto Path é imutável e hashable, o que o torna seguro para uso em dicionários e conjuntos.

from pathlib import Path

# Criando objetos Path
caminho = Path("meu_projeto/arquivo.txt")
print(caminho)  # meu_projeto/arquivo.txt

# Caminho absoluto
absoluto = Path.cwd() / "meu_projeto" / "arquivo.txt"
print(absoluto)  # /home/usuario/meu_projeto/arquivo.txt

# Diretório home
home = Path.home()
print(home)  # /home/usuario ou C:\Users\usuario

# Atributos do caminho
print(caminho.name)      # arquivo.txt
print(caminho.stem)      # arquivo
print(caminho.suffix)    # .txt
print(caminho.parent)    # meu_projeto
print(caminho.anchor)    # '' (vazio para relativo) ou '/' no Unix

Além dos atributos básicos, Path oferece métodos para verificar propriedades do caminho, como exists(), is_file(), is_dir(), e para manipular o sistema de arquivos, como mkdir(), rename(), unlink(). Veremos esses métodos em detalhes na próxima seção.

Operações

O pathlib fornece uma vasta gama de operações para trabalhar com arquivos e diretórios. Vamos dividir em categorias: operações de consulta, operações de modificação e operações de iteração.

Operações de consulta

Essas operações nos permitem obter informações sobre o caminho sem alterá-lo.

  • exists(): verifica se o caminho existe.
  • is_file() e is_dir(): verificam se é um arquivo ou diretório.
  • stat(): retorna informações detalhadas como tamanho, permissões, etc.
  • resolve(): retorna o caminho absoluto, resolvendo links simbólicos.
  • iterdir(): gera os itens de um diretório.
from pathlib import Path

caminho = Path("meu_projeto/arquivo.txt")

# Verificação de existência
print(caminho.exists())  # False (se não existir)

# Criando um diretório e arquivo para teste
Path("meu_projeto").mkdir(exist_ok=True)
Path("meu_projeto/arquivo.txt").touch()

print(caminho.exists())  # True
print(caminho.is_file()) # True
print(caminho.is_dir())  # False

# Resolver caminho absoluto
print(caminho.resolve())  # /home/usuario/meu_projeto/arquivo.txt

# Iterar sobre diretório
for item in Path("meu_projeto").iterdir():
    print(item)

Operações de modificação

Essas operações alteram o sistema de arquivos.

  • mkdir(): cria um diretório.
  • rename(): renomeia um arquivo ou diretório.
  • unlink(): remove um arquivo.
  • rmdir(): remove um diretório vazio.
  • touch(): cria um arquivo vazio ou atualiza a data de modificação.
  • write_text() e read_text(): escrever e ler texto de forma simples.
from pathlib import Path

caminho = Path("meu_projeto/arquivo.txt")

# Escrever texto
caminho.write_text("Olá, mundo!", encoding="utf-8")

# Ler texto
print(caminho.read_text(encoding="utf-8"))  # Olá, mundo!

# Renomear
caminho.rename("meu_projeto/renomeado.txt")

# Remover arquivo
caminho.unlink()

# Criar e remover diretório
Path("meu_projeto/temp").mkdir()
Path("meu_projeto/temp").rmdir()

Operações de iteração e busca

O método glob() é extremamente útil para encontrar arquivos que correspondem a um padrão, semelhante ao glob do módulo glob, mas integrado ao Path.

from pathlib import Path

# Listar todos os arquivos .txt em um diretório
for txt in Path("meu_projeto").glob("*.txt"):
    print(txt)

# Recursivamente
for py in Path("meu_projeto").rglob("*.py"):
    print(py)

vs os.path

Antes do pathlib, a manipulação de caminhos era feita principalmente com o módulo os.path, que trata caminhos como strings. Enquanto os.path é funcional, ele tem algumas desvantagens: requer cuidado com separadores, concatenação manual e não é tão expressivo. Vamos comparar as duas abordagens.

Com os.path, você precisa usar funções como os.path.join para concatenar, os.path.exists para verificar existência, e os.path.isdir para checar diretórios. Tudo isso retorna strings, o que pode levar a erros sutis. Já com pathlib, você tem objetos com métodos, tornando o código mais orientado a objetos e fácil de ler.

Operaçãoos.pathpathlib
Concatenaros.path.join("dir", "file")Path("dir") / "file"
Existênciaos.path.exists(path)Path(path).exists()
Nome do arquivoos.path.basename(path)Path(path).name
Extensãoos.path.splitext(path)[1]Path(path).suffix
Diretório paios.path.dirname(path)Path(path).parent

Além disso, pathlib é mais seguro em relação a diferenças entre sistemas operacionais: enquanto os.path.join usa o separador correto automaticamente, pathlib também o faz, mas de forma mais transparente. O pathlib também oferece métodos convenientes para operações de arquivo, como read_text() e write_text(), que exigiriam abrir o arquivo manualmente com open() no estilo os.path.

No entanto, o os.path ainda é amplamente utilizado em código legado e em algumas APIs que esperam strings. O pathlib é recomendado para novos projetos, e o Python 3.6+ integrou pathlib como padrão para muitas funções (por exemplo, open() aceita objetos Path).

Boas práticas

Ao usar pathlib, algumas boas práticas podem tornar seu código mais robusto e legível.

  • Use Path em vez de strings: Sempre que possível, prefira objetos Path a strings para representar caminhos. Isso evita erros de concatenação e facilita a leitura.
  • Aproveite o operador /: Use / para unir caminhos, em vez de os.path.join ou f-strings com barras. Isso torna o código mais limpo e portável.
  • Utilize métodos de conveniência: Para ler e escrever arquivos, use read_text() e write_text() em vez de abrir manualmente com open() quando o conteúdo for texto simples. Eles gerenciam o fechamento do arquivo automaticamente.
  • Prefira glob() para buscar arquivos: Use Path.glob() e rglob() para listar arquivos, em vez de combinar os.listdir() com fnmatch.
  • Trate erros adequadamente: Ao realizar operações que podem falhar (como mkdir() ou unlink()), capture exceções como FileExistsError e FileNotFoundError.
from pathlib import Path
import shutil

# Exemplo de boas práticas

def criar_projeto(nome: str):
    """Cria um diretório de projeto com um README."""
    projeto = Path("projetos") / nome
    try:
        projeto.mkdir(parents=True, exist_ok=False)
    except FileExistsError:
        raise ValueError(f"Projeto {nome} já existe.")

    readme = projeto / "README.md"
    readme.write_text(f"# {nome}\n", encoding="utf-8")
    return projeto

# Uso
projeto = criar_projeto("meu_app")
print(projeto)

Referências

Exercícios

  1. Exercício 1: Crie um script que use pathlib para listar todos os arquivos com extensão .py no diretório atual e em subdiretórios, e imprima o nome e o tamanho de cada um.

✓ Resposta:
from pathlib import Path

for py in Path('.').rglob('*.py'):
    if py.is_file():
        print(f'{py}: {py.stat().st_size} bytes')
  1. Exercício 2: Escreva uma função que, dado um diretório, crie uma subpasta chamada backup e copie todos os arquivos .txt do diretório original para ela, mantendo os nomes.

✓ Resposta:
import shutil
from pathlib import Path

def backup_txt(diretorio):
    dir_path = Path(diretorio)
    backup_dir = dir_path / 'backup'
    backup_dir.mkdir(exist_ok=True)
    for txt in dir_path.glob('*.txt'):
        shutil.copy2(txt, backup_dir / txt.name)
  1. Exercício 3: Usando pathlib, verifique se um caminho é absoluto ou relativo. Se for relativo, converta para absoluto usando resolve() e imprima o resultado.

✓ Resposta:
from pathlib import Path

caminho = Path('algum/arquivo.txt')
if caminho.is_absolute():
    print('Absoluto')
else:
    print('Relativo')
    print('Absoluto:', caminho.resolve())
  1. Exercício 4: Crie uma função que receba um caminho de arquivo e retorne o diretório pai, o nome do arquivo sem extensão e a extensão, usando pathlib.

✓ Resposta:
from pathlib import Path

def info_arquivo(caminho):
    p = Path(caminho)
    return {
        'pai': p.parent,
        'nome_sem_ext': p.stem,
        'extensao': p.suffix
    }
  1. Exercício 5: Escreva um script que percorra um diretório e, para cada arquivo que tenha mais de 1 MB, imprima seu caminho e tamanho em MB.

✓ Resposta:
from pathlib import Path

for arquivo in Path('.').rglob('*'):
    if arquivo.is_file():
        tamanho = arquivo.stat().st_size
        if tamanho > 1_000_000:
            print(f'{arquivo}: {tamanho/1_000_000:.2f} MB')

Observações finais

O pathlib é uma adição moderna e poderosa à biblioteca padrão do Python. Ele simplifica o trabalho com caminhos, tornando o código mais limpo e menos propenso a erros. Ao migrar de os.path, você notará uma curva de aprendizado suave, mas os benefícios em legibilidade e segurança valem a pena. Lembre-se de que, para muitas funções que aceitam strings de caminho, você pode passar diretamente um objeto Path (por exemplo, open(), shutil.copy()), o que facilita a integração. Pratique com os exercícios e explore a documentação oficial para descobrir todos os recursos disponíveis.