6 November 2026•baca 7 menit

Modul Python: Import yang Mencetak Laporannya

Kamu memindahkan satu fungsi ke file sendiri, meng-import-nya, dan seluruh laporan jalan lagi lalu memakan inputmu. Modul adalah file, import menjalankannya dari atas ke bawah, dan satu baris menentukan pekerjaan mana yang sedang dijalankan file itu.

Baca dalam bahasa Inggris

Modul itu cuma file Python yang bisa di-import oleh file lain, supaya isinya bisa dipakai ulang tanpa disalin. File .py apa pun yang sudah pernah kamu tulis sudah termasuk modul. Kamu memindahkan laporan ke report.py, dan file kedua butuh satu fungsi dari situ. Jadi kamu menaruh import report di bagian atas lalu menjalankan file kedua itu.

Import-nya menjalankan seluruh laporan. Ia membaca inputmu dulu, lalu mencetak setiap baris dan totalnya, semuanya sebelum file kedua sampai ke baris berikutnya. Tidak ada error yang muncul, jadi tidak ada yang memberi tahu kenapa.

Sebabnya satu aturan tentang apa yang dilakukan import, dan perbaikannya satu baris plus sedikit penataan ulang. Kamu akan melihat keduanya gagal lalu lulus.

Poin Penting

  • import report menjalankan seluruh file dari atas ke bawah saat itu juga, jadi pekerjaan di level atas terjadi lagi di dalam file yang meng-import-nya.
  • __name__ bernilai __main__ kalau kamu menjalankan filenya sendiri, dan bernilai nama modul kalau ada yang meng-import; guard membandingkan dua string itu.
  • Pekerjaan sebaiknya ada di dalam fungsi, karena def cuma mengikat nama, dan pemanggilan di bawah if __name__ == "__main__": yang memulainya.
  • Nilai yang dihitung di level modul selesai sekali saja: total di versi awal adalah angka 9, jadi memanggilnya memunculkan TypeError: 'int' object is not callable.
  • Guard hanya melindungi baris yang menjorok di bawahnya, jadi print liar di level atas tetap jalan setiap kali file di-import.

Dua file, satu perintah

Aturannya sama seperti di artikel-artikel sebelumnya, misalnya Python Dictionaries: the Key That Isn't There. Kamu menulis programnya, artikelnya memberimu checker, dan kamu menjalankan checker itu sampai berubah dari merah ke hijau. Kamu butuh dua file dan python3. Tidak ada repository, tidak ada Docker, tidak ada akun.

Simpan ini sebagai report.py. Isinya skripmu apa adanya, dengan laporan di level atas:

PYTHON
# report.py
import sys

rows = []
for line in sys.stdin:
    line = line.strip()
    if not line:
        continue
    name, qty = line.split(",")
    rows.append((name, int(qty)))

total = sum(qty for _, qty in rows)
print("item,qty")
for name, qty in rows:
    print(f"{name},{qty}")
print(f"total,{total}")

Apa yang dilakukan import pada sebuah file

Nama file adalah nama yang kamu import: report.py di-import sebagai report, tanpa .py.

Waktu Python menemui import report, ia mencari filenya lalu menjalankannya dari atas ke bawah, saat itu juga. Ini terjadi sekali per proses. Setiap baris di level atas dijalankan: loop atas sys.stdin, sum, tiap print. Python tidak tahu baris mana yang kamu maksud sebagai bagian yang bisa dipakai ulang.

Dari mana ia mencari filenya? Folder skrip yang kamu jalankan ada paling depan di jalur import. Saya cek dengan probe satu baris di folder bernama probe. Saat menjalankan python3 path_probe.py di situ, sys.path[0] berisi basename probe. Itu sebabnya dua file dalam satu folder bisa saling import tanpa setup apa pun.

Sekarang versi awalnya, di-import. Ia mencetak laporan lengkap: item,qty, kopi,3, teh,2, kopi,4, total,9. Ada masalah kedua juga. total adalah angka yang sudah jadi, bukan fungsi, jadi memanggilnya memunculkan TypeError: 'int' object is not callable.

Simpan checker-nya di sebelah report.py. Ia memberi program itu kopi,3, teh,2, dan kopi,4, dan mengharapkan total 9. Ia menjalankan filemu sekali sebagai skrip dan dua kali sebagai import.

PYTHON
# check_report.py
import subprocess
import sys

DATA = """kopi,3
teh,2
kopi,4
"""

EXPECTED = """item,qty
kopi,3
teh,2
kopi,4
total,9"""


def run(args, stdin=""):
    return subprocess.run(
        [sys.executable] + args,
        input=stdin,
        text=True,
        capture_output=True,
    )


def report(results):
    for label, passed, detail in results:
        mark = "PASS" if passed else "FAIL"
        note = f"  <- {detail}" if detail and not passed else ""
        print(f"{mark}  {label}{note}")
    passed = [ok for _, ok, _ in results].count(True)
    print(f"\n{passed} of {len(results)} checks passed")


