Path.walk no Python: percorra diretórios

Publicado em: 28/08/2026
Tempo de leitura: 5 minutos
A tranquil wooden pathway winds through a vibrant autumn forest, covered in fallen leaves.

Percorrer árvores de diretórios é uma tarefa comum em automação, backup, auditoria, limpeza e indexação. Durante anos, os.walk() foi a ferramenta padrão. Versões modernas do Python também oferecem pathlib.Path.walk(), que integra a travessia ao modelo orientado a objetos de pathlib e devolve caminhos como objetos Path.

Neste guia, você aprenderá a percorrer diretórios de cima para baixo ou de baixo para cima, filtrar subpastas, tratar erros, lidar com links simbólicos, calcular tamanhos, localizar extensões, remover árvores com segurança e manter compatibilidade com versões anteriores.

Primeiro exemplo com Path.walk

from pathlib import Path

raiz = Path("projeto")

for diretorio, subdirs, arquivos in raiz.walk():
    print("Diretório:", diretorio)
    for nome in arquivos:
        caminho = diretorio / nome
        print("  Arquivo:", caminho)

Cada iteração retorna uma tupla com o diretório atual como Path, uma lista de nomes de subdiretórios e uma lista de nomes de arquivos. Os nomes precisam ser combinados com o diretório para formar caminhos completos.

Por que usar pathlib?

Path concentra operações de caminho, extensão, leitura, escrita, comparação e metadados em uma API consistente.

for diretorio, _, arquivos in raiz.walk():
    for nome in arquivos:
        caminho = diretorio / nome
        if caminho.suffix == ".py":
            print(caminho.relative_to(raiz))

Isso evita misturar repetidamente os.path.join(), os.path.splitext() e outras funções. O artigo sobre pathlib no Python apresenta os fundamentos da classe Path.

Travessia top-down

Por padrão, a árvore é percorrida de cima para baixo. O diretório pai aparece antes dos filhos.

for diretorio, subdirs, arquivos in raiz.walk(top_down=True):
    print(diretorio)

Esse modo permite modificar a lista subdirs para controlar quais pastas serão visitadas.

Ignorando diretórios

Remova nomes da lista de subdiretórios durante a travessia top-down.

IGNORAR = {".git", ".venv", "node_modules", "__pycache__"}

for diretorio, subdirs, arquivos in raiz.walk():
    subdirs[:] = [nome for nome in subdirs if nome not in IGNORAR]

    for nome in arquivos:
        print(diretorio / nome)

A atribuição com slice modifica a mesma lista usada internamente pelo walker. Apenas reatribuir subdirs = [...] não controla a recursão.

Ordenando a travessia

A ordem devolvida pelo sistema de arquivos não deve ser considerada estável. Para resultados reproduzíveis, ordene as listas.

for diretorio, subdirs, arquivos in raiz.walk():
    subdirs.sort()
    arquivos.sort()
    for nome in arquivos:
        print(diretorio / nome)

A ordenação custa processamento adicional, mas é útil em testes, relatórios e geração determinística.

Travessia bottom-up

Com top_down=False, os filhos aparecem antes do pai.

for diretorio, subdirs, arquivos in raiz.walk(top_down=False):
    print(diretorio)

Esse modo é útil para remover uma árvore, pois os arquivos e diretórios internos precisam ser apagados antes do diretório pai.

Remoção segura de uma árvore

from pathlib import Path


def remover_arvore(raiz: Path) -> None:
    for diretorio, subdirs, arquivos in raiz.walk(top_down=False):
        for nome in arquivos:
            (diretorio / nome).unlink()
        for nome in subdirs:
            (diretorio / nome).rmdir()
    raiz.rmdir()

Esse exemplo é destrutivo. Valide o caminho, recuse raízes perigosas e considere shutil.rmtree() para uma implementação consolidada. Nunca construa uma rotina de exclusão baseada em entrada não confiável sem normalização e limites.

Tratamento de erros com on_error

Erros ao listar um diretório podem ser tratados por uma função.

import logging

logger = logging.getLogger(__name__)


def registrar_erro(erro: OSError) -> None:
    logger.warning("Não foi possível acessar %s: %s", erro.filename, erro)

