Trabalhando com Path e sistema de arquivos

1. Introdução ao módulo pathlib

O módulo pathlib, introduzido no Python 3.4, revolucionou a forma como trabalhamos com caminhos de arquivos. Antes dele, a manipulação de caminhos era feita majoritariamente com os.path, que tratava caminhos como strings — uma abordagem propensa a erros e pouco intuitiva.

Com pathlib, caminhos se tornam objetos com métodos e propriedades, tornando o código mais legível, seguro e orientado a objetos. Além disso, o módulo lida automaticamente com as diferenças entre sistemas operacionais (Windows usa \, Linux/macOS usam /).

Criando objetos Path

from pathlib import Path

# Caminho absoluto
caminho_absoluto = Path("/home/usuario/projetos/meu_projeto")
print(caminho_absoluto)  # /home/usuario/projetos/meu_projeto

# Caminho relativo
caminho_relativo = Path("documentos/relatorio.txt")
print(caminho_relativo)  # documentos/relatorio.txt

# Caminho a partir do diretório atual
atual = Path.cwd()
print(atual)  # /home/usuario/projetos (exemplo)

# Caminho a partir do diretório home
home = Path.home()
print(home)  # /home/usuario (exemplo)

Propriedades básicas

arquivo = Path("/home/usuario/documentos/relatorio_final.pdf")

print(f"Parent: {arquivo.parent}")           # /home/usuario/documentos
print(f"Name: {arquivo.name}")               # relatorio_final.pdf
print(f"Stem: {arquivo.stem}")               # relatorio_final
print(f"Suffix: {arquivo.suffix}")           # .pdf

# Acessando ancestrais
print(f"Parent do parent: {arquivo.parent.parent}")  # /home/usuario

2. Navegação e verificação de caminhos

Verificando existência e tipo

caminho = Path("meu_arquivo.txt")

print(f"Existe? {caminho.exists()}")
print(f"É arquivo? {caminho.is_file()}")
print(f"É diretório? {caminho.is_dir()}")
caminho = Path("/home/usuario/documentos/projetos/python/script.py")

# Acessando pais
for parent in caminho.parents:
    print(parent)

# Saída:
# /home/usuario/documentos/projetos/python
# /home/usuario/documentos/projetos
# /home/usuario/documentos
# /home/usuario
# /home

# Obtendo partes do caminho
print(caminho.parts)  
# ('/', 'home', 'usuario', 'documentos', 'projetos', 'python', 'script.py')

Resolvendo caminhos

caminho_relativo = Path("documentos/./relatorios/../arquivo.txt")

# .resolve() retorna o caminho absoluto normalizado
print(caminho_relativo.resolve())
# /home/usuario/documentos/arquivo.txt (exemplo)

# .absolute() retorna o caminho absoluto sem normalizar
print(caminho_relativo.absolute())
# /home/usuario/documentos/./relatorios/../arquivo.txt

3. Operações com diretórios

Criando e removendo diretórios

from pathlib import Path

# Criar diretório simples
novo_dir = Path("novo_diretorio")
novo_dir.mkdir(exist_ok=True)  # exist_ok=True evita erro se já existir

# Criar diretórios aninhados
dir_aninhado = Path("pasta1/pasta2/pasta3")
dir_aninhado.mkdir(parents=True, exist_ok=True)

# Remover diretório vazio
novo_dir.rmdir()  # Só funciona se estiver vazio!

Listando conteúdo

diretorio = Path(".")

# Listar todos os itens
for item in diretorio.iterdir():
    print(item)

# Buscar com padrão (glob)
for py_file in diretorio.glob("*.py"):
    print(py_file)

# Busca recursiva
for all_py in diretorio.rglob("*.py"):
    print(all_py)

Diretórios temporários

import tempfile
from pathlib import Path

with tempfile.TemporaryDirectory() as temp_dir:
    temp_path = Path(temp_dir)

    # Criar arquivo temporário
    arquivo_temp = temp_path / "dados.txt"
    arquivo_temp.write_text("Conteúdo temporário")

    print(f"Arquivo temporário em: {arquivo_temp}")
    # O diretório é automaticamente removido ao sair do bloco with

4. Manipulação de arquivos

Lendo e escrevendo arquivos

arquivo = Path("exemplo.txt")

# Escrever texto
arquivo.write_text("Olá, mundo!\nSegunda linha.", encoding="utf-8")

# Ler texto
conteudo = arquivo.read_text(encoding="utf-8")
print(conteudo)

# Trabalhar com bytes
dados_binarios = b"\x00\x01\x02\x03"
arquivo_bin = Path("dados.bin")
arquivo_bin.write_bytes(dados_binarios)

dados_lidos = arquivo_bin.read_bytes()
print(dados_lidos)

Renomeando e movendo arquivos

