Modul dan mekanisme import

Mekanisme import, jalur pencarian modul, dan fungsi idiom if __name__ == '__main__'.

Daftar isi

Modul adalah berkas Python berisi definisi dan pernyataan yang dapat dipakai ulang oleh berkas lain melalui pernyataan import. Program yang besar hampir selalu dipecah ke dalam beberapa modul, sehingga mekanisme import menjadi fondasi bagi seluruh pengorganisasian kode pada topik-topik berikutnya. Topik ini membahas bentuk-bentuk pernyataan import, jalur pencarian yang ditempuh Python untuk menemukan sebuah modul, dan kebiasaan interpreter mengeksekusi modul hanya satu kali. Idiom if __name__ == "__main__" juga dibahas sebagai alat membedakan kode yang diimpor dari kode yang dijalankan langsung.

Bentuk-bentuk pernyataan import#

Pernyataan import memiliki tiga bentuk yang masing-masing mengikat nama dengan cara berbeda. Bentuk import modul membuat seluruh isi modul dapat diakses melalui nama modulnya, bentuk from modul import nama menarik satu nama tertentu ke dalam namespace saat ini, yaitu kumpulan nama yang berlaku pada satu lingkup kode, dan bentuk import modul as alias memberi nama singkat untuk modul yang panjang.

python Contoh: tiga bentuk pernyataan import
import math
from statistics import mean
import statistics as stats

print(math.sqrt(144))
print(mean([2, 4, 6]))
print(stats.median([3, 1, 2]))
text Keluaran
12.0
4
2

Bentuk pertama menaruh fungsi sqrt di balik awalan math., sehingga asal setiap nama tetap terlihat pada tempat pemakaiannya. Bentuk kedua mengimpor mean secara langsung, dan hasilnya berupa bilangan bulat karena seluruh masukannya bilangan bulat. Bentuk ketiga mengikat modul statistics ke nama singkat stats, yang berguna ketika nama modul ditulis berkali-kali. Ketiganya dapat dicampur dalam satu berkas sesuai kebutuhan keterbacaan.

Membuat dan mengimpor modul sendiri#

Setiap berkas berekstensi .py adalah modul, dan mengimpornya sama saja dengan mengeksekusi seluruh isi berkas tersebut dari atas ke bawah. Karena itu, variabel dan fungsi yang didefinisikan di tingkat atas modul dapat diakses sebagai atribut setelah impor berhasil. Contoh berikut menulis berkas modul ke direktori sementara, lalu mengimpornya.

python Contoh: membuat modul lalu mengimpornya
import sys
import tempfile
from pathlib import Path

# menulis berkas modul contoh ke direktori sementara
direktori = Path(tempfile.mkdtemp(prefix="contoh-modul-"))
(direktori / "salam.py").write_text(
    'PENYAPA = "Halo dari modul salam"\n'
    'print("modul salam dieksekusi")\n',
    encoding="utf-8",
)

# sebelum jalurnya didaftarkan, modul belum dapat diimpor
try:
    import salam
except ModuleNotFoundError as e:
    print("gagal:", str(e))

# mendaftarkan direktori pada sys.path
sys.path.insert(0, str(direktori))

import salam
print(salam.PENYAPA)
text Keluaran
gagal: No module named 'salam'
modul salam dieksekusi
Halo dari modul salam

Keluaran tersebut menunjukkan tiga kejadian berurutan. Sebelum jalurnya terdaftar, pernyataan import ditolak dengan error ModuleNotFoundError. Setelah sys.path.insert mendaftarkan direktorinya, impor berhasil dan isi modul dieksekusi, yang terlihat dari cetakan modul salam dieksekusi. Variabel PENYAPA yang didefinisikan di tingkat atas modul kemudian dibaca sebagai atribut salam.PENYAPA.

Pola penulisan modul ke direktori sementara disertai pendaftaran jalurnya dipakai pada contoh-contoh topik ini agar setiap blok kode berdiri sendiri. Pada proyek sungguhan, direktori proyek biasanya sudah terdaftar secara alami karena Python mendaftarkan direktori skrip yang dijalankan.

