Kursus Python / Menyusun paket dan menjalankan titik masuk program

Paket mengelompokkan modul Python dalam struktur bertingkat. Menempatkan perilaku program di fungsi main() dan memanggilnya melalui titik masuk yang jelas memungkinkan modul diimpor tanpa langsung menjalankan perhitungan aplikasi.

Buat struktur paket biasa

Latihan ini memakai paket biasa dengan berkas __init__.py. Python juga mendukung namespace package yang dapat tidak memiliki berkas tersebut, tetapi struktur eksplisit berikut memudahkan pengenalan. Gunakan folder proyek baru agar tidak tercampur dengan contoh dua modul dan import.

project/
  expense_app/
    __init__.py
    math_tools.py
    __main__.py

Jalankan perintah dari folder project, yaitu induk expense_app. Jangan masuk ke dalam folder paket untuk menjalankan perintah paket ini. Nama expense_app pada perintah harus sama persis dengan nama folder yang hendak ditemukan Python.

Gunakan Python 3.12 atau lebih baru melalui lingkungan proyek. Latihan hanya memakai Python dan tiga berkas di bawah, tanpa paket pihak ketiga. Simpan semuanya dengan encoding UTF-8. Uraian resmi tentang dua peran nama khusus ini tersedia pada dokumentasi __main__ Python.

Simpan semua berkas dan jalankan paket

Isi lengkap expense_app/__init__.py:

"""Expense calculation package."""

Berkas ini memuat docstring singkat dan tidak memulai perhitungan. Isi lengkap expense_app/math_tools.py:

def total(amounts):
    return sum(amounts)

Isi lengkap expense_app/__main__.py:

from .math_tools import total

def main():
    print(total([3500, 8000, 2500]))

if __name__ == "__main__":
    main()

Dari folder project, jalankan:

python -m expense_app

Keluaran berupa satu baris 14000. Opsi -m mencari paket menggunakan aturan pencarian modul, lalu menjalankan __main__.py di dalamnya. Titik pada from .math_tools import total berarti mencari modul saudara di paket yang sama.

Jangan mengganti perintah tersebut dengan python expense_app/__main__.py sambil mengharapkan hasil identik. Eksekusi berkas langsung tidak menyediakan konteks induk paket yang dibutuhkan relative import contoh ini dan dapat menghasilkan ImportError. Perbedaan ini berasal dari cara eksekusi, bukan dari kesalahan fungsi penjumlahan.

Gunakan __name__ untuk mengendalikan efek samping

Saat paket dieksekusi dengan perintah di atas, lingkungan eksekusi tingkat teratas mendapat nama __main__. Saat modul yang sama diimpor sebagai expense_app.__main__, atribut __name__-nya adalah expense_app.__main__. Kondisi pada akhir berkas membedakan kedua situasi itu.

Impor tetap mengeksekusi pernyataan tingkat teratas: fungsi didefinisikan dan math_tools diimpor. Namun isi main() belum dijalankan karena tidak dipanggil oleh kondisi tersebut. Pemeriksaan nama tidak otomatis menonaktifkan perintah lain di luar fungsi atau di luar blok penjaga.

Untuk paket sederhana, __main__.py sering dibuat sangat pendek dan hanya memanggil fungsi dari modul aplikasi lain. Penjaga nama bukan kewajiban untuk setiap berkas paket; pada latihan ini ia sengaja dipakai agar impor expense_app.__main__ juga dapat diuji tanpa memulai aplikasi. Jangan menganggap konvensi satu contoh sebagai satu-satunya susunan paket yang sah.

Lokasi data dan lokasi modul mengikuti aturan berbeda. Jika aplikasi memakai open("data.csv"), path relatif itu mengacu pada working directory, bukan otomatis ke folder paket. Untuk aplikasi yang dijalankan dari berbagai lokasi, pilih argumen path yang jelas atau mekanisme sumber daya paket sesuai kebutuhan, alih-alih menganggap data selalu ditemukan di sebelah modul.

Uji eksekusi dan penggunaan ulang secara terpisah

Masih dari folder project, jalankan pemeriksaan impor berikut:

python -c "import expense_app.__main__; print('imported')"

Keluaran harus hanya imported. Jika 14000 ikut muncul, periksa apakah ada main() atau print yang dipanggil pada tingkat teratas tanpa penjaga. Pemeriksaan ini menguji efek samping saat impor, sedangkan python -m expense_app menguji perilaku aplikasi.

  1. Jalankan paket dan cocokkan 14000. Ubah hanya 2500 menjadi 3500 di dalam main; hasilnya menjadi 15000.
  2. Dengan perubahan itu masih ada, jalankan pemeriksaan impor. Hasilnya tetap hanya imported karena fungsi perhitungan tidak dipanggil.
  3. Coba eksekusi berkas langsung sebagai percobaan terpisah dan bandingkan kesalahan relative import dengan keberhasilan -m. Pertahankan isi kode saat membandingkan perintah.
  4. Pulihkan nilai 2500 dan pastikan eksekusi paket kembali menghasilkan 14000 serta pemeriksaan impor tetap hanya menghasilkan imported.

Jika muncul pesan bahwa paket tidak ditemukan, periksa posisi terminal, interpreter, dan nama folder sebelum memodifikasi sys.path. Mengganti nama folder paket juga mengharuskan perubahan pada jalur impor serta perintah yang merujuknya.

Menyusun folder paket belum sama dengan menyiapkan distribusi yang dapat dipasang melalui pip. Distribusi memerlukan metadata, proses build, dan pengujian instalasi tersendiri. Contoh ini hanya menetapkan struktur sumber dan cara menjalankan aplikasi secara terkontrol.