Path.glob
glob('*.py') looks in this folder only; glob('**/*.py') and rglob('*.py') look everywhere below it. Unlike the glob module, pathlib's * also matches dotfiles and ** is always recursive. Results come in file-system order — sort them.
Demo
from pathlib import Path for f in ['app.py', '.hidden.py', 'src/core.py', 'src/lib/util.py', 'docs/guide.md']: Path(f).parent.mkdir(parents=True, exist_ok=True) Path(f).touch() sorted(p.as_posix() for p in Path('.').glob('*.py'))
'*.py' stays in the top folder but still finds '.hidden.py' — pathlib globbing treats dotfiles like any other name. '**' matches zero or more folders, so '**/*.py' finds app.py too. A trailing slash selects folders only ('**/' lists '.' itself and every sub-folder). walk() reports the starting folder as '.', and each triple lists names, not paths.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| pattern | str | PurePath | yes | Relative glob: * ? [seq] within a component, ** for any depth. An absolute or empty pattern is an error. |
| case_sensitive | bool | None | no (None) | 3.12+. None = the platform rule: case-sensitive on POSIX, not on Windows. |
| recurse_symlinks | bool | no (False) | 3.13+. Follow symlinks to folders when expanding **. |
| top_down | bool | no (True) | walk only: yield a folder before its sub-folders; with top_down=True you may prune dirnames in place. |
| on_error | callable | None | no (None) | walk only: called with the OSError when a folder cannot be listed; errors are ignored by default. |
| follow_symlinks | bool | no (False) | walk only: descend into symlinked folders. |
Return value
Iterator[Path] | Iterator[tuple[Path, list[str], list[str]]] — glob / rglob yield matching paths; walk yields one triple per folder.
Common patterns
from pathlib import Path for path in sorted(Path('data').rglob('*.csv')): process(path)
from pathlib import Path images = [p for p in Path('img').rglob('*') if p.suffix.lower() in {'.jpg', '.jpeg', '.png'}]
from pathlib import Path for folder, dirs, files in Path('.').walk(): dirs[:] = [d for d in dirs if d not in {'.git', 'node_modules', '__pycache__'}] for name in files: print(folder / name)
from pathlib import Path total = sum(p.stat().st_size for p in Path('build').rglob('*') if p.is_file())
Examples
Pitfalls
from pathlib import Path type(Path('.').glob('*')).__name__
from pathlib import Path for n in ['b.txt', 'a.txt']: Path(n).touch() [p.name for p in sorted(Path('.').glob('*.txt'))]
from pathlib import Path for n in ['a.jpg', 'b.png']: Path(n).touch() list(Path('.').glob('*.{jpg,png}'))
from pathlib import Path for n in ['a.jpg', 'b.png']: Path(n).touch() sorted(p.name for p in Path('.').iterdir() if p.suffix in {'.jpg', '.png'})
from pathlib import Path Path('a.txt').touch() found = Path('.').glob('*.txt') first = list(found) list(found)
from pathlib import Path Path('a.txt').touch() found = list(Path('.').glob('*.txt')) len(found), len(found)
When to use
- Finding files by name or extension (glob / rglob)
- Walking a tree folder by folder, with pruning (walk, 3.12+)
- Matching paths you already have → PurePath.full_match
- Hidden-file-aware shell semantics → the glob module (glob.glob skips dotfiles by default)
Notes
FAQ
sorted(Path('folder').rglob('*.ext')) — or glob('**/*.ext'). Both search every sub-folder.