چرا Circular Import در Python رخ می‌دهد و راه‌حل مطمئن آن چیست؟

چرا مهم است

اگر با خطای partially initialized module روبه‌رو شدی، شناخت ترتیب بارگذاری کمک می‌کند علت را پیدا کنی. برای تو مهم است بدانی تغییر شکل import لزوماً dependency دوطرفه را برطرف نمی‌کند.

قبل از خواندن، این‌ها را بدان۳ مفهوم
Circular Import

وضعیتی که در آن دو یا چند module، مستقیم یا از طریق یک زنجیره، یکدیگر را import می‌کنند. اگر نامی پیش از تعریف شدن درخواست شود، این چرخه می‌تواند خطا ایجاد کند.

بیشتر درباره این مفهوم

cache

محلی برای نگهداری موارد قبلاً آماده‌شده و استفادهٔ دوباره از آن‌ها. در فرایند import، Python شیء module ثبت‌شده در sys.modules را دوباره استفاده می‌کند.

بیشتر درباره این مفهوم

type hints

توضیحاتی در برنامه برای مشخص کردن نوع مورد انتظار متغیرها، ورودی‌ها و خروجی function‌ها که ابزارهای بررسی نوع می‌توانند از آن‌ها استفاده کنند.

بیشتر درباره این مفهوم

آموزش Real Python به بررسی Circular Import در Python می‌پردازد؛ وضعیتی که دو یا چند module، مستقیم یا از طریق یک زنجیره، یکدیگر را import می‌کنند. وجود این چرخه به‌تنهایی الزاماً خطا ایجاد نمی‌کند. مشکل زمانی رخ می‌دهد که یک module نامی را درخواست کند که module دیگر هنوز آن را تعریف نکرده، چون اجرای فایلش تمام نشده است.

ریشهٔ این رفتار در ترتیب بارگذاری است. Python ابتدا module را در sys.modules ثبت می‌کند و سپس فایل آن را از بالا به پایین اجرا می‌کند. sys.modules نقش cache مربوط به module‌های importشده را دارد؛ اگر درخواست دیگری برای همان module برسد، Python از همان شیء موجود استفاده می‌کند. اما حضور در این cache به معنی پایان اجرای فایل نیست: ممکن است بعضی function‌ها، class‌ها یا متغیرها هنوز وجود نداشته باشند.

دسترسی زودهنگام به این نام‌ها می‌تواند ImportError یا AttributeError ایجاد کند؛ معمولاً همراه با پیامی دربارهٔ module‌ای که هنوز کاملاً آماده نشده است. طبق آموزش، نوع خطا به شکل دستور import بستگی دارد و هر دو می‌توانند نشانهٔ همان چرخه باشند، نه دو مشکل مستقل.

مثال مقاله یک package به نام shop است. در order.py، کلاس Order برای کار با Product به آن وابسته است. در سمت دیگر، product.py نیز Order را import می‌کند، چون یکی از method‌های Product سفارش می‌سازد و method دیگری بررسی می‌کند که محصول در سفارش وجود دارد یا نه. این وابستگی دوطرفه زمینهٔ بروز خطا را فراهم می‌کند.

راه‌حل مطمئن، اصلاح ساختار dependency است تا import‌ها در یک جهت حرکت کنند؛ به سمت module‌ای که بتواند بدون دیگری وجود داشته باشد. اگر فعلاً امکان این تغییر نیست، آموزش انتقال import به داخل function نیازمند آن را پیشنهاد می‌کند و آن را تنها راه‌حل موقتی می‌داند که با هر ترتیب import دوام می‌آورد.

برای import‌هایی که فقط به‌خاطر type hints لازم‌اند نیز باید از شرط TYPE_CHECKING استفاده کرد. این نکته حتی در Python 3.14 برقرار است؛ با اینکه annotation‌ها در این نسخه به‌صورت lazy ارزیابی می‌شوند، این رفتار نیاز به محافظت از import صرفاً مربوط به type hints را از بین نمی‌برد.

نکته کلیدی

اول بررسی کن آیا می‌توانی dependency را یک‌طرفه کنی. اگر فعلاً نمی‌توانی، import را داخل function نیازمندش ببر؛ برای import صرفاً مربوط به type hints هم از شرط TYPE_CHECKING استفاده کن.

خواندن مقاله اصلی (در زبانه‌ی جدید باز می‌شود)
برچسب‌ها (۳)