Package adalah direktori yang menghimpun beberapa modul terkait menjadi satu unit impor yang dipanggil dengan satu nama. Ketika jumlah berkas program bertambah, pengelompokan semacam ini menjaga nama modul tetap teratur dan mencegah tabrakan nama antarbagian program. Topik ini membahas struktur package beserta peran berkas __init__.py, teknik relative import untuk komunikasi antarmodul di dalam package, serta tata letak proyek standar yang dipakai sebagian besar proyek Python.
Package sebagai direktori berisi modul#
Package reguler adalah direktori yang memuat berkas __init__.py beserta modul-modul anggotanya. Modul di dalamnya diimpor dengan notasi titik, misalnya pengelolaan.catatan, sehingga setiap nama modul selalu membawa nama package-nya. Contoh berikut membangun satu package berisi dua modul, lalu mengimpornya.
import sys
import tempfile
from pathlib import Path
akar = Path(tempfile.mkdtemp(prefix="contoh-paket-"))
paket = akar / "pengelolaan"
paket.mkdir()
(paket / "__init__.py").write_text(
'"""Package untuk pengelolaan catatan."""\n'
'TAHUN_MULAI = 2026\n',
encoding="utf-8",
)
(paket / "catatan.py").write_text(
'def simpan(teks):\n'
' return "catatan tersimpan: " + teks\n',
encoding="utf-8",
)
(paket / "statistik.py").write_text(
'def jumlah(nilai):\n'
' return sum(nilai)\n',
encoding="utf-8",
)
sys.path.insert(0, str(akar))
from pengelolaan import catatan, statistik
print(catatan.simpan("daftar belanja"))
print(statistik.jumlah([10, 20, 30]))
import pengelolaan
print(pengelolaan.TAHUN_MULAI)catatan tersimpan: daftar belanja
60
2026Pernyataan from pengelolaan import catatan menarik modul catatan keluar dari package-nya, sehingga pemanggilan fungsi tidak perlu menuliskan awalan panjang. Pada baris terakhir, package itu sendiri diimpor sebagai objek, dan konstanta TAHUN_MULAI yang ditulis di dalam __init__.py terbaca sebagai atribut package. Struktur yang dibangun contoh tersebut digambarkan pada diagram berikut.
Berkas __init__.py dieksekusi satu kali ketika package pertama kali diimpor, sebelum modul-modul anggotanya dimuat. Isinya boleh kosong, tetapi tempat itu juga tepat untuk konstanta bersama atau docstring package. Karena seluruh modul selalu berada di balik nama package, dua package yang berbeda boleh memuat modul dengan nama sama tanpa saling menimpa.
Relative import antarmodul#
Modul sesama anggota package sering saling membutuhkan, dan relative import menyediakan cara menghubungkannya tanpa menuliskan nama package di setiap berkas. Awalan satu titik berarti package yang sama, sedangkan dua titik berarti package di atasnya. Apabila nama package diubah kelak, baris-baris relative import di dalamnya tidak perlu disentuh.
import sys
import tempfile
from pathlib import Path
akar = Path(tempfile.mkdtemp(prefix="contoh-relatif-"))
paket = akar / "penilaian"
paket.mkdir()
(paket / "__init__.py").write_text("", encoding="utf-8")
(paket / "data.py").write_text("NILAI = [78, 65, 91]\n", encoding="utf-8")
(paket / "laporan.py").write_text(
'from .data import NILAI\n'
'\n'
'def rata_rata():\n'
' return sum(NILAI) / len(NILAI)\n',
encoding="utf-8",
)
sys.path.insert(0, str(akar))
from penilaian import laporan
print(laporan.rata_rata())78.0Baris from .data import NILAI pada laporan.py berarti mengambil NILAI dari modul data yang berada pada package yang sama. Bentuk absolut from penilaian.data import NILAI menghasilkan perilaku yang sama, tetapi relative import membuat hubungan antarmodul tampak pada satu pandangan dan tetap benar setelah package diganti namanya. Sebagian besar proyek memilih salah satu gaya, lalu memakainya secara konsisten di seluruh kode.
Tata letak proyek standar#
Proyek yang akan dibagikan umumnya memisahkan kode sumber dari berkas pendukungnya. Tata letak yang paling disarankan adalah src layout, yaitu menempatkan package di dalam folder src, sebagaimana ditunjukkan pohon berikut.
proyek-catatan/
├── pyproject.toml
├── README.md
├── src/
│ └── pengelolaan/
│ ├── __init__.py
│ ├── catatan.py
│ └── laporan.py
└── test/
└── test_catatan.pyFolder src memaksa package diakses melalui versi yang benar-benar terpasang pada lingkungan, bukan melalui direktori kerja yang kebetulan sedang dibuka. Dengan begitu, hasil pengujian mencerminkan keadaan package yang akan dipakai orang lain. Berkas pyproject.toml menampung metadata proyek dan daftar dependensinya, sedangkan folder test memisahkan kode pengujian dari kode produksi. Pemisahan ini juga membuat struktur proyek langsung terbaca oleh alat bantu seperti pengelola lingkungan dan pemeriksa gaya kode. Berkas README.md pada akar proyek menjadi halaman pertama yang dibaca pengguna baru, sehingga tempatnya juga baku pada tata letak ini.
Tata letak alternatif bernama flat layout menaruh package langsung di akar proyek tanpa folder src. Bentuk ini lebih pendek dan banyak dipakai pada proyek lama, tetapi menuntut kehati-hatian ekstra karena direktori kerja dan kode sumber bercampur pada satu tempat. Untuk proyek baru, tata letak src menjadi pilihan default yang lebih aman.
Kesalahan umum#
Kesalahan pertama adalah menjalankan modul di dalam package secara langsung sebagai skrip, misalnya python3 laporan.py dari dalam foldernya. Relative import membutuhkan konteks package, sedangkan eksekusi langsung membuat __package__ kosong, sehingga baris from .data import NILAI gagal.
import subprocess
import sys
import tempfile
from pathlib import Path
akar = Path(tempfile.mkdtemp(prefix="contoh-relatif-"))
paket = akar / "penilaian"
paket.mkdir()
(paket / "__init__.py").write_text("", encoding="utf-8")
(paket / "data.py").write_text("NILAI = [78, 65, 91]\n", encoding="utf-8")
(paket / "laporan.py").write_text(
'from .data import NILAI\n',
encoding="utf-8",
)
# modul di dalam package dijalankan langsung sebagai skrip
hasil = subprocess.run(
[sys.executable, str(paket / "laporan.py")],
capture_output=True,
text=True,
)
print(hasil.stderr.splitlines()[-1])ImportError: attempted relative import with no known parent packagePerbaikannya adalah memanggil modul itu melalui package-nya dengan python3 -m penilaian.laporan dari akar proyek, atau menyediakan satu berkas entry point di luar package yang mengimpornya. Pada tata letak semacam ini, peran itu biasanya dipegang oleh satu berkas seperti utama.py di akar proyek, sehingga tidak ada modul package yang perlu dijalankan sendirian.
Kesalahan kedua adalah circular import, yaitu dua modul yang saling mengimpor pada tingkat atas. Modul yang dimuat lebih dahulu menerima versi setengah jadi dari modul pasangannya, sehingga atribut yang dirujuk belum ada dan program berhenti dengan NameError atau AttributeError. Perbaikannya adalah memindahkan kode yang dipakai bersama ke modul ketiga, atau menunda impor ke dalam badan fungsi yang membutuhkannya.
Kesalahan ketiga adalah menaruh seluruh kode ke dalam __init__.py. Berkas itu dieksekusi pada setiap impor package, sehingga logika berat di dalamnya memperlambat program yang hanya membutuhkan satu modul anggota. Tempat yang tepat untuk logika adalah modul anggota, sedangkan __init__.py cukup berisi konstanta ringan atau pengumpulan nama.
Ringkasan#
- Package reguler adalah direktori dengan
__init__.pyyang modulnya diimpor lewat notasi titik sepertipengelolaan.catatan. __init__.pydieksekusi sekali saat package pertama kali diimpor dan menjadi tempat konstanta bersama tingkat package.- Relative import dengan awalan titik menghubungkan modul sesama anggota package tanpa menuliskan nama package-nya.
- Tata letak
srcmemisahkan kode sumber dari direktori kerja, sedangkan entry point diletakkan di luar package agar tidak ada modul yang dijalankan langsung.
Referensi#
- Modules — Python Software Foundation — Bagian tutorial resmi tentang package dan relative import.
- PEP 328 – Imports: Multi-Line and Absolute/Relative — Python Software Foundation — Proposal yang memperkenalkan sintaks relative import berawalan titik.
- The import system — Python Software Foundation — Rincian mekanisme pemuatan submodul dan relative import.
- src layout vs flat layout — Python Packaging Authority — Perbandingan resmi dua tata letak proyek Python beserta pertimbangannya.