Apa yang akan terjadi jika dua modul saling mengimpor?
Untuk menggeneralisasi masalah, bagaimana dengan impor siklik di Python?
Apa yang akan terjadi jika dua modul saling mengimpor?
Untuk menggeneralisasi masalah, bagaimana dengan impor siklik di Python?
Jawaban:
Ada diskusi yang sangat bagus tentang ini di comp.lang.python tahun lalu. Ini menjawab pertanyaan Anda dengan cukup teliti.
Impor benar-benar sangat mudah. Ingat saja yang berikut ini:
'import' dan 'from xxx import yyy' adalah pernyataan yang dapat dieksekusi. Mereka mengeksekusi ketika program yang sedang berjalan mencapai garis itu.
Jika modul tidak ada di sys.modules, maka impor membuat entri modul baru di sys.modules dan kemudian mengeksekusi kode dalam modul. Itu tidak mengembalikan kontrol ke modul panggilan sampai eksekusi selesai.
Jika suatu modul ada di sys.modules maka impor hanya mengembalikan modul itu apakah sudah selesai dijalankan. Itulah alasan mengapa impor siklik dapat mengembalikan modul yang tampaknya sebagian kosong.
Akhirnya, skrip pelaksana berjalan dalam modul bernama __main__, mengimpor skrip dengan namanya sendiri akan membuat modul baru yang tidak terkait dengan __main__.
Bawa banyak itu bersama-sama dan Anda tidak akan mendapatkan kejutan ketika mengimpor modul.
Jika Anda melakukannya import foodi dalam bardan import bardi dalam foo, itu akan berfungsi dengan baik. Pada saat segala sesuatu benar-benar berjalan, kedua modul akan terisi penuh dan akan memiliki referensi satu sama lain.
Masalahnya adalah kapan Anda melakukannya from foo import abcdan from bar import xyz. Karena sekarang setiap modul memerlukan modul lain untuk sudah diimpor (sehingga nama yang kita impor ada) sebelum dapat diimpor.
from foo import *dan from bar import *juga akan berfungsi dengan baik.
from x import y, namun masih mendapatkan kesalahan impor melingkar
importpernyataan dijalankan. Jadi itu tidak akan kesalahan tetapi Anda mungkin tidak mendapatkan semua variabel yang Anda harapkan.
from foo import *dan from bar import *, semua yang dieksekusi di foodalam fase inisialisasi bar, dan fungsi sebenarnya barbelum didefinisikan ...
Impor siklik berakhir, tetapi Anda harus berhati-hati untuk tidak menggunakan modul yang diimpor secara siklik selama inisialisasi modul.
Pertimbangkan file-file berikut:
a.py:
print "a in"
import sys
print "b imported: %s" % ("b" in sys.modules, )
import b
print "a out"
b.py:
print "b in"
import a
print "b out"
x = 3
Jika Anda menjalankan a.py, Anda akan mendapatkan yang berikut:
$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
b out
a out
Pada impor kedua dari b.py (yang kedua a in), juru bahasa Python tidak mengimpor blagi, karena sudah ada dalam modul dikt.
Jika Anda mencoba mengakses b.xdari aselama inisialisasi modul, Anda akan mendapatkan AttributeError.
Tambahkan baris berikut ke a.py:
print b.x
Kemudian, outputnya adalah:
$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
Traceback (most recent call last):
File "a.py", line 4, in <module>
import b
File "/home/shlomme/tmp/x/b.py", line 2, in <module>
import a
File "/home/shlomme/tmp/x/a.py", line 7, in <module>
print b.x
AttributeError: 'module' object has no attribute 'x'
Ini karena modul dieksekusi saat impor dan pada saat b.xdiakses, saluran x = 3belum dieksekusi, yang hanya akan terjadi setelahnya b out.
__name__bukan 'a'. Pada awalnya, saya benar-benar bingung mengapa file akan dieksekusi dua kali.
Seperti jawaban lain yang menggambarkan pola ini dapat diterima dalam python:
def dostuff(self):
from foo import bar
...
Yang akan menghindari pelaksanaan pernyataan impor ketika file diimpor oleh modul lain. Hanya jika ada ketergantungan melingkar logis, ini akan gagal.
Sebagian besar Impor Circular sebenarnya bukan impor melingkar yang logis tetapi lebih meningkatkan ImportErrorkesalahan, karena cara import()mengevaluasi pernyataan tingkat atas dari seluruh file ketika dipanggil.
Ini ImportErrorshampir selalu dapat dihindari jika Anda ingin impor Anda positif :
Pertimbangkan impor melingkar ini:
# profiles/serializers.py
from images.serializers import SimplifiedImageSerializer
class SimplifiedProfileSerializer(serializers.Serializer):
name = serializers.CharField()
class ProfileSerializer(SimplifiedProfileSerializer):
recent_images = SimplifiedImageSerializer(many=True)
# images/serializers.py
from profiles.serializers import SimplifiedProfileSerializer
class SimplifiedImageSerializer(serializers.Serializer):
title = serializers.CharField()
class ImageSerializer(SimplifiedImageSerializer):
profile = SimplifiedProfileSerializer()
Dari David Beazleys, Modul dan Paket Pembicaraan yang luar biasa : Live and Let Die! - PyCon 2015 ,, 1:54:00berikut adalah cara untuk menangani impor melingkar dengan python:
try:
from images.serializers import SimplifiedImageSerializer
except ImportError:
import sys
SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']
Ini mencoba mengimpor SimplifiedImageSerializerdan jika ImportErrordinaikkan, karena sudah diimpor, itu akan menariknya dari importcache.
PS: Anda harus membaca seluruh kiriman ini dengan suara David Beazley.
Saya mendapat contoh di sini yang mengejutkan saya!
foo.py
import bar
class gX(object):
g = 10
bar.py
from foo import gX
o = gX()
main.py
import foo
import bar
print "all done"
Di baris perintah: $ python main.py
Traceback (most recent call last):
File "m.py", line 1, in <module>
import foo
File "/home/xolve/foo.py", line 1, in <module>
import bar
File "/home/xolve/bar.py", line 1, in <module>
from foo import gX
ImportError: cannot import name gX
import bardi foo.pyakhir
bardan fookeduanya harus menggunakan gX, solusi 'terbersih' adalah memasukkan gXmodul lain dan memiliki keduanya foodan barmengimpor modul itu. (terbersih dalam arti bahwa tidak ada dependensi semantik yang tersembunyi.)
barbahkan tidak dapat menemukan gXdi foo. impor melingkar baik-baik saja dengan sendirinya, tetapi hanya saja itu gXtidak ditentukan ketika diimpor.
Modul a.py:
import b
print("This is from module a")
Modul b.py
import a
print("This is from module b")
Menjalankan "Modul a" akan menampilkan:
>>>
'This is from module a'
'This is from module b'
'This is from module a'
>>>
Ini output 3 baris ini sementara itu seharusnya menghasilkan infinitival karena impor melingkar. Apa yang terjadi baris demi baris saat menjalankan "Modul a" tercantum di sini:
import b. jadi akan mengunjungi modul bimport a. jadi akan mengunjungi modul aimport btetapi perhatikan bahwa baris ini tidak akan dieksekusi lagi , karena setiap file dalam python mengeksekusi baris impor hanya untuk sekali, tidak masalah di mana atau kapan dijalankan. sehingga akan lolos ke baris berikutnya dan mencetak "This is from module a"."This is from module b""This is from module a"dan program akan selesai.Saya sepenuhnya setuju dengan jawaban pythoneer di sini. Tetapi saya telah menemukan beberapa kode yang cacat dengan impor melingkar dan menyebabkan masalah ketika mencoba untuk menambahkan tes unit. Jadi untuk menambalnya dengan cepat tanpa mengubah semua yang Anda bisa menyelesaikan masalah dengan melakukan impor dinamis.
# Hack to import something without circular import issue
def load_module(name):
"""Load module using imp.find_module"""
names = name.split(".")
path = None
for name in names:
f, path, info = imp.find_module(name, path)
path = [path]
return imp.load_module(name, f, path[0], info)
constants = load_module("app.constants")
Sekali lagi, ini bukan perbaikan permanen tetapi dapat membantu seseorang yang ingin memperbaiki kesalahan impor tanpa mengubah terlalu banyak kode.
Bersulang!
Ada banyak jawaban bagus di sini. Walaupun biasanya ada solusi cepat untuk masalah ini, beberapa di antaranya terasa lebih pythonic daripada yang lain, jika Anda memiliki kemewahan melakukan beberapa refactoring, pendekatan lain adalah menganalisis organisasi kode Anda, dan mencoba untuk menghapus ketergantungan melingkar. Anda dapat menemukan, misalnya, bahwa Anda memiliki:
File a.py
from b import B
class A:
@staticmethod
def save_result(result):
print('save the result')
@staticmethod
def do_something_a_ish(param):
A.save_result(A.use_param_like_a_would(param))
@staticmethod
def do_something_related_to_b(param):
B.do_something_b_ish(param)
File b.py
from a import A
class B:
@staticmethod
def do_something_b_ish(param):
A.save_result(B.use_param_like_b_would(param))
Dalam hal ini, hanya memindahkan satu metode statis ke file terpisah, katakan c.py:
File c.py
def save_result(result):
print('save the result')
akan memungkinkan menghapus save_resultmetode dari A, dan dengan demikian memungkinkan menghapus impor A dari a di b:
File Refactored a.py
from b import B
from c import save_result
class A:
@staticmethod
def do_something_a_ish(param):
A.save_result(A.use_param_like_a_would(param))
@staticmethod
def do_something_related_to_b(param):
B.do_something_b_ish(param)
File Refactored b.py
from c import save_result
class B:
@staticmethod
def do_something_b_ish(param):
save_result(B.use_param_like_b_would(param))
Singkatnya, jika Anda memiliki alat (misalnya pylint atau PyCharm) yang melaporkan metode yang bisa statis, hanya melemparkan staticmethoddekorator pada mereka mungkin bukan cara terbaik untuk membungkam peringatan. Meskipun metode ini tampaknya terkait dengan kelas, mungkin lebih baik untuk memisahkannya, terutama jika Anda memiliki beberapa modul terkait yang mungkin membutuhkan fungsionalitas yang sama dan Anda bermaksud untuk mempraktikkan prinsip KERING.
Impor sirkuler dapat membingungkan karena impor melakukan dua hal:
Yang pertama dilakukan hanya sekali, sedangkan yang terakhir di setiap pernyataan impor. Impor sirkuler menciptakan situasi ketika mengimpor modul menggunakan modul yang diimpor dengan kode yang dieksekusi sebagian. Karena itu tidak akan melihat objek yang dibuat setelah pernyataan impor. Contoh kode di bawah ini menunjukkannya.
Impor sirkular bukanlah kejahatan utama yang harus dihindari dengan cara apa pun. Dalam beberapa kerangka kerja seperti Flask mereka sangat alami dan mengubah kode Anda untuk menghilangkannya tidak membuat kode lebih baik.
main.py
print 'import b'
import b
print 'a in globals() {}'.format('a' in globals())
print 'import a'
import a
print 'a in globals() {}'.format('a' in globals())
if __name__ == '__main__':
print 'imports done'
print 'b has y {}, a is b.a {}'.format(hasattr(b, 'y'), a is b.a)
oleh
print "b in, __name__ = {}".format(__name__)
x = 3
print 'b imports a'
import a
y = 5
print "b out"
a.py
print 'a in, __name__ = {}'.format(__name__)
print 'a imports b'
import b
print 'b has x {}'.format(hasattr(b, 'x'))
print 'b has y {}'.format(hasattr(b, 'y'))
print "a out"
output python main.py dengan komentar
import b
b in, __name__ = b # b code execution started
b imports a
a in, __name__ = a # a code execution started
a imports b # b code execution is already in progress
b has x True
b has y False # b defines y after a import,
a out
b out
a in globals() False # import only adds a to main global symbol table
import a
a in globals() True
imports done
b has y True, a is b.a True # all b objects are available
Saya memecahkan masalah dengan cara berikut, dan berfungsi dengan baik tanpa kesalahan. Pertimbangkan dua file a.pydan b.py.
Saya menambahkan ini ke a.pydan itu berhasil.
if __name__ == "__main__":
main ()
import b
y = 2
def main():
print ("a out")
print (b.x)
if __name__ == "__main__":
main ()
import a
print ("b out")
x = 3 + a.y
Output yang saya dapatkan adalah
>>> b out
>>> a out
>>> 5
Ok, saya pikir saya punya solusi yang cukup keren. Katakanlah Anda memiliki file adan file b. Anda memiliki defatau classdalam file byang ingin Anda gunakan dalam modul a, tetapi Anda memiliki sesuatu yang lain, baik def, classatau variabel dari file ayang Anda butuhkan dalam definisi Anda atau kelas dalam file b. Apa yang dapat Anda lakukan adalah, di bagian bawah file a, setelah memanggil fungsi atau kelas dalam file ayang diperlukan dalam file b, tetapi sebelum memanggil fungsi atau kelas dari file byang Anda butuhkan untuk file a, katakan import b
Lalu, dan inilah bagian kuncinya , dalam semua definisi atau kelas dalam file byang memerlukan defatau classdari filea(sebut saja CLASS), katamufrom a import CLASS
Ini berfungsi karena Anda dapat mengimpor file btanpa Python mengeksekusi salah satu pernyataan impor dalam file b, dan dengan demikian Anda menghindari impor melingkar.
Sebagai contoh:
class A(object):
def __init__(self, name):
self.name = name
CLASS = A("me")
import b
go = B(6)
go.dostuff
class B(object):
def __init__(self, number):
self.number = number
def dostuff(self):
from a import CLASS
print "Hello " + CLASS.name + ", " + str(number) + " is an interesting number."
Voila.
from a import CLASSsebenarnya tidak melewatkan mengeksekusi semua kode dalam a.py. Inilah yang sebenarnya terjadi: (1) Semua kode di a.py dijalankan sebagai modul khusus "__main__". (2) Pada import b, kode tingkat atas dalam b.py dijalankan (mendefinisikan kelas B) dan kemudian kontrol kembali ke "__main__". (3) "__main__" akhirnya memberikan kontrol ke go.dostuff(). (4) ketika dostuff () muncul import a, ia menjalankan semua kode dalam a.py lagi , kali ini sebagai modul "a"; kemudian mengimpor objek CLASS dari modul baru "a". Jadi sebenarnya, ini akan bekerja dengan baik jika Anda digunakan import adi mana saja di b.py.