def main():
    as_script = run(["report.py"], DATA)
    as_import = run(["-c", "import report"], DATA)
    as_call = run(["-c", "import report; print(report.total([('a', 1), ('b', 2)]))"])

    stderr_script = as_script.stderr.strip().splitlines()
    results = [
        ("the script runs without crashing", as_script.returncode == 0,
         stderr_script[-1] if stderr_script else ""),
        ("the script prints the report, total last", as_script.stdout.strip() == EXPECTED,
         repr(as_script.stdout)),
        ("importing it does not crash", as_import.returncode == 0, as_import.stderr.strip()),
        ("importing it prints nothing", as_import.stdout == "", repr(as_import.stdout)),
        ("total() is a function you can call", as_call.returncode == 0 and as_call.stdout.strip() == "3",
         as_call.stderr.strip().splitlines()[-1] if as_call.stderr.strip() else repr(as_call.stdout)),
    ]

    report(results)
    return 0 if all(ok for _, ok, _ in results) else 1


if __name__ == "__main__":
    sys.exit(main())

Jalankan:

TEXT
python3 check_report.py
TEXT
PASS  the script runs without crashing
PASS  the script prints the report, total last
PASS  importing it does not crash
FAIL  importing it prints nothing  <- 'item,qty\nkopi,3\nteh,2\nkopi,4\ntotal,9\n'
FAIL  total() is a function you can call  <- TypeError: 'int' object is not callable

3 of 5 checks passed

Exit code-nya 1. Kalau dijalankan sebagai skrip, filenya baik-baik saja. Kedua kegagalan datang dari import, dan yang pertama menunjukkan seluruh laporan yang bocor keluar.

Satu baris menentukan pekerjaan file itu

Python memberi setiap file sebuah nama selama ia berjalan. Nama itu ada di __name__. Isinya __main__ kalau kamu menjalankan filenya sendiri, dan nama modul kalau ada yang meng-import-nya.

Taruh satu baris, print(__name__), di file bernama name_probe.py. Jalankan python3 name_probe.py dan ia mencetak __main__. Import dari file lain, dan baris yang sama mencetak name_probe.

Kodenya identik dan namanya berbeda. Baris if __name__ == "__main__": membandingkan dua string itu. Kalau cocok, blok yang menjorok dijalankan. Kalau file lain yang meng-import punyamu, keduanya tidak cocok dan blok itu dilewati.

Jadi rencananya punya dua bagian. Taruh pekerjaan di dalam fungsi, karena def cuma mengikat nama saat file di-import. Lalu taruh satu pemanggilan yang memulai pekerjaan itu di bawah guard.

Perbaikannya

Saya pecah versi awal jadi tiga fungsi. parse membaca barisnya, total menjumlahkannya, dan main mencetak laporan. Sekarang total adalah fungsi, dan itu menghapus TypeError tadi. Dua baris terakhir memegang guard-nya.

PYTHON
# report.py
import sys


def parse(lines):
    rows = []
    for line in lines:
        line = line.strip()
        if not line:
            continue
        name, qty = line.split(",")
        rows.append((name, int(qty)))
    return rows


def total(rows):
    return sum(qty for _, qty in rows)


def main():
    rows = parse(sys.stdin)
    print("item,qty")
    for name, qty in rows:
        print(f"{name},{qty}")
    print(f"total,{total(rows)}")


if __name__ == "__main__":
    main()

Jalankan checker-nya lagi:

TEXT
python3 check_report.py
TEXT
PASS  the script runs without crashing
PASS  the script prints the report, total last
PASS  importing it does not crash
PASS  importing it prints nothing
PASS  total() is a function you can call

5 of 5 checks passed

Exit code-nya 0.

Yang tidak dilakukan ini

Package adalah folder dengan file __init__.py, dan relative import adalah bentuk from . import yang dipakai di dalamnya. Keduanya untuk nanti. Dua file dalam satu folder tidak butuh keduanya.

Guard juga tidak menyelamatkan print yang kamu tinggalkan di level modul. Kalau ada print("debug") liar di luar semua fungsi dan di luar guard, setiap proses yang meng-import file itu mencetaknya sekali. Guard hanya melindungi baris yang menjorok di bawahnya.

Sebelum artikel berikutnya

Hapus dua baris terakhir dari file yang sudah diperbaiki: baris if __name__ == "__main__": dan pemanggilan main() di bawahnya. Jalankan checker-nya lagi. Saya ukur 4 dari 5 dan exit code 1, dan satu-satunya kegagalan adalah ini:

TEXT
FAIL  the script prints the report, total last  <- ''

Skripnya jalan dan tidak mencetak apa pun. Sekarang kembalikan kedua baris itu dan jalankan checker sampai ia bilang 5 dari 5 lagi.

Itu membuktikan checker bisa membedakan file yang dijalankan dan yang cuma di-import. Ia tidak bisa membuktikan kode di dalam main() benar.

Kalau import menjalankan seluruh file, apa lagi di skrip lamamu yang selama ini sudah berjalan di level atas?

Biar makin jago ngoding.

Yuk gabung bareng temen-temen developer lainnya buat dapet update teknologi, tips, dan tutorial santai tiap minggu.

Biar Nggak Kudet

Update teknologi, tips ngoding, dan insight santai langsung ke email kamu tiap minggu.

Santai, anti spam. Bisa berhenti langganan kapan aja.

Baca Selanjutnya

Lihat semua

Untuk developer yang belajar dari kesulitan

© 2026 Inva.dev.