origem = Path("exemplo.txt")
destino = Path("novo_nome.txt")

# Renomear
origem.rename(destino)

# Mover para outro diretório (também usa rename)
destino.rename(Path("backup/novo_nome.txt"))

# Substituir (sobrescreve se existir)
destino.replace(Path("final.txt"))

Copiando e removendo arquivos

import shutil
from pathlib import Path

origem = Path("original.txt")
copia = Path("copia.txt")

# Copiar arquivo
shutil.copy(origem, copia)

# Copiar diretório inteiro
shutil.copytree(Path("pasta_origem"), Path("pasta_destino"))

# Remover arquivo
arquivo = Path("para_remover.txt")
arquivo.unlink(missing_ok=True)  # missing_ok=True evita erro se não existir

5. Metadados e atributos de arquivos

arquivo = Path("exemplo.txt")

# Obter estatísticas completas
stats = arquivo.stat()

print(f"Tamanho: {stats.st_size} bytes")
print(f"Último acesso: {stats.st_atime}")
print(f"Última modificação: {stats.st_mtime}")
print(f"Metadados alterados em: {stats.st_ctime}")

# Converter timestamps para datetime
from datetime import datetime
data_modificacao = datetime.fromtimestamp(stats.st_mtime)
print(f"Modificado em: {data_modificacao}")

6. Permissões e propriedades avançadas

arquivo = Path("exemplo.txt")

# Alterar permissões (modo octal)
arquivo.chmod(0o644)  # rw-r--r--

# Obter proprietário e grupo
print(f"Proprietário: {arquivo.owner()}")
print(f"Grupo: {arquivo.group()}")

# Trabalhar com links simbólicos
link = Path("meu_link")

# Criar link simbólico
link.symlink_to(arquivo)

# Verificar se é link
print(f"É link? {link.is_symlink()}")

# Ler destino do link
print(f"Aponta para: {link.readlink()}")

7. Padrões e busca avançada com glob

diretorio = Path(".")

# Padrões básicos
print("Arquivos .txt:")
for txt in diretorio.glob("*.txt"):
    print(f"  {txt}")

# Curinga de caractere único
print("Arquivos com 4 caracteres + .txt:")
for arquivo in diretorio.glob("????.txt"):
    print(f"  {arquivo}")

# Conjuntos de caracteres
print("Arquivos que começam com a, b ou c:")
for arquivo in diretorio.glob("[abc]*.txt"):
    print(f"  {arquivo}")

# Busca recursiva com **
print("Todos os arquivos .py recursivamente:")
for py in diretorio.rglob("*.py"):
    print(f"  {py}")

# Combinando padrões
padroes = ["*.txt", "*.md", "*.py"]
for padrao in padroes:
    for arquivo in diretorio.glob(padrao):
        print(f"Encontrado: {arquivo}")

8. Boas práticas e tratamento de erros

Gerenciamento de contexto

from pathlib import Path

# Usando Path como gerenciador de contexto (para arquivos)
arquivo = Path("dados.txt")
with arquivo.open("r", encoding="utf-8") as f:
    conteudo = f.read()

Tratamento de exceções

from pathlib import Path
import shutil

def processar_arquivo(caminho_str: str):
    caminho = Path(caminho_str)

    try:
        if not caminho.exists():
            raise FileNotFoundError(f"Arquivo não encontrado: {caminho}")

        if not caminho.is_file():
            raise NotADirectoryError(f"{caminho} não é um arquivo")

        conteudo = caminho.read_text(encoding="utf-8")

        # Processar...
        backup = caminho.with_suffix(".bak")
        shutil.copy(caminho, backup)

        return conteudo

    except FileNotFoundError as e:
        print(f"Erro: {e}")
        return None
    except PermissionError as e:
        print(f"Sem permissão para acessar: {e}")
        return None
    except NotADirectoryError as e:
        print(f"Erro de tipo: {e}")
        return None
    except Exception as e:
        print(f"Erro inesperado: {e}")
        return None

# Uso
resultado = processar_arquivo("documento.txt")

Código cross-platform

from pathlib import Path

# pathlib já lida com diferenças de separadores automaticamente
caminho = Path("pasta") / "subpasta" / "arquivo.txt"
# No Windows: pasta\subpasta\arquivo.txt
# No Linux/macOS: pasta/subpasta/arquivo.txt

# Acessar diretórios específicos do sistema
home = Path.home()
temp = Path("/tmp")  # Linux/macOS
# No Windows, use: Path(os.environ.get('TEMP', '/tmp'))

# Verificar sistema operacional
import sys
if sys.platform == "win32":
    config_dir = Path(os.environ.get("APPDATA")) / "meu_app"
else:
    config_dir = Path.home() / ".config" / "meu_app"

config_dir.mkdir(parents=True, exist_ok=True)

Referências