`pathlib` provides the `Path` class — an object-oriented way to work with filesystem paths. It replaces `os.path` string manipulation with clean, readable attribute access.
```python
from pathlib import Path
```
## Creating Path objects
```python
p = Path("/home/user/documents/report.txt")
p = Path(".") # current directory
p = Path.home() # home directory: /home/user
p = Path.cwd() # current working directory
# Windows paths also work:
p = Path("C:/Users/Alice/Desktop/file.txt")
```
## Path attributes
```python
p = Path("/home/user/projects/script.py")
print(p.name) # "script.py" — filename with extension
print(p.stem) # "script" — filename without extension
print(p.suffix) # ".py" — extension including dot
print(p.suffixes) # [".py"] — all extensions (for ".tar.gz" gives [".tar", ".gz"])
print(p.parent) # /home/user/projects — parent directory
print(p.parents) # all parent dirs (iterable)
print(p.parts) # ("/", "home", "user", "projects", "script.py")
print(p.root) # "/"
print(p.anchor) # "/" — drive + root on Windows
```
## Joining paths
Use `/` operator — far cleaner than `os.path.join`:
```python
base = Path("/home/user")
config = base / "config" / "settings.json"
print(config) # /home/user/config/settings.json
# Works with strings on the right side
reports = base / "reports"
report_file = reports / "2024" / "q1.csv"
```
## Checking existence
```python
p = Path("/home/user/data.txt")
print(p.exists()) # True/False — file or dir exists
print(p.is_file()) # True if it exists and is a regular file
print(p.is_dir()) # True if it exists and is a directory
print(p.is_symlink()) # True if it is a symbolic link
```
## Converting and comparing
```python
p = Path("/home/user/file.txt")
# Convert to string
str(p) # "/home/user/file.txt"
p.as_posix() # "/home/user/file.txt" — always forward slashes
# Absolute path
p.resolve() # resolves symlinks, returns absolute Path
# Relative path
p.relative_to("/home/user") # Path("file.txt")
```
## Reading and writing files
`Path` objects can read and write text and bytes directly — no `open()` needed for simple cases:
```python
from pathlib import Path
p = Path("notes.txt")
# Write text
p.write_text("Hello, pathlib!\n", encoding="utf-8")
# Read text
content = p.read_text(encoding="utf-8")
print(content) # Hello, pathlib!
# Read bytes
data = p.read_bytes()
# Write bytes
p.write_bytes(b"\x00\x01\x02")
```
For line-by-line reading, use `open()` with a Path:
```python
with p.open("r", encoding="utf-8") as f:
for line in f:
print(line.strip())
```
## Listing directory contents
```python
d = Path("/home/user/projects")
# All entries (files and subdirs)
for item in d.iterdir():
print(item.name, "dir" if item.is_dir() else "file")
# Only files
files = [f for f in d.iterdir() if f.is_file()]
# Only directories
subdirs = [f for f in d.iterdir() if f.is_dir()]
```
## glob: pattern-based search
```python
src = Path("/home/user/projects")
# All Python files in the directory (non-recursive)
py_files = list(src.glob("*.py"))
# All Python files recursively (** = any number of dirs)
all_py = list(src.rglob("*.py"))
# All log files in any subdirectory named "logs"
logs = list(src.glob("**/logs/*.log"))
```
## Creating directories
```python
new_dir = Path("/home/user/projects/new_feature")
# Create one directory
new_dir.mkdir()
# Create parent directories if they don't exist, no error if already exists
new_dir.mkdir(parents=True, exist_ok=True)
```
## Renaming and deleting
```python
p = Path("old_name.txt")
# Rename (returns new Path)
new_p = p.rename("new_name.txt")
print(new_p) # new_name.txt
# Delete a file
p.unlink()
# Delete an empty directory
d = Path("empty_dir")
d.rmdir()
# Check and delete
if p.exists():
p.unlink()
# Python 3.8+: missing_ok
p.unlink(missing_ok=True) # no error if not found
```
## Practical example: process all CSV files
```python
data_dir = Path("data")
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)
for csv_file in data_dir.glob("*.csv"):
content = csv_file.read_text()
processed = content.upper() # dummy transformation
out_file = output_dir / csv_file.name
out_file.write_text(processed)
print(f"Processed: {csv_file.name}")
```
Given the path string `"/home/user/projects/data/report_2024.csv"`, create a `Path` object from it and print:
- The full filename (`name`)
- The filename without extension (`stem`)
- The extension (`suffix`)
- The parent directory (`parent`)
- All parts of the path (`parts`)
from pathlib import Path
p = Path("/home/user/projects/data/report_2024.csv")
# Print: name, stem, suffix, parent, parts
print(p.name) # ???
print(p.stem) # ???
print(p.suffix) # ???
print(p.parent) # ???
print(p.parts) # ???
Write a function `build_path(base, *parts)` that takes a base directory as a string and any number of path parts, joins them using the `/` operator, and returns the final `Path` object. Example: `build_path("/home/user", "projects", "myapp", "config.json")` → `Path("/home/user/projects/myapp/config.json")`.
from pathlib import Path
def build_path(base, *parts):
pass
result = build_path("/home/user", "projects", "myapp", "config.json")
print(result) # /home/user/projects/myapp/config.json
Solution
from pathlib import Path
def build_path(base, *parts):
p = Path(base)
for part in parts:
p = p / part
return p
# Shorter: Path(base).joinpath(*parts)
result = build_path("/home/user", "projects", "myapp", "config.json")
print(result) # /home/user/projects/myapp/config.json
Write a function `change_extension(path_str, new_ext)` that takes a path string and a new extension (with dot, like `".txt"`), and returns a new `Path` with the extension changed. Example: `change_extension("/home/user/data.csv", ".json")` → `Path("/home/user/data.json")`. Use `p.with_suffix()`.
Write a function `safe_read(path_str, encoding="utf-8")` that reads the content of a file at `path_str` and returns it as a string. If the file does not exist, return `None`. If any other error occurs, return `None`. Use `Path.read_text()`.
Write a function `write_and_verify(path_str, content)` that:
1. Writes `content` to the file at `path_str` using `Path.write_text()`
2. Reads it back using `Path.read_text()`
3. Returns `True` if the content matches, `False` otherwise
Use `encoding="utf-8"` for both operations.
Write a function `count_by_extension(dir_path)` that takes a directory path string and returns a dictionary mapping each file extension to the count of files with that extension in the directory (non-recursive). Example: if the dir has `a.py`, `b.py`, `c.txt`, `d.txt`, `e.md`, result is `{".py": 2, ".txt": 2, ".md": 1}`.
from pathlib import Path
def count_by_extension(dir_path):
pass
# Example: if dir has a.py, b.py, c.txt, d.txt, e.md
# result: {".py": 2, ".txt": 2, ".md": 1}
Solution
from pathlib import Path
def count_by_extension(dir_path):
counts = {}
for p in Path(dir_path).iterdir():
if p.is_file():
ext = p.suffix
counts[ext] = counts.get(ext, 0) + 1
return counts
# Test with a temp directory
import tempfile, os
with tempfile.TemporaryDirectory() as d:
for name in ["a.py", "b.py", "c.txt", "d.txt", "e.md"]:
Path(d, name).write_text("", encoding="utf-8")
print(count_by_extension(d))
# {".py": 2, ".txt": 2, ".md": 1}
Write a function `find_large_files(dir_path, min_size_bytes)` that recursively searches a directory for all files larger than `min_size_bytes` and returns their paths as a sorted list of `Path` objects. Use `p.rglob("*")` and `p.stat().st_size`.
from pathlib import Path
def find_large_files(dir_path, min_size_bytes):
pass
# Returns sorted list of Path objects for files > min_size_bytes
Solution
from pathlib import Path
def find_large_files(dir_path, min_size_bytes):
return sorted(
p for p in Path(dir_path).rglob("*")
if p.is_file() and p.stat().st_size > min_size_bytes
)
# Test
for f in find_large_files(".", 1000):
print(f)
Write a function `ensure_dir(path_str)` that creates the directory at `path_str` if it does not exist. It should:
- Create all intermediate parent directories as needed
- Not raise an error if the directory already exists
- Return the `Path` object of the directory
Use `Path.mkdir(parents=True, exist_ok=True)`.
from pathlib import Path
def ensure_dir(path_str):
pass
p = ensure_dir("/tmp/myapp/logs/2024")
print(p.is_dir()) # True
print(p) # /tmp/myapp/logs/2024
Solution
from pathlib import Path
def ensure_dir(path_str):
p = Path(path_str)
p.mkdir(parents=True, exist_ok=True)
return p
p = ensure_dir("/tmp/myapp/logs/2024")
print(p.is_dir()) # True
print(p) # /tmp/myapp/logs/2024
Write a function `backup(path_str)` that creates a backup copy of a file by appending `.bak` to its name. If the file does not exist, raise `FileNotFoundError`. Return the `Path` of the backup file. Example: `backup("/home/user/data.txt")` creates `/home/user/data.txt.bak` and returns that path.
from pathlib import Path
import shutil
def backup(path_str):
pass
Path("/tmp/data_original.txt").write_text("important", encoding="utf-8")
bak = backup("/tmp/data_original.txt")
print(bak) # /tmp/data_original.txt.bak
Solution
from pathlib import Path
import shutil
def backup(path_str):
p = Path(path_str)
if not p.exists():
raise FileNotFoundError(f"File not found: {p}")
bak = p.with_suffix(p.suffix + ".bak")
shutil.copy2(str(p), str(bak))
return bak
Path("/tmp/data_original.txt").write_text("important", encoding="utf-8")
bak = backup("/tmp/data_original.txt")
print(bak) # /tmp/data_original.txt.bak
print(bak.read_text(encoding="utf-8")) # important
Write a function `find_python_files(start_dir)` that recursively finds all `.py` files under `start_dir`, sorts them by name, and returns a list of their relative paths (relative to `start_dir`) as strings. Use `Path.rglob("*.py")` and `p.relative_to(start_dir)`.
from pathlib import Path
def find_python_files(start_dir):
pass
# Returns list of relative path strings sorted by name
for f in find_python_files("."):
print(f)
Solution
from pathlib import Path
def find_python_files(start_dir):
base = Path(start_dir)
return sorted(
str(p.relative_to(base))
for p in base.rglob("*.py")
)
for f in find_python_files("."):
print(f)
No split tab
Cookie preferences
We use necessary cookies to run the site. With your permission, we can also save your site preferences and use analytics and advertising cookies to understand usage and support the project.
Open tools in tabs.Exercises, IDE tools, and trainers stay available as site tabs.
Switch without losing context.Move between explanations, code, and utilities while keeping your place.
Use the sidebar as your map.The left panels hold navigation, settings, files, libraries, and tool controls.
PythonJavaScriptSQLite
One IDE, three practical modes
Python in the browser.Run small scripts, try libraries, and practice API requests without installing anything.
JavaScript for quick experiments.Test browser-friendly code and compare ideas next to your learning materials.
SQLite for data practice.Open the database explorer to inspect tables, write queries, and learn SQL workflows locally.
TopicIDE
Work side by side with split tabs
Keep instructions visible.Open an exercise or reference page beside the IDE instead of jumping back and forth.
Compare tools while you learn.Place regex checks, explanations, and code experiments next to each other when the task needs it.
Close the split when you are done.The workspace returns to a single focused tab, and your open site tabs remain available.
Code Typing Trainer
Or plain text
This trainer is designed for a physical keyboard.Open this section on a laptop or desktop with a wide screen. Touch typing practice will not work correctly on a phone.
Speed: 0 chars/min
0 words/min
Best speed (60s): 0 chars/min
0 words/min
Errors: 0
Total time: 0.0 s
To practice touch typing, avoid looking at your physical keyboard.