Urutan pencarian modul#

sys.path adalah list berisi string direktori yang ditelusuri secara berurutan setiap kali ada pernyataan import. Python memeriksa satu per satu direktori pada list itu, berhenti pada kecocokan pertama, dan menimbulkan ModuleNotFoundError bila seluruh daftar telah habis tanpa hasil. Urutan penelusurannya digambarkan pada diagram berikut.

import nama_modul1. direktori skrip2. isi PYTHONPATH3. site-packagespernyataan import pada kodedirektori tempat berkas yang dijalankan beradadaftar direktori tambahan dari variabel lingkungandirektori tempat pip memasang package
Urutan penelusuran sys.path saat pernyataan import dijalankan

Tiga sumber pertama yang diperiksa adalah direktori tempat skrip berada, isi PYTHONPATH, lalu direktori site-packages tempat pip memasang package. Posisi terdepan direktori skrip menjelaskan mengapa modul yang ditulis di samping program selalu dapat diimpor tanpa pengaturan tambahan. Isi sys.path sendiri dapat diperiksa dari dalam program, dan contoh berikut hanya menampilkan bagian yang nilainya tidak bergantung pada mesin.

python Contoh: memeriksa sys.path
import sys

print(type(sys.path).__name__)
print(any("site-packages" in jalur for jalur in sys.path))
text Keluaran
list
True

Nilai True pada baris kedua berarti salah satu anggota sys.path mengandung direktori site-packages, yaitu tempat package pihak ketiga berada. Jalur lengkapnya sengaja tidak dicetak karena isinya berbeda pada setiap pemasangan, tetapi kehadirannya selalu dapat diuji seperti di atas.

Modul dieksekusi sekali per proses#

Import bukan pemanggilan ulang. Sebuah modul dieksekusi penuh hanya pada impor pertamanya, dan hasilnya disimpan pada dictionary sys.modules yang memetakan nama modul ke objek modulnya. Impor berikutnya pada proses yang sama cukup membaca dari cache tersebut, yaitu penyimpanan sementara di dalam memori, sehingga isi modul tidak dijalankan lagi.

python Contoh: impor kedua tidak mengeksekusi ulang
import sys
import tempfile
from pathlib import Path

direktori = Path(tempfile.mkdtemp(prefix="contoh-modul-"))
(direktori / "salam.py").write_text(
    'PENYAPA = "Halo dari modul salam"\n'
    'print("modul salam dieksekusi")\n',
    encoding="utf-8",
)
sys.path.insert(0, str(direktori))

import salam
import salam  # sudah ada di sys.modules, diabaikan
print(salam.PENYAPA)
print("salam" in sys.modules)
text Keluaran
modul salam dieksekusi
Halo dari modul salam
True

Modul contoh memuat print pada tingkat atas, dan cetakan itu hanya muncul satu kali meskipun pernyataan import salam ditulis dua kali. Sifat ini menjadikan modul tempat yang tepat untuk keadaan yang perlu dijaga sepanjang program, misalnya konfigurasi yang dibaca sekali lalu dipakai bersama. Konsekuensinya, menyegarkan data dengan mengimpor ulang tidak berfungsi, dan perbaikannya adalah memanggil ulang fungsi yang ada di dalam modul tersebut.

Idiom if name == “main”#

Setiap modul membawa atribut __name__. Nilai atribut __name__ bernilai __main__ ketika berkas dijalankan langsung oleh interpreter dan bernilai nama modul itu sendiri ketika berkas diimpor. Idiom if __name__ == "__main__" memanfaatkan perbedaan itu untuk menaruh kode yang hanya berjalan pada mode skrip, misalnya pembacaan argumen baris perintah, sehingga satu berkas dapat berfungsi ganda sebagai pustaka dan sebagai program.

python Contoh: satu berkas, dua peran
import subprocess
import sys
import tempfile
from pathlib import Path

