Mengapa import penghitung berhasil di satu folder tetapi gagal dengan ModuleNotFoundError di folder lain? Jawabannya ada pada daftar lokasi bernama sys.path yang ditelusuri interpreter setiap kali statement import berjalan. Memahami urutan daftar ini memberi kita kemampuan mendiagnosis sendiri semua keanehan import — termasuk yang paling menjengkelkan: program yang “tadi masih jalan” lalu rusak setelah kita menambahkan satu file latihan. Pada Chapter ini kita membedah daftar tersebut, mengenali cache __pycache__, dan mereproduksi sendiri kejadian penimpaan module.
Urutan Pencarian Module #
Ketika interpreter menemui statement import, interpreter menelusuri lokasi berikut secara berurutan dan berhenti pada temuan pertama:
- Folder tempat script yang dijalankan berada — atau direktori saat ini bila kita memakai REPL maupun
python3 -c. - Daftar folder pada environment variable
PYTHONPATH— bila kita mengisinya (panduan praktisnya dibahas pada Chapter Memahami PYTHONPATH). - Folder standard library milik instalasi Python.
- Folder
site-packagestempatpipmemasang package pihak ketiga.
Urutan inilah sumber segala kejutan: module yang kita tulis sendiri di folder script selalu menang atas module bawaan Python dengan nama yang sama — pertarungan yang tidak sehat dan akan kita buktikan di bagian akhir Chapter.
Mengaudit Isi sys.path #
Daftar lokasi tersebut tersimpan sebagai list sys.path di dalam module sys, sehingga bisa kita cetak kapan saja. Simpan kode berikut sebagai inspeksi_path.py — versi ini sengaja meringkas setiap entri menjadi nama foldernya saja supaya hasilnya tetap mudah dibaca:
import os
import sys
print("Jumlah entri sys.path:", len(sys.path))
for index, path in enumerate(sys.path):
if path == "":
print(index, " (direktori saat ini)")
else:
print(index, " ", os.path.basename(path))Output yang Dihasilkan:
Jumlah entri sys.path: 5
0 modul_ku
1 python312.zip
2 python3.12
3 lib-dynload
4 site-packagesMari kita baca hasilnya per baris:
- Entri 0 (
modul_ku): folder tempatinspeksi_path.pyberada — folder kerja kita sendiri. Inilah alasan file.pydalam folder yang sama bisa saling mengimpor. - Entri 1-3 (
python312.zip,python3.12,lib-dynload): lokasi-lokasi standard library tempat module bawaan sepertiosdansystinggal. - Entri 4 (
site-packages): rumah bagi package yang dipasangpip.
Entri lengkap di komputer Anda berisi jalur folder instalasi Python masing-masing — panjang dan berbeda antar mesin — tetapi pola urutannya selalu sama: folder script dulu, standard library kemudian, site-packages terakhir.
Ada satu detail yang membedakan mode eksekusi. Saat kode dijalankan lewat python3 -c atau REPL, entri pertama berupa string kosong yang berarti “direktori saat ini”:
$ python3 -c "import sys; print(repr(sys.path[0]))"
''pycache dan File .pyc #
Saat module diimpor, interpreter tidak hanya menjalankan kodenya — interpreter juga menerjemahkan source code menjadi bytecode yang lebih cepat dieksekusi. Terjemahan tersebut disimpan sebagai file .pyc di dalam folder __pycache__ di sebelah file module, sehingga import berikutnya tidak perlu menerjemahkan ulang.
Kita amati prosesnya langsung. Siapkan file pembulatan.py:
def bulatkan_ke_ratusan(angka):
return round(angka, -2)Lalu file program.py yang mengimpornya:
import pembulatan
print(pembulatan.bulatkan_ke_ratusan(1234.56))Output yang Dihasilkan:
1200.0Setelah program berjalan, isi folder berubah menjadi:
cache-demo/
├── __pycache__
│ └── pembulatan.cpython-312.pyc
├── pembulatan.py
└── program.pyFile pembulatan.cpython-312.pyc adalah bytecode hasil terjemahan untuk interpreter Python 3.12. Interpreter membandingkan waktu modifikasi file .py dengan .pyc di dalam __pycache__; bila source code berubah, terjemahan lama dibuang dan diganti yang baru. Folder __pycache__ boleh dihapus kapan saja tanpa efek apa pun selain import pertama yang sedikit lebih lambat — dan folder ini sebaiknya diabaikan oleh sistem kontrol versi seperti Git.
Module Shadowing: Ketika File Latihan Menimpa Standard Library #
Kini kita reproduksi kejadian yang menyeramkan tadi. Bayangkan kita membuat file latihan bernama json.py di folder kerja:
def tampilkan_pesan(data):
print("Data:", data)File lain di folder yang sama memakai module json bawaan seperti biasa:
import json
pesan = '{"kota": "Bandung"}'
hasil = json.loads(pesan)
print(hasil["kota"])Output yang Dihasilkan:
Traceback (most recent call last):
File "/home/budi/belajar/latihan-json/program.py", line 4, in <module>
hasil = json.loads(pesan)
^^^^^^^^^^
AttributeError: module 'json' has no attribute 'loads'Pesan lengkap berisi jalur file di komputer tempat program dijalankan — di mesin Anda akan berbeda, tetapi galatnya sama. ModuleNotFoundError tidak muncul karena import json berhasil — hanya saja yang dimuat adalah file latihan kita, bukan module standard library. Setiap module yang dimuat dari file menyimpan jalurnya di attribute __file__, jadi kita bisa membuktikan dari mana module itu datang:
import json
print("Module json dimuat dari:", json.__file__)Output yang Dihasilkan:
Module json dimuat dari: /home/budi/belajar/latihan-json/json.pyTanpa file latihan tersebut, json.__file__ menunjuk ke jalur panjang di dalam folder instalasi Python yang berakhir di json/__init__.py — petunjuk bahwa yang dimuat adalah package json bawaan. Karena folder script berada di urutan pertama sys.path, file latihan kita menang tanpa peringatan apa pun.
Dengan mekanisme pencarian ini, satu file .py hanyalah module tunggal. Program yang lebih besar butuh cara mengelompokkan puluhan module ke dalam folder — itulah package, topik Chapter berikutnya.