ในการทำความเข้าใจการอ้างอิงแบบวงกลมคุณต้องจำไว้ว่า Python เป็นภาษาสคริปต์ การดำเนินการของคำสั่งภายนอกวิธีการเกิดขึ้นในเวลาคอมไพล์ คำสั่งนำเข้าจะดำเนินการเช่นเดียวกับการเรียกใช้วิธีการและเพื่อให้เข้าใจว่าคุณควรคิดเช่นการเรียกใช้
เมื่อคุณนำเข้าสิ่งที่เกิดขึ้นจะขึ้นอยู่กับว่าไฟล์ที่คุณกำลังนำเข้ามีอยู่แล้วในตารางโมดูลหรือไม่ ถ้าเป็นเช่นนั้น Python จะใช้สิ่งที่อยู่ในตารางสัญลักษณ์ หากไม่เป็นเช่นนั้น Python จะเริ่มอ่านไฟล์โมดูลรวบรวม / ดำเนินการ / นำเข้าสิ่งที่พบในนั้น สัญลักษณ์ที่อ้างอิงในเวลาคอมไพล์จะพบหรือไม่ขึ้นอยู่กับว่ามีการเห็นหรือยังไม่เห็นโดยคอมไพเลอร์
ลองนึกภาพคุณมีไฟล์ต้นฉบับสองไฟล์:
ไฟล์ X.py
def X1:
return "x1"
from Y import Y2
def X2:
return "x2"
ไฟล์ Y.py
def Y1:
return "y1"
from X import X1
def Y2:
return "y2"
ตอนนี้สมมติว่าคุณรวบรวมไฟล์ X.py. คอมไพเลอร์เริ่มต้นด้วยการกำหนดเมธอด X1 จากนั้นเข้าสู่คำสั่ง import ใน X.py สิ่งนี้ทำให้คอมไพเลอร์หยุดการคอมไพล์ X.py ชั่วคราวและเริ่มคอมไพล์ Y.py หลังจากนั้นไม่นานคอมไพเลอร์ก็เข้าสู่คำสั่งนำเข้าใน Y.py. เนื่องจาก X.py อยู่ในตารางโมดูลแล้ว Python จึงใช้ตารางสัญลักษณ์ X.py ที่ไม่สมบูรณ์ที่มีอยู่เพื่อตอบสนองการอ้างอิงที่ร้องขอ สัญลักษณ์ใด ๆ ที่ปรากฏก่อนคำสั่งนำเข้าใน X.py ขณะนี้อยู่ในตารางสัญลักษณ์ แต่สัญลักษณ์ใด ๆ ที่ตามมาจะไม่ปรากฏ เนื่องจาก X1 ปรากฏก่อนคำสั่งนำเข้าจึงนำเข้าได้สำเร็จ จากนั้น Python จะดำเนินการรวบรวม Y.py. ในการดำเนินการดังกล่าวจะกำหนด Y2 และทำการรวบรวม Y.py จากนั้นจะดำเนินการรวบรวม X.py ต่อและค้นหา Y2 ในตารางสัญลักษณ์ Y.py ในที่สุดการคอมไพล์ก็เสร็จสมบูรณ์โดยไม่มีข้อผิดพลาด
จะมีบางอย่างที่แตกต่างออกไปมากหากคุณพยายามรวบรวม Y.py จากบรรทัดคำสั่ง ขณะรวบรวม Y.py คอมไพลเลอร์จะเข้าสู่คำสั่งนำเข้าก่อนที่จะกำหนด Y2 จากนั้นจะเริ่มรวบรวม X.py. ในไม่ช้าก็จะเข้าสู่คำสั่งนำเข้าใน X.py ที่ต้องใช้ Y2 แต่ Y2 ไม่ได้กำหนดไว้ดังนั้นการคอมไพล์จึงล้มเหลว
โปรดทราบว่าหากคุณแก้ไข X.py เพื่อนำเข้า Y1 การคอมไพล์จะสำเร็จเสมอไม่ว่าคุณจะคอมไพล์ไฟล์ใดก็ตาม อย่างไรก็ตามหากคุณแก้ไขไฟล์ Y.py เพื่อนำเข้าสัญลักษณ์ X2 ไฟล์ทั้งสองจะไม่คอมไพล์
เมื่อใดก็ตามที่โมดูล X หรือโมดูลใด ๆ ที่ X นำเข้าอาจนำเข้าโมดูลปัจจุบันห้ามใช้:
from X import Y
เมื่อใดก็ตามที่คุณคิดว่าอาจมีการนำเข้าแบบวงกลมคุณควรหลีกเลี่ยงการรวบรวมการอ้างอิงเวลาไปยังตัวแปรในโมดูลอื่น ๆ พิจารณารหัสที่ดูไร้เดียงสา:
import X
z = X.Y
สมมติว่าโมดูล X นำเข้าโมดูลนี้ก่อนที่โมดูลนี้จะนำเข้า X อีกสมมติว่า Y ถูกกำหนดไว้ใน X หลังคำสั่งนำเข้า จากนั้น Y จะไม่ถูกกำหนดเมื่อนำเข้าโมดูลนี้และคุณจะได้รับข้อผิดพลาดในการคอมไพล์ หากโมดูลนี้นำเข้า Y ก่อนคุณสามารถหลีกเลี่ยงได้ แต่เมื่อเพื่อนร่วมงานคนหนึ่งของคุณเปลี่ยนลำดับของคำจำกัดความในโมดูลที่สามโดยบริสุทธิ์ใจโค้ดจะแตก
ในบางกรณีคุณสามารถแก้ไขการอ้างอิงแบบวงกลมได้โดยย้ายคำสั่งนำเข้าลงด้านล่างคำจำกัดความสัญลักษณ์ที่โมดูลอื่นต้องการ ในตัวอย่างข้างต้นคำจำกัดความก่อนคำสั่งนำเข้าไม่เคยล้มเหลว คำจำกัดความหลังจากคำสั่งนำเข้าบางครั้งล้มเหลวขึ้นอยู่กับลำดับของการคอมไพล์ คุณยังสามารถใส่คำสั่งนำเข้าไว้ท้ายไฟล์ได้ตราบใดที่ไม่จำเป็นต้องใช้สัญลักษณ์ที่นำเข้าในเวลาคอมไพล์
โปรดทราบว่าการย้ายคำสั่งการนำเข้าลงในโมดูลจะบดบังสิ่งที่คุณกำลังทำอยู่ ชดเชยสิ่งนี้ด้วยความคิดเห็นที่ด้านบนของโมดูลของคุณดังต่อไปนี้:
โดยทั่วไปนี่เป็นแนวทางปฏิบัติที่ไม่ดี แต่บางครั้งก็ยากที่จะหลีกเลี่ยง