for diretorio, subdirs, arquivos in raiz.walk(on_error=registrar_erro):
    ...

Sem uma função, erros de listagem podem ser ignorados conforme o comportamento documentado. Quando a completude importa, registre, conte ou propague explicitamente.

Interrompendo ao primeiro erro

def falhar(erro: OSError) -> None:
    raise erro

for item in raiz.walk(on_error=falhar):
    ...

Escolha a política conforme o objetivo. Um indexador pode continuar e registrar; uma auditoria de segurança talvez precise falhar se qualquer área ficar inacessível.

Links simbólicos para diretórios exigem cuidado. Com a opção apropriada, o walker pode seguir esses links. Isso pode criar ciclos ou fazer a travessia sair da árvore esperada.

for diretorio, subdirs, arquivos in raiz.walk(follow_symlinks=False):
    ...

O padrão mais seguro é não seguir links. Se o projeto precisa segui-los, mantenha um conjunto de diretórios reais visitados, imponha limites e considere dispositivos ou pontos de montagem.

Detectando ciclos

Uma estratégia em sistemas compatíveis é registrar combinações de dispositivo e inode obtidas por stat().

visitados: set[tuple[int, int]] = set()

for diretorio, subdirs, arquivos in raiz.walk(follow_symlinks=True):
    info = diretorio.stat()
    chave = (info.st_dev, info.st_ino)
    if chave in visitados:
        subdirs.clear()
        continue
    visitados.add(chave)

Em plataformas diferentes, semânticas de inode e links podem variar. Teste no ambiente de destino.

Localizando arquivos por extensão

EXTENSOES = {".py", ".toml", ".json"}

encontrados: list[Path] = []
for diretorio, subdirs, arquivos in raiz.walk():
    subdirs[:] = [d for d in subdirs if d not in {".git", ".venv"}]
    for nome in arquivos:
        caminho = diretorio / nome
        if caminho.suffix.lower() in EXTENSOES:
            encontrados.append(caminho)

Para padrões simples sem necessidade de poda, Path.rglob() pode ser mais curto. walk() é melhor quando você precisa controlar diretórios, erros e ordem.

Calculando o tamanho da árvore

def tamanho_total(raiz: Path) -> int:
    total = 0
    for diretorio, _, arquivos in raiz.walk():
        for nome in arquivos:
            caminho = diretorio / nome
            try:
                total += caminho.stat().st_size
            except OSError:
                continue
    return total

O resultado pode mudar durante a travessia se arquivos forem criados, removidos ou modificados. Para uma visão consistente, seria necessário um snapshot do sistema de arquivos ou mecanismo específico da plataforma.

Encontrando arquivos grandes

def maiores_que(raiz: Path, limite: int):
    for diretorio, _, arquivos in raiz.walk():
        for nome in arquivos:
            caminho = diretorio / nome
            try:
                tamanho = caminho.stat().st_size
            except OSError:
                continue
            if tamanho > limite:
                yield caminho, tamanho

Usar um gerador evita manter todos os resultados na memória.

Comparação com os.walk

Path.walk() e os.walk() possuem conceitos semelhantes: top-down, poda de diretórios, tratamento de erros e links. A principal diferença prática é que Path.walk integra o diretório atual à API de pathlib.

import os

for diretorio, subdirs, arquivos in os.walk("projeto"):
    ...

Em projetos que já usam Path, a nova API reduz conversões. Em bibliotecas compatíveis com versões antigas do Python, os.walk() continua sendo uma escolha sólida.

Compatibilidade de versão

Path.walk() foi adicionado em versões modernas do Python. Se o pacote suporta versões anteriores, teste a existência do método ou mantenha uma implementação com os.walk().

from pathlib import Path
import os


def percorrer(raiz: Path):
    if hasattr(raiz, "walk"):
        yield from raiz.walk()
        return

    for diretorio, subdirs, arquivos in os.walk(raiz):
        yield Path(diretorio), subdirs, arquivos

Declare a versão mínima no pyproject.toml e nos metadados do pacote.

Concorrência durante a travessia

