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:
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:
proyek sudah siap
['data', 'src']
['modul']- Baris 3 (
Path("proyek").mkdir(exist_ok=True)): membuat folderproyek;exist_ok=Truemembuat pemanggilan ulang aman, bukan error. - Baris 6 (
Path("proyek/src/modul").mkdir(parents=True, ...)):parents=Truemembuat seluruh folder yang dibutuhkan di sepanjang jalur —src/dibuat lebih dulu, lalumodul/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:
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:
from pathlib import Path
Path("proyek/data").rmdir()
print("Setelah data dihapus:", sorted(p.name for p in Path("proyek").iterdir()))Output yang Dihasilkan:
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:
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:
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:
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:
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:
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:
mentah-salinan.csv
mentah.csv
nama,kota
Budi,JakartaFungsi 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.