Proyek nyata jarang berupa satu berkas datar: kode di src/, data di data/, hasil uji di tests/. Mengelola struktur folder secara program — membuat, mengisi, menelusuri, lalu membersihkannya — adalah keterampilan wajib. Setelah Chapter Bekerja dengan pathlib mengenalkan object Path, chapter ini memakainya untuk mengatur direktori, ditambah satu module pendukung: shutil untuk operasi salin. Semua operasi di sini bekerja relatif terhadap direktori kerja program, sama seperti open().

Membuat Direktori dengan mkdir() #

Method Path.mkdir() membuat satu direktori, dan dua argument-nya menjawab dua masalah klasik:

python
from pathlib import Path

Path("proyek").mkdir(exist_ok=True)
print("proyek sudah siap")

Path("proyek/src/modul").mkdir(parents=True, exist_ok=True)
Path("proyek/src/modul/__init__.py").write_text("", encoding="utf-8")
Path("proyek/data").mkdir(parents=True, exist_ok=True)

print(sorted(p.name for p in Path("proyek").iterdir()))
print(sorted(p.name for p in Path("proyek/src").iterdir()))

Output yang Dihasilkan:

text
proyek sudah siap
['data', 'src']
['modul']
  1. Baris 3 (Path("proyek").mkdir(exist_ok=True)): membuat folder proyek; exist_ok=True membuat pemanggilan ulang aman, bukan error.
  2. Baris 6 (Path("proyek/src/modul").mkdir(parents=True, ...)): parents=True membuat seluruh folder yang dibutuhkan di sepanjang jalur — src/ dibuat lebih dulu, lalu modul/ di dalamnya. Tanpa argument ini, mkdir() menolak membuat folder yang orang tuanya belum ada.

Tanpa exist_ok=True, mkdir() pada folder yang sudah ada memicu FileExistsError:

text
FileExistsError - [Errno 17] File exists: 'proyek'

Padanan os.path untuk pola ini adalah os.makedirs(..., exist_ok=True) — pathlib menyatukan kedua perilaku dalam satu method.

Menghapus Direktori dengan rmdir() #

Method rmdir() menghapus direktori yang kosong:

python
from pathlib import Path

Path("proyek/data").rmdir()

print("Setelah data dihapus:", sorted(p.name for p in Path("proyek").iterdir()))

Output yang Dihasilkan:

text
Setelah data dihapus: ['src']

Folder data lenyap dari daftar isi proyek. Namun rmdir() menolak direktori yang masih berisi — perlindungan agar program tidak menghapus data secara tak sengaja:

text
OSError - [Errno 39] Directory not empty: 'proyek/src'

Folder src masih berisi modul/, sehingga penghapusan ditolak. Untuk mengosongkannya, hapus isinya dulu satu per satu; module shutil juga menyediakan shutil.rmtree() yang menghapus seluruh pohon direktori sekaligus — senjata yang hanya boleh dipakai dengan jalur yang sangat yakin.

Menjelajah Isi: iterdir() dan os.listdir() #

Mengambil isi sebuah folder punya dua API: os.listdir() klasik yang mengembalikan list berisi nama sebagai str, dan Path.iterdir() yang menghasilkan object Path satu per satu:

python
from pathlib import Path

print(sorted(p.name for p in Path("proyek").iterdir()))

Method iterdir() hemat memori untuk folder berisi puluhan ribu berkas karena tidak membangun list utuh, dan setiap elemennya siap dipakai — nama bisa diambil lewat p.name, ekstensi lewat p.suffix, tanpa perakitan string manual. Pemanggilan sorted() pada contoh di atas tetap dianjurkan agar urutan hasil pasti; urutan mentah dari disk tidak dijamin. Untuk pemfilteran sederhana seperti “hanya berkas .csv”, Path.glob("*.csv") pada Chapter Bekerja dengan pathlib sering lebih ringkas daripada mengolah hasil iterdir() sendiri.

Menelusuri Pohon Direktori dengan walk() #

Untuk menelusuri folder dan seluruh subfoldernya sekaligus, method Path.walk() (hadir di Python 3.12) menghasilkan tiga nilai per folder: path folder, daftar nama subfolder, dan daftar nama berkas:

python
from pathlib import Path

root = Path("laporan")
(root / "2024").mkdir(parents=True)
(root / "2025" / "catatan").mkdir(parents=True)
(root / "readme.txt").write_text("Arsip laporan tahunan", encoding="utf-8")
(root / "2024" / "q1.txt").write_text("Laporan Q1 2024", encoding="utf-8")
(root / "2024" / "q2.txt").write_text("Laporan Q2 2024", encoding="utf-8")
(root / "2025" / "q1.txt").write_text("Laporan Q1 2025", encoding="utf-8")
(root / "2025" / "catatan" / "draft.txt").write_text("Draft laporan", encoding="utf-8")

for folder, subfolder, berkas in root.walk():
    print(folder, "|", sorted(subfolder), "|", sorted(berkas))

Output yang Dihasilkan:

text
laporan | ['2024', '2025'] | ['readme.txt']
laporan/2024 | [] | ['q1.txt', 'q2.txt']
laporan/2025 | ['catatan'] | ['q1.txt']
laporan/2025/catatan | [] | ['draft.txt']

Penelusuran dimulai dari folder teratas lalu turun ke setiap subfolder — pola yang pas untuk mengumpulkan semua berkas .txt, menghitung total ukuran, atau membackup. Nilai subfolder dan berkas berisi nama saja (bukan path penuh); rakit dengan folder / nama saat butuh lokasi lengkap. Versi klasiknya adalah fungsi os.walk() dengan hasil tuple yang sama; Path.walk() membawa kelebihan: nilai pertamanya sudah berupa Path yang bisa langsung dirakit dengan operator /. Kebalikan urutan penelusuran (folder terdalam dulu) tersedia lewat walk(top_down=False).

Menyalin Berkas dengan shutil.copy() #

Menghapus bisa oleh Path, tetapi menyalin milik module shutil:

python
import shutil
from pathlib import Path

Path("proyek/data").mkdir(exist_ok=True)
Path("proyek/data/mentah.csv").write_text("nama,kota\nBudi,Jakarta\n", encoding="utf-8")

shutil.copy("proyek/data/mentah.csv", "proyek/data/mentah-salinan.csv")

for berkas in sorted(Path("proyek/data").iterdir()):
    print(berkas.name)
print(Path("proyek/data/mentah-salinan.csv").read_text(encoding="utf-8"), end="")

Output yang Dihasilkan:

text
mentah-salinan.csv
mentah.csv
nama,kota
Budi,Jakarta

Fungsi shutil.copy() menyalin isi berkas beserta izin aksesnya, namun tanpa metadata waktu — shutil.copy2() melengkapi metadata tersebut untuk kebutuhan backup. Berbeda dengan read_text() + write_text(), penyalinan lewat shutil bekerja untuk semua jenis berkas termasuk biner, tanpa kita memikirkan encoding sama sekali.