Árvores de diretórios mudam. Um arquivo listado pode desaparecer antes do stat(); permissões podem mudar e um diretório pode ser renomeado. Trate OSError próximo da operação que pode falhar.

Não presuma que verificar exists() antes evita erro. Entre a verificação e o uso, outro processo pode alterar o caminho. Prefira tentar a operação e capturar a exceção apropriada.

Segurança e caminhos não confiáveis

Ao percorrer uma raiz fornecida por usuário, resolva e valide a localização permitida. Links simbólicos e sequências como .. podem levar para fora do diretório esperado.

base = Path("/srv/uploads").resolve()
alvo = (base / entrada_usuario).resolve()

if not alvo.is_relative_to(base):
    raise ValueError("caminho fora da área permitida")

A validação precisa considerar a política de links simbólicos e possíveis mudanças concorrentes.

Erros comuns

  • Reatribuir subdirs em vez de alterar a lista: use subdirs[:].
  • Confiar na ordem do sistema de arquivos: ordene quando precisar de determinismo.
  • Seguir symlinks sem detectar ciclos: a travessia pode ser infinita.
  • Ignorar erros silenciosamente: defina política de completude.
  • Usar top-down para remover diretórios: prefira bottom-up.
  • Presumir snapshot consistente: a árvore pode mudar durante o processo.

Exemplo completo: inventário de projeto

from dataclasses import dataclass
from pathlib import Path

@dataclass
class ArquivoInfo:
    caminho: Path
    tamanho: int


def inventariar(raiz: Path) -> list[ArquivoInfo]:
    resultado: list[ArquivoInfo] = []
    ignorar = {".git", ".venv", "node_modules", "__pycache__"}

    for diretorio, subdirs, arquivos in raiz.walk():
        subdirs[:] = sorted(d for d in subdirs if d not in ignorar)
        for nome in sorted(arquivos):
            caminho = diretorio / nome
            try:
                info = caminho.stat()
            except OSError:
                continue
            resultado.append(
                ArquivoInfo(
                    caminho=caminho.relative_to(raiz),
                    tamanho=info.st_size,
                )
            )

    return resultado

O inventário ignora diretórios pesados, produz ordem estável, trabalha com caminhos relativos e tolera arquivos que desaparecem.

Conclusão

Path.walk() leva a travessia de árvores para a API de pathlib. Ele permite poda de diretórios, controle de ordem, tratamento de erros e travessia bottom-up usando objetos Path.

A documentação oficial de Path.walk no pathlib detalha parâmetros e comportamento com links. Use a função quando precisar de controle fino, trate mudanças concorrentes do sistema de arquivos e mantenha uma estratégia de compatibilidade se o projeto suporta versões antigas.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Minimalist hourglass filled with sand symbolizing time and patience, against a soft background.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    asyncio.timeout no Python: controle prazos

    Aprenda asyncio.timeout no Python para deadlines, timeout_at, reagendamento, TaskGroup, cleanup, retries e cancelamento assíncrono seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A detailed view of computer programming code on a screen, showcasing software development.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    TaskGroup no Python: concorrência estruturada

    Aprenda asyncio.TaskGroup no Python para concorrência estruturada, resultados, cancelamento, ExceptionGroup, timeouts e tarefas aninhadas.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Black and white close-up of a dictionary page showing the definition of 'virus.'
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    MappingProxyType: dicionário só leitura

    Aprenda MappingProxyType no Python para expor dicionários somente leitura, criar visões dinâmicas, snapshots e proteger invariantes sem cópias.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    A close-up of a padlock securing a wire fence, symbolizing protection and safety.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Literal no Python: restrinja valores

    Aprenda typing.Literal no Python para restringir valores, criar overloads, discriminar TypedDict, usar match/case e melhorar APIs tipadas.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Close-up of hands typing on a laptop keyboard, Python book in sight, coding in progress.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    TypedDict no Python: dicionários tipados

    Aprenda TypedDict no Python para definir dicionários tipados, campos opcionais, NotRequired, Required, APIs e variantes com segurança estática.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Protocol no Python: tipagem estrutural

    Aprenda typing.Protocol no Python para tipagem estrutural, contratos genéricos, callbacks, runtime_checkable, testes e baixo acoplamento.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026