Untuk memahami dependensi melingkar, Anda perlu ingat bahwa Python pada dasarnya adalah bahasa scripting. Eksekusi pernyataan di luar metode terjadi pada waktu kompilasi. Pernyataan impor dijalankan seperti pemanggilan metode, dan untuk memahaminya Anda harus memikirkannya seperti pemanggilan metode.
Saat Anda melakukan impor, apa yang terjadi bergantung pada apakah file yang Anda impor sudah ada di tabel modul. Jika ya, Python menggunakan apa pun yang saat ini ada di tabel simbol. Jika tidak, Python mulai membaca file modul, mengkompilasi / mengeksekusi / mengimpor apa pun yang ditemukannya di sana. Simbol yang direferensikan pada waktu kompilasi ditemukan atau tidak, bergantung pada apakah simbol telah dilihat, atau belum dilihat oleh kompilator.
Bayangkan Anda memiliki dua file sumber:
Berkas X.py
def X1:
return "x1"
from Y import Y2
def X2:
return "x2"
Berkas Y.py
def Y1:
return "y1"
from X import X1
def Y2:
return "y2"
Sekarang misalkan Anda mengkompilasi file X.py. Kompilator memulai dengan mendefinisikan metode X1, dan kemudian menekan pernyataan import di X.py. Ini menyebabkan kompilator menghentikan sementara kompilasi X.py dan mulai mengkompilasi Y.py. Tak lama kemudian kompilator menekan pernyataan import di Y.py. Karena X.py sudah ada di tabel modul, Python menggunakan tabel simbol X.py yang tidak lengkap untuk memenuhi semua referensi yang diminta. Simbol apapun yang muncul sebelum pernyataan import di X.py sekarang ada di tabel simbol, tapi simbol apapun setelahnya tidak. Karena X1 sekarang muncul sebelum pernyataan impor, itu berhasil diimpor. Python kemudian melanjutkan kompilasi Y.py. Dengan melakukan itu, ia mendefinisikan Y2 dan menyelesaikan kompilasi Y.py. Kemudian melanjutkan kompilasi X.py, dan menemukan Y2 di tabel simbol Y.py. Kompilasi akhirnya selesai tanpa kesalahan.
Sesuatu yang sangat berbeda terjadi jika Anda mencoba mengkompilasi Y.py dari baris perintah. Saat mengkompilasi Y.py, kompilator menekan pernyataan import sebelum mendefinisikan Y2. Kemudian ia mulai mengkompilasi X.py. Segera muncul pernyataan import di X.py yang membutuhkan Y2. Tapi Y2 tidak terdefinisi, jadi kompilasi gagal.
Harap perhatikan bahwa jika Anda memodifikasi X.py untuk mengimpor Y1, kompilasi akan selalu berhasil, apa pun file yang Anda kompilasi. Namun jika Anda memodifikasi file Y.py untuk mengimpor simbol X2, tidak ada file yang akan dikompilasi.
Kapan pun modul X, atau modul apa pun yang diimpor oleh X mungkin mengimpor modul saat ini, JANGAN gunakan:
from X import Y
Setiap kali Anda merasa mungkin ada impor melingkar, Anda juga harus menghindari referensi waktu kompilasi ke variabel di modul lain. Pertimbangkan kode yang tampak tidak bersalah:
import X
z = X.Y
Misalkan modul X mengimpor modul ini sebelum modul ini mengimpor X. Selanjutnya anggaplah Y didefinisikan dalam X setelah pernyataan import. Maka Y tidak akan ditentukan ketika modul ini diimpor, dan Anda akan mendapatkan kesalahan kompilasi. Jika modul ini mengimpor Y terlebih dahulu, Anda bisa lolos begitu saja. Namun jika salah satu rekan kerja Anda dengan polosnya mengubah urutan definisi di modul ketiga, kode tersebut akan rusak.
Dalam beberapa kasus, Anda dapat menyelesaikan dependensi melingkar dengan memindahkan pernyataan import ke bawah definisi simbol yang dibutuhkan oleh modul lain. Dalam contoh di atas, definisi sebelum pernyataan import tidak pernah gagal. Definisi setelah pernyataan import terkadang gagal, tergantung pada urutan kompilasi. Anda bahkan dapat meletakkan pernyataan import di akhir file, selama tidak ada simbol yang diimpor diperlukan pada waktu kompilasi.
Perhatikan bahwa memindahkan pernyataan import ke bawah dalam modul mengaburkan apa yang Anda lakukan. Ganti rugi ini dengan komentar di bagian atas modul Anda seperti berikut ini:
#import X (actual import moved down to avoid circular dependency)
Secara umum ini adalah praktik yang buruk, tetapi terkadang sulit untuk dihindari.