direktori = Path(tempfile.mkdtemp(prefix="contoh-main-"))
skrip = direktori / "konversi.py"
skrip.write_text(
    'def celsius_ke_fahrenheit(celsius):\n'
    '    return celsius * 9 / 5 + 32\n'
    '\n'
    'print(__name__)\n'
    '\n'
    'if __name__ == "__main__":\n'
    '    print(celsius_ke_fahrenheit(25))\n',
    encoding="utf-8",
)

sys.path.insert(0, str(direktori))

# peran pertama, diimpor sebagai modul
import konversi
print("impor selesai")

# peran kedua, dijalankan langsung oleh interpreter baru
hasil = subprocess.run([sys.executable, str(skrip)], capture_output=True, text=True)
print(hasil.stdout, end="")
text Keluaran
konversi
impor selesai
__main__
77.0

Dua baris pertama keluaran berasal dari proses utama. Saat diimpor, __name__ bernilai konversi, dan blok if dilewati sehingga baris konversi tidak dicetak. Dua baris terakhir berasal dari proses baru yang menjalankan berkas yang sama sebagai skrip. Pada proses itu, __name__ bernilai __main__ dan fungsi konversi akhirnya dipanggil. Pola ini menjelaskan mengapa banyak berkas pustaka dapat diamankan untuk diimpor tanpa efek samping yang tidak diinginkan.

Kesalahan umum#

Kesalahan pertama adalah memberi nama berkas yang sama dengan modul pustaka standar. Contoh berikut menulis statistics.py salinan tiruan, lalu mengimpornya.

python Contoh: berkas yang menimpa modul pustaka standar
import sys
import tempfile
from pathlib import Path

direktori = Path(tempfile.mkdtemp(prefix="contoh-timpa-"))
(direktori / "statistics.py").write_text(
    'print("ini salinan lokal, bukan pustaka standar")\n',
    encoding="utf-8",
)
sys.path.insert(0, str(direktori))

import statistics
text Keluaran
ini salinan lokal, bukan pustaka standar

Pustaka standar tidak pernah dimuat karena direktori tempat berkasnya berada diperiksa lebih dahulu, dan seluruh program yang mengandalkan fungsi statistik pun ikut rusak. Perbaikannya adalah mengganti nama berkas menjadi sesuatu yang tidak bentrok, misalnya rekap_statistik.py, lalu menghapus folder __pycache__ bila ada agar sisa hasil kompilasi tidak membingungkan.

Kesalahan kedua adalah menganggap impor ulang sebagai cara menyegarkan data. Seperti dibahas pada bagian sebelumnya, modul hanya dieksekusi sekali per proses, sehingga perubahan isi berkasnya tidak terlihat oleh program yang sedang berjalan. Cara yang benar adalah membungkus data yang berubah ke dalam fungsi, lalu memanggil fungsi itu setiap kali nilai terbaru dibutuhkan.

Kesalahan ketiga adalah menyebarkan sys.path.insert di banyak berkas untuk memaksa impor berhasil. Suntingan seperti itu sulit dilacak dan mudah saling menimpa. Kode yang saling membutuhkan dari arah yang berlawanan sebaiknya disusun ulang menjadi package, sehingga satu titik masuk yang terletak di luar package cukup untuk menjalankan seluruh program.

Ringkasan#

  • Pernyataan import mengeksekusi berkas modul satu kali per proses dan mengikat objek modulnya pada sys.modules, sehingga impor berikutnya tidak menjalankan ulang apa pun.
  • Python menelusuri sys.path secara berurutan dari direktori skrip, isi PYTHONPATH, hingga site-packages, dan berhenti pada kecocokan pertama.
  • __name__ bernilai __main__ ketika berkas dijalankan langsung dan bernilai nama modulnya ketika diimpor, sehingga blok if __name__ == "__main__" hanya berjalan pada mode skrip.
  • Pendaftaran direktori melalui sys.path.insert tepat untuk eksperimen sesaat, sedangkan kode yang saling membutuhkan diatur sebagai package.

Referensi#