1. Pengenalan Pathlib
pathlib adalah modul standar Python yang diperkenalkan di Python 3.4 untuk menyediakan cara modern dan berorientasi objek dalam bekerja dengan file system. Modul ini menggantikan pendekatan lama menggunakan os.path dengan pendekatan yang lebih elegan dan Pythonic.
Sebelum pathlib, programmer Python harus mengandalkan fungsi-fungsi dari modul os dan os.path yang menggunakan string untuk merepresentasikan path. Pendekatan ini sering kali menghasilkan kode yang sulit dibaca dan rentan terhadap kesalahan.
Mengapa Menggunakan Pathlib?
| Keunggulan | Penjelasan |
|---|---|
| OOP Approach | Path direpresentasikan sebagai objek, bukan string biasa |
| Cross-Platform | Otomatis menangani perbedaan separator path Windows dan Unix |
| Method Chaining | Bisa menggabungkan beberapa operasi dalam satu baris |
| Built-in Operations | File read/write, globbing, dan operasi directory tersedia langsung |
| Readable Code | Kode lebih mudah dibaca dan dipahami dibanding os.path |
| Type Safety | Menggunakan objek Path, bukan string yang bisa salah format |
Perbandingan: os.path vs pathlib
# ===== CARA LAMA: os.path =====
import os
base_dir = os.path.expanduser("~")
project_dir = os.path.join(base_dir, "projects", "myapp")
config_file = os.path.join(project_dir, "config.json")
if os.path.exists(config_file):
with open(config_file, 'r') as f:
content = f.read()
# Mendapatkan ekstensi file
ext = os.path.splitext(config_file)[1] # '.json'
# Mendapatkan nama file tanpa ekstensi
name = os.path.splitext(os.path.basename(config_file))[0] # 'config'
# ===== CARA BARU: pathlib =====
from pathlib import Path
base_dir = Path.home()
project_dir = base_dir / "projects" / "myapp"
config_file = project_dir / "config.json"
if config_file.exists():
content = config_file.read_text()
# Mendapatkan ekstensi file
ext = config_file.suffix # '.json'
# Mendapatkan nama file tanpa ekstensi
name = config_file.stem # 'config'
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ STRUKTUR PATH OBJECT โ
โ โ
โ /home/user/projects/myapp/config.json โ
โ โโโ anchor: "/" โ
โ โโโ root: "/" โ
โ โโโ parent: /home/user/projects/myapp โ
โ โโโ parents: [/home/user/projects, โ
โ โ /home/user, /home, /] โ
โ โโโ name: "config.json" โ
โ โโโ stem: "config" โ
โ โโโ suffix: ".json" โ
โ โโโ suffixes: [".json"] โ
โ โโโ parts: ("/", "home", "user", "projects", โ
โ โ "myapp", "config.json") โ
โ โโโ as_posix(): "home/user/projects/myapp/config.json"โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Pathlib tersedia secara bawaan di Python 3.4+. Tidak perlu instalasi tambahan. Untuk Python versi lama, gunakan pip install pathlib2.
2. Membuat Path Objects
Ada beberapa cara untuk membuat objek Path di Python. Modul pathlib menyediakan dua kelas utama: Path (untuk sistem operasi saat ini) dan PurePath (untuk operasi yang tidak memerlukan akses file system).
Path dan PurePath
from pathlib import Path, PurePath, PurePosixPath, PureWindowsPath
# Menggunakan Path (otomatis sesuai OS saat ini)
p1 = Path("documents/report.pdf")
p2 = Path.home() / "documents" / "report.pdf"
p3 = Path.cwd() / "data" / "input.csv"
# Menggunakan PurePath (tanpa akses file system)
pp1 = PurePath("folder/subfolder/file.txt")
pp2 = PurePosixPath("/home/user/file.txt") # Format Unix
pp3 = PureWindowsPath("C:\\Users\\user\\file.txt") # Format Windows
# Dari string yang sudah ada
path_str = "/home/user/data/file.csv"
p4 = Path(path_str)
# Dari beberapa bagian
p5 = Path("/home", "user", "data", "file.csv")
# Dari tuple
parts = ("/home", "user", "data", "file.csv")
p6 = Path(*parts)
print(type(p1)) # <class 'pathlib.PosixPath'> (Linux/Mac)
print(type(p1)) # <class 'pathlib.WindowsPath'> (Windows)
# PurePath bisa digunakan lintas platform
# Ini berguna saat menulis kode yang memproses path dari OS lain
print(PurePosixPath("/usr/bin/python3")) # Selalu format Unix
print(PureWindowsPath("C:/Python312/python.exe")) # Selalu format Windows
Path Khusus
from pathlib import Path
# Path direktori home user
home = Path.home()
print(home) # /home/user (Linux) atau C:\Users\user (Windows)
# Path direktori kerja saat ini
cwd = Path.cwd()
print(cwd) # /home/user/projects
# Path absolut dari path relatif
rel_path = Path("data/input.csv")
abs_path = rel_path.resolve()
print(abs_path) # /home/user/projects/data/input.csv
# Membuat Path dari ekspansi tilde (~)
tilde_path = Path("~/documents/file.txt")
expanded = tilde_path.expanduser()
print(expanded) # /home/user/documents/file.txt
# Menggunakan operator /
# Operator / bisa digunakan untuk menggabungkan path
project = Path("/home/user/projects/myapp")
src = project / "src"
main = src / "main.py"
print(main) # /home/user/projects/myapp/src/main.py
# Menggabungkan dengan path relatif
config = project / "config" / "settings.json"
print(config)
# Chain beberapa level sekaligus
deep_path = Path.home() / "projects" / "web" / "src" / "components" / "App.tsx"
print(deep_path)
Konversi Path
from pathlib import Path
p = Path("/home/user/documents/report.pdf")
# Konversi ke string
path_str = str(p)
print(type(path_str)) # <class 'str'>
# Konversi ke bytes
path_bytes = bytes(p)
print(type(path_bytes)) # <class 'bytes'>
# Format POSIX (menggunakan /)
posix = p.as_posix()
print(posix) # /home/user/documents/report.pdf
# Format URI
uri = p.as_uri()
print(uri) # file:///home/user/documents/report.pdf
# Hanya untuk path absolut
try:
Path("relative/path").as_uri()
except ValueError as e:
print(f"Error: {e}") # relative path can't be expressed as a file URI
# Menggunakan f-string
p = Path("data") / "output" / "results.json"
print(f"File: {p}") # File: data/output/results.json
print(f"Exists: {p.exists()}") # Exists: False
3. Properti dan Atribut Path
Objek Path memiliki banyak properti yang memudahkan kita untuk mengakses berbagai komponen dari sebuah path tanpa perlu memanipulasi string secara manual.
Komponen Dasar Path
from pathlib import Path
p = Path("/home/user/projects/myapp/src/main.py")
# Nama file lengkap (termasuk ekstensi)
print(f"name: {p.name}") # main.py
# Nama tanpa ekstensi (stem)
print(f"stem: {p.stem}") # main
# Ekstensi file
print(f"suffix: {p.suffix}") # .py
# Semua ekstensi (untuk file seperti archive.tar.gz)
p2 = Path("archive.tar.gz")
print(f"suffixes: {p2.suffixes}") # ['.tar', '.gz']
print(f"stem: {p2.stem}") # archive.tar
print(f"suffix: {p2.suffix}") # .gz
# Mengganti nama file
new_name = p.with_name("test.py")
print(f"with_name: {new_name}") # /home/user/projects/myapp/src/test.py
# Mengganti stem
new_stem = p.with_stem("utils")
print(f"with_stem: {new_stem}") # /home/user/projects/myapp/src/utils.py
# Mengganti ekstensi
new_suffix = p.with_suffix(".txt")
print(f"with_suffix: {new_suffix}") # /home/user/projects/myapp/src/main.txt
# Bagian-bagian path (parts)
print(f"parts: {p.parts}")
# ('/', 'home', 'user', 'projects', 'myapp', 'src', 'main.py')
Parent dan Ancestors
from pathlib import Path
p = Path("/home/user/projects/myapp/src/main.py")
# Parent directory (satu level ke atas)
print(f"parent: {p.parent}")
# /home/user/projects/myapp/src
# Semua parent (dari terdekat ke terjauh)
print(f"parents: {list(p.parents)}")
# [PosixPath('/home/user/projects/myapp/src'),
# PosixPath('/home/user/projects/myapp'),
# PosixPath('/home/user/projects'),
# PosixPath('/home/user'),
# PosixPath('/home'),
# PosixPath('/')]
# Mengakses parent tertentu dengan index
print(f"parents[0]: {p.parents[0]}") # /home/user/projects/myapp/src
print(f"parents[1]: {p.parents[1]}") # /home/user/projects/myapp
print(f"parents[2]: {p.parents[2]}") # /home/user/projects
# Menggunakan operator /
# Naik ke parent lalu turun ke path lain
sibling = p.parent / "utils.py"
print(f"sibling: {sibling}")
# /home/user/projects/myapp/src/utils.py
# Naik 2 level dan turun ke folder lain
tests = p.parents[1] / "tests" / "test_main.py"
print(f"tests: {tests}")
# /home/user/projects/myapp/tests/test_main.py
# Relative path dari satu path ke path lain
base = Path("/home/user/projects")
target = Path("/home/user/projects/myapp/src/main.py")
rel = target.relative_to(base)
print(f"relative: {rel}") # myapp/src/main.py
Path Absolut dan Relatif
from pathlib import Path
# Mengecek apakah path absolut
print(Path("/home/user").is_absolute()) # True
print(Path("relative/path").is_absolute()) # False
# Mengubah ke path absolut
rel = Path("data/output.csv")
abs_path = rel.resolve()
print(f"Absolute: {abs_path}")
# Mendapatkan path relatif
cwd = Path.cwd()
target = Path.home() / "documents" / "file.txt"
try:
rel_path = target.relative_to(cwd)
print(f"Relative: {rel_path}")
except ValueError:
print("Tidak bisa membuat path relatif (berbeda root)")
# is_relative_to (Python 3.9+)
target = Path("/home/user/projects/myapp/main.py")
print(target.is_relative_to("/home/user/projects")) # True
print(target.is_relative_to("/tmp")) # False
# Anchor (root + drive)
p_win = Path("C:/Users/user/file.txt")
# print(p_win.anchor) # 'C:\\' di Windows
p_unix = Path("/home/user/file.txt")
print(p_unix.anchor) # '/'
4. Membaca dan Menulis File
Salah satu keunggulan terbesar pathlib adalah kemampuannya untuk membaca dan menulis file secara langsung dari objek Path tanpa perlu menggunakan open().
Membaca File
from pathlib import Path
config_path = Path("config.json")
# Membaca sebagai teks (satu baris!)
if config_path.exists():
content = config_path.read_text(encoding="utf-8")
print(content)
# Membaca sebagai binary
image_path = Path("images/logo.png")
if image_path.exists():
data = image_path.read_bytes()
print(f"Size: {len(data)} bytes")
# Membaca per baris
lines_path = Path("data.csv")
if lines_path.exists():
# read_text() lalu split
lines = lines_path.read_text().splitlines()
for line in lines[:5]: # 5 baris pertama
print(line)
# Membaca dengan open() untuk kontrol lebih
log_path = Path("app.log")
if log_path.exists():
with log_path.open("r", encoding="utf-8") as f:
for line in f:
if "ERROR" in line:
print(line.strip())
# Context manager dari pathlib
with Path("data.txt").open("r") as f:
content = f.read()
Menulis File
from pathlib import Path
import json
output_dir = Path("output")
output_dir.mkdir(parents=True, exist_ok=True)
# Menulis teks (satu baris!)
text_file = output_dir / "report.txt"
text_file.write_text("Laporan harian\nTanggal: 26 Juni 2026\n", encoding="utf-8")
# Menulis binary
binary_file = output_dir / "data.bin"
binary_file.write_bytes(b'\x00\x01\x02\x03\x04')
# Menulis JSON
config = {
"app_name": "MyApp",
"version": "1.0.0",
"debug": False,
"database": {
"host": "localhost",
"port": 5432
}
}
config_path = output_dir / "config.json"
config_path.write_text(json.dumps(config, indent=2), encoding="utf-8")
# Menggunakan open() untuk menulis dengan kontrol lebih
log_path = output_dir / "app.log"
with log_path.open("a", encoding="utf-8") as f: # mode append
f.write("[2026-06-26] Aplikasi dimulai\n")
f.write("[2026-06-26] Konfigurasi dimuat\n")
# Menulis beberapa baris sekaligus
lines = ["Baris 1", "Baris 2", "Baris 3", "Baris 4"]
multi_path = output_dir / "multi.txt"
multi_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
write_text() dan write_bytes() akan menimpa file yang sudah ada. Gunakan mode "a" (append) via open() jika ingin menambahkan di akhir file.
5. Navigasi Directory
Pathlib menyediakan berbagai method untuk menavigasi dan menjelajahi struktur direktori.
List Directory
from pathlib import Path
project_dir = Path(".")
# List semua item di direktori
print("=== Semua Item ===")
for item in project_dir.iterdir():
print(f" {'๐' if item.is_dir() else '๐'} {item.name}")
# Hanya file
print("\n=== Hanya File ===")
files = [f for f in project_dir.iterdir() if f.is_file()]
for f in files:
print(f" ๐ {f.name} ({f.stat().st_size} bytes)")
# Hanya direktori
print("\n=== Hanya Direktori ===")
dirs = [d for d in project_dir.iterdir() if d.is_dir()]
for d in dirs:
print(f" ๐ {d.name}")
# List dengan filter ekstensi
python_files = list(project_dir.glob("*.py"))
print(f"\nFile Python: {len(python_files)}")
for f in python_files:
print(f" {f.name}")
# List sorted by name
all_files = sorted(project_dir.iterdir(), key=lambda p: p.name)
for f in all_files:
print(f" {f.name}")
# List sorted by modification time
from datetime import datetime
recent_files = sorted(
[f for f in project_dir.iterdir() if f.is_file()],
key=lambda p: p.stat().st_mtime,
reverse=True
)
for f in recent_files[:10]:
mtime = datetime.fromtimestamp(f.stat().st_mtime)
print(f" {f.name} โ {mtime:%Y-%m-%d %H:%M}")
Recursive Traversal
from pathlib import Path
def print_tree(directory, prefix="", max_depth=3, current_depth=0):
"""Mencetak struktur direktori seperti tree command."""
if current_depth > max_depth:
return
items = sorted(directory.iterdir(), key=lambda p: (p.is_file(), p.name))
for i, item in enumerate(items):
is_last = (i == len(items) - 1)
connector = "โโโ " if is_last else "โโโ "
print(f"{prefix}{connector}{item.name}")
if item.is_dir():
extension = " " if is_last else "โ "
print_tree(item, prefix + extension, max_depth, current_depth + 1)
# Cetak struktur project
project = Path("my_project")
print(f"๐ {project.name}/")
print_tree(project)
# Menggunakan rglob untuk rekursif
print("\n=== Semua file .py secara rekursif ===")
for py_file in Path(".").rglob("*.py"):
print(f" {py_file}")
# Walk manual dengan level kedalaman
def walk_with_depth(path, depth=0, max_depth=2):
"""Generator yang yield (path, depth) tuples."""
if depth > max_depth:
return
for item in sorted(path.iterdir()):
yield item, depth
if item.is_dir():
yield from walk_with_depth(item, depth + 1, max_depth)
for path, depth in walk_with_depth(Path(".")):
indent = " " * depth
icon = "๐" if path.is_dir() else "๐"
print(f"{indent}{icon} {path.name}")
6. Globbing dan Pattern Matching
Globbing adalah salah satu fitur paling powerful dari pathlib. Dengan glob, kita bisa mencari file dan direktori berdasarkan pola tertentu.
Dasar Glob
from pathlib import Path
project = Path(".")
# glob() โ mencari di direktori saat ini saja
# * = semua file
all_items = list(project.glob("*"))
print(f"Semua item: {len(all_items)}")
# *.py = semua file Python
py_files = list(project.glob("*.py"))
for f in py_files:
print(f" {f}")
# *.txt = semua file teks
txt_files = list(project.glob("*.txt"))
# ? = satu karakter apapun
# Mencari file dengan nama 1 karakter + .py
single_char = list(project.glob("?.py"))
print(f"File 1 karakter .py: {single_char}")
# [abc] = karakter dalam kurung
abc_files = list(project.glob("[abc]*.py"))
print(f"File dimulai a/b/c: {abc_files}")
# [0-9] = range karakter
num_files = list(project.glob("[0-9]*"))
print(f"File dimulai angka: {num_files}")
# ** = rekursif ke semua subdirektori
all_py_recursive = list(project.rglob("*.py"))
print(f"Semua .py (rekursif): {len(all_py_recursive)}")
# rglob() = shorthand untuk **/pattern
# project.rglob("*.py") sama dengan project.glob("**/*.py")
Pattern Lanjutan
from pathlib import Path
base = Path("src")
# Mencari semua file Python di subdirektori src
for py in base.rglob("*.py"):
print(py)
# Mencari file test
test_files = list(base.rglob("test_*.py"))
print(f"Test files: {len(test_files)}")
# Mencari file dengan beberapa ekstensi
import itertools
extensions = ["*.py", "*.js", "*.ts", "*.html", "*.css"]
source_files = []
for ext in extensions:
source_files.extend(base.rglob(ext))
print(f"Source files: {len(source_files)}")
# Atau gunakan itertools.chain
from itertools import chain
all_sources = list(chain.from_iterable(
base.rglob(ext) for ext in ["*.py", "*.js", "*.ts"]
))
# Mencari file berdasarkan pattern kompleks
# File yang mengandung "config" di nama
config_files = list(base.rglob("*config*"))
print(f"Config files: {config_files}")
# File dengan ganda ekstensi (misal .tar.gz)
archives = list(base.rglob("*.tar.gz"))
# Mencari hanya direktori (folders)
all_dirs = [p for p in base.rglob("*") if p.is_dir()]
print(f"Directories: {len(all_dirs)}")
# Pattern dengan negasi menggunakan filter
non_test = [f for f in base.rglob("*.py") if not f.name.startswith("test_")]
print(f"Non-test Python files: {len(non_test)}")
Filter Hasil Glob
from pathlib import Path
from datetime import datetime, timedelta
base = Path(".")
# Filter berdasarkan ukuran
large_files = [
f for f in base.rglob("*")
if f.is_file() and f.stat().st_size > 1_000_000 # > 1MB
]
for f in large_files:
size_mb = f.stat().st_size / 1_000_000
print(f" {f} โ {size_mb:.2f} MB")
# Filter berdasarkan waktu modifikasi
one_week_ago = datetime.now() - timedelta(days=7)
recent_files = [
f for f in base.rglob("*.py")
if f.is_file() and datetime.fromtimestamp(f.stat().st_mtime) > one_week_ago
]
print(f"File Python dimodifikasi minggu ini: {len(recent_files)}")
# Filter berdasarkan nama (menggunakan regex)
import re
pattern = re.compile(r"^v\d+\.\d+\.\d+.*\.py$")
versioned = [
f for f in base.rglob("*.py")
if pattern.match(f.name)
]
# Menggabungkan beberapa kriteria
def is_interesting(path: Path) -> bool:
"""File yang menarik: Python, > 100 baris, bukan test."""
if not path.suffix == ".py":
return False
if path.name.startswith("test_"):
return False
if path.name.startswith("__"):
return False
try:
lines = path.read_text().count('\n')
return lines > 100
except (OSError, UnicodeDecodeError):
return False
interesting = [f for f in base.rglob("*.py") if is_interesting(f)]
print(f"File Python menarik: {len(interesting)}")
7. Operasi File dan Directory
Pathlib menyediakan method-method untuk melakukan operasi file system seperti membuat, menghapus, menyalin, dan memindahkan file.
Membuat Directory
from pathlib import Path
# Membuat satu direktori
new_dir = Path("output")
new_dir.mkdir(exist_ok=True) # Tidak error jika sudah ada
# Membuat direktori bersarang (parent=True)
deep_dir = Path("output/reports/2026/juni")
deep_dir.mkdir(parents=True, exist_ok=True)
print(f"Created: {deep_dir}")
# Membuat direktori sementara
import tempfile
# Temporary directory dengan pathlib
with tempfile.TemporaryDirectory() as tmp:
tmp_path = Path(tmp)
(tmp_path / "test.txt").write_text("Hello!")
print(f"Temp dir: {tmp_path}")
print(f"Files: {list(tmp_path.iterdir())}")
# Membuat struktur project lengkap
def create_project_structure(base: Path):
"""Membuat struktur project Python standar."""
dirs = [
base / "src" / "mypackage",
base / "tests",
base / "docs",
base / "data" / "raw",
base / "data" / "processed",
base / "notebooks",
]
for d in dirs:
d.mkdir(parents=True, exist_ok=True)
# Buat __init__.py di package
if "package" in str(d):
(d / "__init__.py").touch()
# Buat file standar
(base / "README.md").write_text("# My Project\n")
(base / "requirements.txt").write_text("")
(base / ".gitignore").write_text("__pycache__/\n*.pyc\n.env\n")
(base / "pyproject.toml").write_text("[project]\nname = 'myproject'\n")
print(f"Project structure created at {base}")
create_project_structure(Path("my_project"))
Menghapus File dan Directory
from pathlib import Path
import shutil
# Menghapus file
temp_file = Path("temp.txt")
temp_file.write_text("temporary")
temp_file.unlink() # Menghapus file
# unlink(missing_ok=True) tidak error jika file tidak ada (Python 3.8+)
# Menghapus direktori kosong
empty_dir = Path("empty_folder")
empty_dir.mkdir(exist_ok=True)
empty_dir.rmdir() # Hanya untuk direktori kosong
# Menghapus direktori dan isinya (RECURSIVE)
import shutil
build_dir = Path("build")
if build_dir.exists():
shutil.rmtree(build_dir)
# Safe delete dengan pathlib
def safe_delete(path: Path):
"""Menghapus file atau direktori dengan aman."""
if not path.exists():
print(f"โ ๏ธ {path} tidak ditemukan")
return
if path.is_file():
path.unlink()
print(f"๐๏ธ File dihapus: {path}")
elif path.is_dir():
shutil.rmtree(path)
print(f"๐๏ธ Directory dihapus: {path}")
# Clean cache files
def clean_cache(base: Path):
"""Menghapus semua __pycache__ dan .pyc files."""
count = 0
for cache_dir in base.rglob("__pycache__"):
shutil.rmtree(cache_dir)
count += 1
for pyc in base.rglob("*.pyc"):
pyc.unlink()
count += 1
print(f"๐งน Dihapus {count} cache items")
clean_cache(Path("."))
Menyalin dan Memindahkan
from pathlib import Path
import shutil
src = Path("data/input.csv")
dst = Path("backup/input_backup.csv")
# Pastikan direktori tujuan ada
dst.parent.mkdir(parents=True, exist_ok=True)
# Menyalin file
shutil.copy2(src, dst) # copy2 mempertahankan metadata
# Memindahkan / rename file
old_path = Path("temp_file.txt")
new_path = Path("renamed_file.txt")
old_path.rename(new_path)
# Memindahkan ke direktori lain
target_dir = Path("archive")
target_dir.mkdir(exist_ok=True)
new_path.rename(target_dir / new_path.name)
# Menyalin seluruh direktori
src_dir = Path("project_src")
dst_dir = Path("project_backup")
if src_dir.exists():
shutil.copytree(src_dir, dst_dir, dirs_exist_ok=True)
# Rename dengan pathlib (bisa untuk memindahkan juga)
p = Path("old_name.py")
if p.exists():
p.rename(p.parent / "new_name.py")
# Membuat symlink (symbolic link)
target = Path("original.txt")
link = Path("link_to_original.txt")
if target.exists() and not link.exists():
link.symlink_to(target)
print(f"Symlink: {link} โ {target}")
# Mengecek apakah symlink
if link.is_symlink():
print(f"Target: {link.resolve()}")
8. Permission dan Metadata
Pathlib memungkinkan kita untuk mengakses metadata file dan mengatur permission melalui method stat().
File Statistics
from pathlib import Path
from datetime import datetime
p = Path("data/report.csv")
if p.exists():
stat = p.stat()
# Ukuran file
print(f"Size: {stat.st_size} bytes")
print(f"Size: {stat.st_size / 1024:.2f} KB")
# Waktu
created = datetime.fromtimestamp(stat.st_ctime)
modified = datetime.fromtimestamp(stat.st_mtime)
accessed = datetime.fromtimestamp(stat.st_atime)
print(f"Created: {created:%Y-%m-%d %H:%M:%S}")
print(f"Modified: {modified:%Y-%m-%d %H:%M:%S}")
print(f"Accessed: {accessed:%Y-%m-%d %H:%M:%S}")
# Permission (Unix only)
import octal
mode = stat.st_mode
print(f"Mode: {octal(mode)}")
# Cek tipe file
print(f"Is file: {p.is_file()}")
print(f"Is directory: {p.is_dir()}")
print(f"Is symlink: {p.is_symlink()}")
print(f"Is absolute: {p.is_absolute()}")
else:
print(f"File {p} tidak ditemukan")
# Ringkasan ukuran direktori
def dir_size(path: Path) -> int:
"""Menghitung total ukuran direktori."""
total = 0
for item in path.rglob("*"):
if item.is_file():
total += item.stat().st_size
return total
def format_size(size_bytes: int) -> str:
"""Format ukuran ke string yang readable."""
for unit in ['B', 'KB', 'MB', 'GB']:
if size_bytes < 1024:
return f"{size_bytes:.2f} {unit}"
size_bytes /= 1024
return f"{size_bytes:.2f} TB"
project_dir = Path(".")
total = dir_size(project_dir)
print(f"\nTotal ukuran project: {format_size(total)}")
File Permission (Unix)
import os
from pathlib import Path
script = Path("deploy.sh")
# Mengubah permission (Unix/Linux/Mac)
if script.exists():
# chmod 755 (rwxr-xr-x)
script.chmod(0o755)
print(f"Permission set to 755")
# Membaca permission
mode = script.stat().st_mode
# Cek permission tertentu
is_executable = bool(mode & 0o111)
is_readable = bool(mode & 0o444)
is_writable = bool(mode & 0o222)
print(f"Executable: {is_executable}")
print(f"Readable: {is_readable}")
print(f"Writable: {is_writable}")
# Membuat file executable
def make_executable(path: Path):
"""Membuat file bisa dieksekusi."""
if path.exists():
current = path.stat().st_mode
path.chmod(current | 0o111)
print(f"โ
{path} is now executable")
# Readonly
def make_readonly(path: Path):
"""Membuat file hanya-baca."""
if path.exists():
path.chmod(0o444)
print(f"๐ {path} is now read-only")
# Owner only
def make_private(path: Path):
"""Membuat file hanya bisa diakses owner."""
if path.exists():
path.chmod(0o600)
print(f"๐ {path} is now private (owner only)")
9. Pathlib vs os.path
Berikut perbandingan lengkap antara pathlib modern dengan pendekatan lama menggunakan os.path.
| Operasi | os.path (Lama) | pathlib (Modern) |
|---|---|---|
| Gabung path | os.path.join(a, b) | a / b |
| Parent dir | os.path.dirname(p) | p.parent |
| Nama file | os.path.basename(p) | p.name |
| Ekstensi | os.path.splitext(p)[1] | p.suffix |
| Cek ada | os.path.exists(p) | p.exists() |
| Cek file | os.path.isfile(p) | p.is_file() |
| Cek dir | os.path.isdir(p) | p.is_dir() |
| Path absolut | os.path.abspath(p) | p.resolve() |
| Home dir | os.path.expanduser("~") | Path.home() |
| Read file | open(p).read() | p.read_text() |
| Walk | os.walk(d) | d.rglob("*") |
Kompatibilitas dengan os
from pathlib import Path
import os
# Path object bisa digunakan di mana string diperlukan
p = Path("/home/user/file.txt")
# os.path functions menerima Path object
print(os.path.exists(p)) # True/False
print(os.path.getsize(p)) # Ukuran file
# os functions juga menerima Path object
os.chdir(Path.home())
# Menggunakan Path dengan subprocess
import subprocess
script = Path("/usr/bin/python3")
result = subprocess.run(
[str(script), "--version"], # Perlu convert ke str untuk beberapa kasus
capture_output=True, text=True
)
print(result.stdout)
# Menggunakan fsspec atau pathlib dengan open()
p = Path("data.csv")
with open(p, "r") as f: # Path otomatis diterima
content = f.read()
# Convert antara string dan Path
path_str = "/home/user/file.txt"
p = Path(path_str) # String โ Path
s = str(p) # Path โ String
# Menggunakan os.fspath (Python 3.6+)
print(os.fspath(p)) # Menghasilkan string
10. Best Practices dan Tips
Tips Penggunaan
from pathlib import Path
# โ
GUNAKAN: Operator / untuk gabung path
config = Path.home() / ".config" / "myapp" / "settings.json"
# โ HINDARI: Menggunakan str + concatenation
# config = Path(str(Path.home()) + "/.config/myapp/settings.json")
# โ
GUNAKAN: resolve() untuk path absolut tanpa symlink
abs_path = Path("relative/file.txt").resolve()
# โ
GUNAKAN: exist_ok=True untuk mkdir
Path("output/data").mkdir(parents=True, exist_ok=True)
# โ
GUNAKAN: missing_ok=True untuk unlink (Python 3.8+)
Path("temp_file.txt").unlink(missing_ok=True)
# โ
GUNAKAN: rglob() untuk pencarian rekursif
all_tests = list(Path(".").rglob("test_*.py"))
# โ
GUNAKAN: Type hints dengan Path
def read_config(path: Path) -> dict:
import json
return json.loads(path.read_text())
# โ
GUNAKAN: Path.cwd() dan Path.home()
project_root = Path.cwd()
user_home = Path.home()
# โ
GUNAKAN: with_suffix untuk mengubah ekstensi
source = Path("data.csv")
backup = source.with_suffix(".csv.bak")
# โ
GUNAKAN: iterdir() untuk list direktori
for item in Path(".").iterdir():
print(item)
Common Patterns
from pathlib import Path
from typing import List, Generator
import hashlib
# Pattern 1: Find all files of certain types
def find_files(directory: Path, extensions: List[str]) -> List[Path]:
"""Mencari file berdasarkan ekstensi."""
results = []
for ext in extensions:
results.extend(directory.rglob(f"*{ext}"))
return sorted(results, key=lambda p: p.name)
# Pattern 2: File hash calculator
def file_hash(path: Path, algorithm: str = "sha256") -> str:
"""Menghitung hash file."""
h = hashlib.new(algorithm)
h.update(path.read_bytes())
return h.hexdigest()
# Pattern 3: Safe file writing (write to temp, then rename)
def safe_write(path: Path, content: str, encoding: str = "utf-8"):
"""Menulis file dengan aman (atomic write)."""
import tempfile
tmp_fd, tmp_path = tempfile.mkstemp(dir=path.parent, suffix=".tmp")
try:
tmp = Path(tmp_path)
tmp.write_text(content, encoding=encoding)
tmp.rename(path)
except:
Path(tmp_path).unlink(missing_ok=True)
raise
# Pattern 4: Directory walker dengan yield
def walk_files(directory: Path) -> Generator[Path, None, None]:
"""Generator yang mengyield semua file secara rekursif."""
for item in directory.iterdir():
if item.is_file():
yield item
elif item.is_dir():
yield from walk_files(item)
# Pattern 5: Backup file sebelum overwrite
def backup_write(path: Path, content: str):
"""Menulis file dengan backup otomatis."""
if path.exists():
backup = path.with_suffix(f"{path.suffix}.bak")
import shutil
shutil.copy2(path, backup)
path.write_text(content)
# Pattern 6: Temporary file dengan context manager
import tempfile
from contextlib import contextmanager
@contextmanager
def temp_file(suffix: str = ".tmp"):
"""Context manager untuk temporary file."""
tmp = Path(tempfile.mktemp(suffix=suffix))
try:
yield tmp
finally:
tmp.unlink(missing_ok=True)
# Usage
with temp_file(".json") as tmp:
tmp.write_text('{"test": true}')
print(tmp.read_text())
# File otomatis dihapus
11. Studi Kasus Nyata
Project Organizer
"""
File Organizer โ Mengorganisir file berdasarkan ekstensi
Gunakan: python organizer.py /path/to/downloads
"""
from pathlib import Path
import shutil
import sys
from datetime import datetime
# Mapping ekstensi ke kategori
FILE_TYPES = {
"Images": [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg", ".webp"],
"Documents": [".pdf", ".doc", ".docx", ".txt", ".odt", ".rtf"],
"Spreadsheets":[".xls", ".xlsx", ".csv", ".ods"],
"Presentations":[".ppt", ".pptx", ".odp"],
"Videos": [".mp4", ".avi", ".mkv", ".mov", ".wmv"],
"Audio": [".mp3", ".wav", ".flac", ".aac", ".ogg"],
"Archives": [".zip", ".rar", ".tar", ".gz", ".7z"],
"Code": [".py", ".js", ".ts", ".html", ".css", ".java", ".cpp"],
}
def get_category(suffix: str) -> str:
"""Mendapatkan kategori berdasarkan ekstensi."""
suffix = suffix.lower()
for category, extensions in FILE_TYPES.items():
if suffix in extensions:
return category
return "Others"
def organize_downloads(download_dir: Path, dry_run: bool = True):
"""Mengorganisir file di direktori downloads."""
if not download_dir.is_dir():
print(f"โ {download_dir} bukan direktori!")
return
moved = 0
for item in download_dir.iterdir():
if item.is_dir() or item.name.startswith("."):
continue
category = get_category(item.suffix)
target_dir = download_dir / category
target_file = target_dir / item.name
# Handle duplikat
counter = 1
while target_file.exists():
stem = item.stem
target_file = target_dir / f"{stem}_{counter}{item.suffix}"
counter += 1
if dry_run:
print(f" [DRY RUN] {item.name} โ {category}/")
else:
target_dir.mkdir(exist_ok=True)
shutil.move(str(item), str(target_file))
print(f" โ
{item.name} โ {category}/")
moved += 1
print(f"\n{'Would move' if dry_run else 'Moved'} {moved} files")
# Jalankan
if __name__ == "__main__":
target = Path(sys.argv[1]) if len(sys.argv) > 1 else Path.home() / "Downloads"
print(f"๐ Organizing: {target}\n")
organize_downloads(target, dry_run=True) # Set False untuk eksekusi
Log Analyzer
"""
Log Analyzer โ Menganalisis file log untuk mencari error patterns
"""
from pathlib import Path
from collections import Counter
from datetime import datetime
from typing import Dict, List
def analyze_logs(log_dir: Path) -> Dict:
"""Menganalisis semua file .log di direktori."""
stats = {
"total_files": 0,
"total_lines": 0,
"errors": Counter(),
"warnings": Counter(),
"recent_errors": [],
}
log_files = sorted(log_dir.rglob("*.log"), key=lambda p: p.name)
stats["total_files"] = len(log_files)
for log_file in log_files:
try:
lines = log_file.read_text(encoding="utf-8", errors="replace").splitlines()
stats["total_lines"] += len(lines)
for i, line in enumerate(lines, 1):
if "ERROR" in line:
# Extract error message
msg = line.split("ERROR", 1)[-1].strip()[:100]
stats["errors"][msg] += 1
stats["recent_errors"].append({
"file": str(log_file),
"line": i,
"message": msg,
})
elif "WARNING" in line:
msg = line.split("WARNING", 1)[-1].strip()[:100]
stats["warnings"][msg] += 1
except Exception as e:
print(f"โ ๏ธ Error reading {log_file}: {e}")
return stats
def print_report(stats: Dict):
"""Mencetak laporan analisis."""
print("=" * 60)
print("๐ LOG ANALYSIS REPORT")
print("=" * 60)
print(f"Total log files: {stats['total_files']}")
print(f"Total lines: {stats['total_lines']:,}")
print(f"Error count: {sum(stats['errors'].values()):,}")
print(f"Warning count: {sum(stats['warnings'].values()):,}")
if stats["errors"]:
print("\n๐ด Top 5 Errors:")
for msg, count in stats["errors"].most_common(5):
print(f" [{count:4d}] {msg}")
if stats["warnings"]:
print("\n๐ก Top 5 Warnings:")
for msg, count in stats["warnings"].most_common(5):
print(f" [{count:4d}] {msg}")
# Jalankan
if __name__ == "__main__":
log_directory = Path("/var/log") # Sesuaikan
if log_directory.exists():
stats = analyze_logs(log_directory)
print_report(stats)
Template Generator
"""
Project Template Generator โ Membuat struktur project dari template
"""
from pathlib import Path
TEMPLATES = {
"python": {
"files": {
"README.md": "# {name}\n\nDeskripsi project.\n",
"pyproject.toml": """[project]
name = "{name}"
version = "0.1.0"
description = ""
authors = [{{name = "Author", email = "author@email.com"}}]
requires-python = ">=3.10"
[project.optional-dependencies]
dev = ["pytest", "ruff", "mypy"]
""",
"src/{name_snake}/__init__.py": '"""Package {name}."""\n__version__ = "0.1.0"\n',
"src/{name_snake}/main.py": """def main():
print("Hello from {name}!")
if __name__ == "__main__":
main()
""",
"tests/__init__.py": "",
"tests/test_main.py": """from {name_snake}.main import main
def test_main(capsys):
main()
captured = capsys.readouterr()
assert "Hello" in captured.out
""",
".gitignore": "__pycache__/\n*.pyc\n.env\n*.egg-info/\ndist/\nbuild/\n",
},
"dirs": ["src/{name_snake}", "tests", "docs"],
},
"web": {
"files": {
"README.md": "# {name}\n\nWeb project.\n",
"index.html": "\n\n{name} \n\n",
"css/style.css": "/* {name} styles */\nbody {{ margin: 0; padding: 0; }}\n",
"js/app.js": "// {name} main script\nconsole.log('{name} loaded');\n",
".gitignore": "node_modules/\n.env\ndist/\n",
},
"dirs": ["css", "js", "images"],
},
}
def create_project(name: str, template: str, base_dir: Path):
"""Membuat project dari template."""
if template not in TEMPLATES:
print(f"โ Template '{template}' tidak ditemukan!")
print(f" Tersedia: {', '.join(TEMPLATES.keys())}")
return
t = TEMPLATES[template]
project_dir = base_dir / name
if project_dir.exists():
print(f"โ {project_dir} sudah ada!")
return
# Format variables
vars = {
"name": name,
"name_snake": name.lower().replace("-", "_").replace(" ", "_"),
}
# Create directories
for d in t["dirs"]:
dir_path = project_dir / d.format(**vars)
dir_path.mkdir(parents=True, exist_ok=True)
# Create files
for filepath, content in t["files"].items():
file_path = project_dir / filepath.format(**vars)
file_path.parent.mkdir(parents=True, exist_ok=True)
file_path.write_text(content.format(**vars), encoding="utf-8")
print(f"โ
Project '{name}' created at {project_dir}")
print(f" Template: {template}")
print(f" Files: {len(t['files'])}")
# Usage
create_project("my-api", "python", Path("projects"))
create_project("my-website", "web", Path("projects"))
12. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Python Pathlib: