Ketika package pertama kita lahir, strukturnya terasa seadanya dan itu tidak masalah. Namun struktur yang dipilih di awal menentukan biaya perawatan bertahun-tahun ke depan: nama module yang buruk sulit diubah setelah banyak kode mengimpornya, dan folder yang tumbuh tanpa prinsip berubah menjadi tumpukan file yang tidak berani disentuh siapa pun. Pada Chapter penutup section ini kita merancang struktur package yang sehat — mulai dari aturan penamaan, prinsip kohesi, pilihan antara layout datar dan src layout, sampai mengenali penyakit struktural paling umum: circular import.
Aturan Penamaan Module dan Package #
Nama module menjadi bagian dari kode semua penggunanya — ditulis, dibaca, dan diketik setiap hari — sehingga pilihan nama adalah keputusan API, bukan urusan kosmetik. Aturan mainnya:
- Semua huruf kecil, tanpa tanda hubung:
analisis_nilai, bukanAnalisisNilaiatauanalisis-nilai. - Pendek dan deskriptif; garis bawah dipakai bila memang membantu keterbacaan (ini juga saran PEP 8).
- Jangan bentrok dengan nama standard library —
json.py,random.py,os.py— dan waspadai nama umum sepertitestyang bisa menabrak alat lain.
Tanda hubung memang dilarang keras karena bukan identifier Python yang sah. Mencobanya langsung membuktikannya:
$ python3 -c "import kasir-app"
File "<string>", line 1
import kasir-app
^
SyntaxError: invalid syntaxPenamaan folder proyek boleh memakai tanda hubung (repo GitHub penuh dengan nama seperti analisis-nilai), tetapi package yang diimpor di dalamnya harus tetap nama identifier yang sah — kompromi umumnya seperti pasangan nama repo analisis-nilai dengan package analisis_nilai.
Prinsip Kohesi: Satu Module, Satu Tanggung Jawab #
Struktur yang baik mengelompokkan kode berdasarkan tanggung jawab domain — produk, pembayaran, laporan — bukan berdasarkan jenis teknisnya seperti folder fungsi, data, atau helper. Pengelompokan teknis membuat satu perubahan fitur menyentuh banyak folder sekaligus; pengelompokan domain menjaga kode yang berubah bersama tetap bersebelahan.
Kita terapkan pada proyek kecil analisis nilai siswa dengan layout datar (flat layout):
analisis-nilai/
├── main.py
└── analisis_nilai/
├── __init__.py
├── hitung.py
├── laporan.py
└── predikat.pySetiap module memegang satu tanggung jawab yang bisa dijelaskan dalam satu kalimat. Isi hitung.py:
def rata_rata(nilai):
return sum(nilai) / len(nilai)
def nilai_tertinggi(nilai):
return max(nilai)Isi predikat.py:
def predikat_nilai(nilai):
if nilai >= 90:
return "A"
if nilai >= 80:
return "B"
if nilai >= 70:
return "C"
return "D"Isi laporan.py, yang memakai dua module lain secara relative:
from .hitung import rata_rata
from .predikat import predikat_nilai
def buat_laporan(nama, nilai):
rata = rata_rata(nilai)
predikat = predikat_nilai(rata)
return f"{nama}: rata-rata {rata:.1f}, predikat {predikat}"File __init__.py menjadi etalase resmi package — hanya tiga baris:
from .hitung import rata_rata
from .predikat import predikat_nilai
from .laporan import buat_laporanKode program utama ditaruh di luar package, pada file main.py:
from analisis_nilai import buat_laporan
nilai_budi = [85, 90, 78]
nilai_siti = [92, 95, 88]
print(buat_laporan("Budi", nilai_budi))
print(buat_laporan("Siti", nilai_siti))Output yang Dihasilkan:
Budi: rata-rata 84.3, predikat B
Siti: rata-rata 91.7, predikat AMari kita bedah keputusan strukturalnya:
main.pydi luar package: kode program (titik masuk) dipisahkan dari kode yang bisa diimpor — prinsip yang sama yang diperkuat polaif __name__ == "__main__"pada Chapter Main Module dan name.__init__.pysebagai etalase: pengguna cukupfrom analisis_nilai import buat_laporantanpa perlu tahu ada tiga file di dalamnya; kita bebas menata ulang isi internal tanpa memecah kode pengguna.- Import antar-module memakai bentuk relative:
from .hitung import rata_ratamenjaga package tetap utuh bila suatu hari diubah nama foldernya.
Flat Layout atau src Layout #
Layout di atas menaruh package analisis_nilai langsung di akar proyek — disebut flat layout. Alternatifnya, src layout menaruh package di dalam folder src/:
analisis-nilai/
├── pyproject.toml
└── src/
└── analisis_nilai/
├── __init__.py
├── hitung.py
├── laporan.py
└── predikat.pyFile pyproject.toml berisi metadata proyek untuk proses pemasangan (topik section Packaging dan Deployment).
| Aspek | Flat layout | src layout |
|---|---|---|
| Jalur package | langsung di akar proyek | di dalam src/ |
| Import dari akar proyek | langsung berhasil | gagal — perlu instalasi (pip install -e .) atau PYTHONPATH |
| Risiko menguji kode yang belum dipasang | ada — akar proyek ikut sys.path | tidak ada |
| Cocok untuk | skrip dan proyek kecil | package yang akan didistribusikan |
Dengan src layout, import analisis_nilai dari akar proyek gagal — justru itu tujuannya: satu-satunya cara memakai package adalah memasangnya resmi lewat pip install -e ., sehingga pengujian selalu memakai kode yang akan didistribusikan. Untuk proyek latihan dan aplikasi skrip, flat layout lebih sederhana dan memadai; pilihan antar keduanya memang selera yang diperdebatkan sehat di komunitas.
Waspadai Circular Import #
Penyakit struktural paling umum: dua module saling mengimpor. Reproduksinya sederhana — layanan.py:
from penyimpanan import simpan_data
def proses_pesanan(pesanan):
simpan_data(pesanan)
return "selesai"penyimpanan.py:
from layanan import proses_pesanan
def simpan_data(data):
return "tersimpan"Output yang Dihasilkan:
Traceback (most recent call last):
File "/home/budi/belajar/siklus/layanan.py", line 1, in <module>
from penyimpanan import simpan_data
File "/home/budi/belajar/siklus/penyimpanan.py", line 1, in <module>
from layanan import proses_pesanan
File "/home/budi/belajar/siklus/layanan.py", line 1, in <module>
from penyimpanan import simpan_data
ImportError: cannot import name 'simpan_data' from partially initialized module 'penyimpanan' (most likely due to a circular import) (/home/budi/belajar/siklus/penyimpanan.py)Baca urutannya: layanan mulai dimuat, mengimpor penyimpanan, yang ternyata mengimpor balik layanan yang belum selesai dimuat — lingkaran itu berakhir di ImportError dengan pesan partially initialized module. Solusinya bukan trik import, melainkan perancangan ulang: pindahkan bagian yang saling dibutuhkan ke module ketiga di bawah keduanya, sehingga arah import kembali searah.
Section Modules dan Packages sampai di sini tuntas: kita bisa mengimpor, membuat module sendiri, menelusuri pencarian, menyusun package, membentuk API publik, memilih gaya import, menjalankan sekaligus mengimpor satu file, dan merancang struktur yang sehat. Ketika program kita menabrak kondisi tak terduga, Python menyediakan mekanisme penanganan yang layak dikuasai sejauh ini — section Exception Handling menunggu kita di babak berikutnya.