5. سیستم ایمپورت

کد پایتون در یک ماژول، با فرآیند ایمپورت کردن ماژول دیگر، به کد آن دسترسی پیدا می‌کند. دستور import رایج‌ترین روش برای فراخوانی سازوکار ایمپورت است، اما تنها روش نیست. همچنین می‌توان از توابعی مانند importlib.import_module() و تابع توکار __import__() برای فراخوانی سازوکار ایمپورت استفاده کرد.

دستور import دو عملیات را ترکیب می‌کند؛ این دستور به دنبال ماژول نام‌برده جست‌وجو می‌کند، سپس نتایج آن جست‌وجو را به یک نام در محدوده محلی مقید می‌کند. عملیات جست‌وجوی دستور import به‌عنوان فراخوانی تابع __import__()، با آرگومان‌های مناسب تعریف شده است. مقدار بازگشتی __import__() برای انجام عملیات مقیدسازی نامِ دستور import استفاده می‌شود. برای جزئیات دقیق آن عملیات اتصال نام، دستور import را ببینید.

فراخوانی مستقیمِ __import__() فقط جست‌وجوی ماژول و در صورت پیدا شدن، عملیات ایجاد ماژول را انجام می‌دهد. اگرچه ممکن است عوارض جانبی خاصی، مانند ایمپورت شدن بسته‌های والد و به‌روزرسانی نهانگاه‌های مختلف (از جمله sys.modules)، رخ دهد، تنها دستور import عملیات پیوند نام را انجام می‌دهد.

هنگامی که یک دستور import اجرا می‌شود، تابع توکار استاندارد __import__() فراخوانی می‌شود. سایر سازوکارهای فراخوانی سیستم ایمپورت (مانند importlib.import_module()) ممکن است انتخاب کنند که __import__() را دور بزنند و از راه‌حل‌های خود برای پیاده‌سازی معناشناسی ایمپورت استفاده کنند.

هنگامی که یک ماژول برای نخستین بار ایمپورت می‌شود، پایتون به جست‌وجوی آن ماژول می‌پردازد و در صورت یافت شدن، یک شیء ماژول ایجاد می‌کند [1] و آن را مقداردهی اولیه می‌کند. اگر ماژول نام‌برده‌شده یافت نشود، یک ModuleNotFoundError پرتاب می‌شود. پایتون هنگامی که سازوکار ایمپورت فراخوانی می‌شود، راهبردهای مختلفی را برای جست‌وجوی ماژول نام‌برده‌شده پیاده‌سازی می‌کند. این راهبردها را می‌توان با استفاده از قلاب‌های مختلفی که در بخش‌های زیر توضیح داده شده‌اند، تغییر داد و گسترش داد.

تغییر یافته در نسخه‌ی 3.3: سامانه‌ی ایمپورت برای پیاده‌سازی کامل فاز دوم PEP 302 به‌روزرسانی شده است. دیگر هیچ سازوکار ایمپورت ضمنی وجود ندارد؛ سامانه‌ی کامل ایمپورت از طریق sys.meta_path در معرض قرار گرفته است. علاوه بر این، پشتیبانی از بسته‌های فضای نام بومی پیاده‌سازی شده است (به PEP 420 مراجعه کنید).

5.1. importlib

ماژول importlib یک API غنی برای تعامل با سامانه‌ی ایمپورت فراهم می‌کند. برای مثال، importlib.import_module() در مقایسه با __import__() توکار، API ساده‌تر و توصیه‌شده‌ای برای فراخوانی سازوکار ایمپورت ارائه می‌دهد. برای جزئیات بیشتر به مستندات کتابخانه‌ی importlib مراجعه کنید.

5.2. بسته‌ها

پایتون تنها یک نوع شیء ماژول دارد و همه‌ی ماژول‌ها از این نوع هستند، صرف‌نظر از اینکه ماژول با پایتون، C یا چیز دیگری پیاده‌سازی شده باشد. برای کمک به سازمان‌دهی ماژول‌ها و ایجاد سلسله‌مراتب نام‌گذاری، پایتون دارای مفهوم بسته‌ها است.

می‌توانید بسته‌ها را پوشه‌هایی در یک سامانه فایل‌بندی و ماژول‌ها را پرونده‌هایی درون پوشه‌ها در نظر بگیرید، اما این تشبیه را بیش از حد واقعی تلقی نکنید، زیرا بسته‌ها و ماژول‌ها لزوماً از سامانه فایل‌بندی منشأ نمی‌گیرند. برای اهداف این مستندات، از این تشبیه مناسب میان پوشه‌ها و پرونده‌ها استفاده خواهیم کرد. مانند پوشه‌های سامانه فایل‌بندی، بسته‌ها به‌صورت سلسله‌مراتبی سازمان‌دهی شده‌اند و بسته‌ها ممکن است خود شامل زیربسته‌ها و همچنین ماژول‌های معمولی باشند.

مهم است به‌خاطر داشته باشید که همه‌ی بسته‌ها ماژول هستند، اما همه‌ی ماژول‌ها بسته نیستند. یا به بیان دیگر، بسته‌ها تنها نوع ویژه‌ای از ماژول هستند. به‌طور مشخص، هر ماژولی که دارای ویژگی __path__ باشد، بسته محسوب می‌شود.

تمام ماژول‌ها نام دارند. نام زیربسته‌ها با یک نقطه از نام بسته والد آن‌ها جدا می‌شود، شبیه به سینتکس استاندارد دسترسی به ویژگی در پایتون. بنابراین ممکن است بسته‌ای به نام email داشته باشید که به نوبه‌ی خود، زیربسته‌ای به نام email.mime و ماژولی درون آن زیربسته به نام email.mime.text دارد.

5.2.1. بسته‌های معمولی

پایتون دو نوع بسته را تعریف می‌کند: بسته‌های معمولی و بسته‌های فضای نام. بسته‌های معمولی همان بسته‌های سنتی هستند که در پایتون 3.2 و نسخه‌های پیشین وجود داشتند. یک بسته معمولی معمولاً به‌صورت پوشه‌ای حاوی یک پرونده __init__.py پیاده‌سازی می‌شود. هنگامی که یک بسته معمولی ایمپورت می‌شود، این پرونده __init__.py به‌طور ضمنی اجرا می‌شود و شیءهایی که تعریف می‌کند به نام‌هایی در فضای نام بسته مقید می‌شوند. پرونده __init__.py می‌تواند حاوی همان کد پایتونی باشد که هر ماژول دیگری می‌تواند حاوی آن باشد، و پایتون هنگام ایمپورت شدن ماژول، چند ویژگی اضافی به آن اضافه می‌کند.

برای مثال، چیدمان سامانه فایل‌بندی زیر یک بسته parent در سطح بالا را با سه زیربسته تعریف می‌کند:

parent/
    __init__.py
    one/
        __init__.py
    two/
        __init__.py
    three/
        __init__.py

ایمپورت کردن parent.one به‌طور ضمنی parent/__init__.py و parent/one/__init__.py را اجرا می‌کند. ایمپورت‌های بعدی parent.two یا parent.three به‌ترتیب parent/two/__init__.py و parent/three/__init__.py را اجرا می‌کنند.

یک زیرپوشه‌ی فاقد پرونده __init__.py درون یک بسته‌ی معمولی، به‌عنوان یک بسته‌ی فضای نام ضمنی (یک «زیربسته‌ی فضای نام») که ریشه در آن والد دارد در نظر گرفته می‌شود. برای مشخصات زیربنایی، PEP 420 را ببینید.

5.2.2. بسته‌های فضای نام

یک بسته فضای نام ترکیبی از بخش‌ها مختلف است، که هر بخش یک زیربسته را به بسته والد اضافه می‌کند. بخش‌ها ممکن است در مکان‌های مختلفی در سامانه فایل‌بندی قرار داشته باشند. بخش‌ها همچنین ممکن است در پرونده‌های zip، روی شبکه، یا هر جای دیگری که پایتون در حین ایمپورت جستجو می‌کند، یافت شوند. بسته‌های فضای نام ممکن است مستقیماً با اشیای روی سامانه فایل‌بندی متناظر باشند یا نباشند؛ آن‌ها ممکن است ماژول‌های مجازی باشند که هیچ بازنمایی عینی ندارند.

بسته‌های فضای نام برای ویژگی __path__ خود از یک فهرست معمولی استفاده نمی‌کنند. در عوض، از یک نوع پیمایش‌پذیر سفارشی استفاده می‌کنند که اگر مسیر بسته والد آن‌ها (یا sys.path برای یک بسته سطح بالا) تغییر کند، در تلاش بعدی برای ایمپورت درون آن بسته، به‌طور خودکار جست‌وجوی جدیدی برای بخش‌های بسته انجام می‌دهد.

در بسته‌های فضای نام، هیچ پرونده parent/__init__.py وجود ندارد. در واقع، ممکن است در حین جست‌وجوی ایمپورت، چندین پوشه parent پیدا شوند که هر یک از بخش متفاوتی فراهم شده است. بنابراین ممکن است parent/one از نظر فیزیکی در کنار parent/two قرار نداشته باشد. در این حالت، پایتون برای بسته سطح بالای parent، هر زمان که آن بسته یا یکی از زیربسته‌های آن ایمپورت شود، یک بسته فضای نام ایجاد می‌کند.

بسته‌های فضای نام همچنین ممکن است درون یک بسته معمولی تودرتو شوند. هنگامی که سامانه‌ی ایمپورت __path__ یک بسته معمولی را جست‌وجو می‌کند و با زیرپوشه‌ای مواجه می‌شود که حاوی پرونده __init__.py نیست، آن زیرپوشه به یک بخش تبدیل می‌شود که در زیربسته‌ی فضای نام بسته‌ی معمولی دربرگیرنده سهم دارد.

همچنین PEP 420 را برای مشخصات بسته‌ی فضای نام ببینید.

5.3. جستجو

برای آغاز جست‌وجو، پایتون به نام کامل ماژول (یا بسته، اما برای اهداف این بحث، تفاوت میان آن‌ها بی‌اهمیت است) که ایمپورت می‌شود نیاز دارد. این نام ممکن است از آرگومان‌های مختلف دستور import یا از پارامترهای توابع importlib.import_module() یا __import__() آمده باشد.

این نام در مراحل مختلف جستجوی ایمپورت استفاده خواهد شد و ممکن است مسیر نقطه‌دار به یک زیرماژول باشد، مثلاً foo.bar.baz. در این حالت، پایتون ابتدا تلاش می‌کند foo را ایمپورت کند، سپس foo.bar و در نهایت foo.bar.baz را. اگر هر یک از ایمپورت‌های میانی ناموفق باشند، یک ModuleNotFoundError پرتاب می‌شود.

5.3.1. نهانگاه ماژول

نخستین مکانی که در جستجوی ایمپورت بررسی می‌شود، sys.modules است. این نگاشت به‌عنوان نهانگاهی برای همه ماژول‌هایی که پیش‌تر ایمپورت شده‌اند، از جمله مسیرهای میانی، عمل می‌کند. بنابراین اگر foo.bar.baz پیش‌تر ایمپورت شده باشد، sys.modules شامل ورودی‌هایی برای foo، foo.bar و foo.bar.baz خواهد بود. هر کلید به‌عنوان مقدار خود، شیء ماژول متناظر را خواهد داشت.

هنگام ایمپورت، نام ماژول در sys.modules جستجو می‌شود و در صورت وجود، مقدار مرتبط، ماژولی است که ایمپورت را برآورده می‌کند و فرآیند تکمیل می‌شود. با این حال، اگر مقدار None باشد، استثنای ModuleNotFoundError پرتاب می‌شود. اگر نام ماژول وجود نداشته باشد، پایتون به جستجو برای ماژول ادامه می‌دهد.

sys.modules قابل نوشتن است. حذف یک کلید ممکن است ماژول مرتبط را از بین نبرد (زیرا ماژول‌های دیگر ممکن است ارجاعاتی به آن داشته باشند)، اما ورودی نهانگاه برای ماژول نام‌برده را نامعتبر می‌کند و باعث می‌شود پایتون در ایمپورت بعدی، دوباره برای ماژول نام‌برده جستجو کند. همچنین می‌توان این کلید را به None انتساب داد، به‌طوری که ایمپورت بعدی ماژول منجر به ModuleNotFoundError شود.

اما توجه داشته باشید، زیرا اگر ارجاعی به شیء ماژول نگه دارید، ورودی نهانگاه آن را در sys.modules نامعتبر کنید و سپس ماژول نام‌برده‌شده را دوباره ایمپورت کنید، دو شیء ماژول با هم یکسان نخواهند بود. در مقابل، importlib.reload() از همان شیء ماژول استفاده مجدد خواهد کرد و به‌سادگی با اجرای دوباره کد ماژول، محتویات ماژول را دوباره مقداردهی اولیه می‌کند.

5.3.2. یابنده‌ها و بارگذارها

اگر ماژول نام‌برده‌شده در sys.modules پیدا نشود، آنگاه پروتکل ایمپورت پایتون برای یافتن و بارگذاری ماژول فراخوانی می‌شود. این پروتکل از دو شیء مفهومی تشکیل شده است: یابنده‌ها و بارگذارها. وظیفه‌ی یک یافتنده این است که تعیین کند آیا می‌تواند ماژول نام‌برده‌شده را با استفاده از هر راهبردی که می‌شناسد پیدا کند. اشیایی که هر دوی این رابط‌ها را پیاده‌سازی می‌کنند، ایمپورت‌کننده‌ها نامیده می‌شوند — آن‌ها وقتی دریابند که می‌توانند ماژول درخواست‌شده را بارگذاری کنند، خودشان را برمی‌گردانند.

پایتون شامل تعدادی یابنده و ایمپورت‌کننده‌ی پیش‌فرض است. اولی می‌داند چگونه ماژول‌های توکار را مکان‌یابی کند، و دومی می‌داند چگونه ماژول‌های فریزشده (frozen modules) را مکان‌یابی کند. سومین یابنده‌ی پیش‌فرض، یک import path را برای یافتن ماژول‌ها جستجو می‌کند. import path فهرستی از مکان‌هاست که ممکن است مسیرهای سامانه فایل‌بندی یا پرونده‌های zip را مشخص کنند. همچنین می‌توان آن را گسترش داد تا برای هر منبع قابل‌مکان‌یابی، مانند منابعی که با URLها شناسایی می‌شوند، جستجو کند.

سازوکار ایمپورت قابل گسترش است، بنابراین می‌توان با افزودن یابندههای جدید، گستره و محدوده‌ی جستجوی ماژول را گسترش داد.

یابنده‌ها در واقع ماژول‌ها را بارگذاری نمی‌کنند. اگر بتوانند ماژول نام‌برده‌شده را بیابند، یک module spec برمی‌گردانند، یعنی دربرگیرنده‌ای از اطلاعات مرتبط با ایمپورت ماژول، که سپس سازوکار ایمپورت هنگام بارگذاری ماژول از آن استفاده می‌کند.

بخش‌های زیر پروتکل مربوط به یابنده‌ها و بارگذارها را با جزئیات بیشتر شرح می‌دهند، از جمله این‌که چگونه می‌توانید یابنده‌ها و بارگذارهای جدیدی را ایجاد و ثبت کنید تا سازوکار ایمپورت را گسترش دهید.

تغییر یافته در نسخه‌ی 3.4: در نسخه‌های پیشین پایتون، یابنده‌ها بارگذارها را مستقیماً برمی‌گرداندند، در حالی که اکنون مشخصات ماژول‌ها را برمی‌گردانند که شامل بارگذارها هستند. بارگذارها هنوز در هنگام ایمپورت استفاده می‌شوند، اما مسئولیت‌های کمتری دارند.

5.3.3. قلاب‌های ایمپورت

سازوکار ایمپورت به‌گونه‌ای طراحی شده است که گسترش‌پذیر باشد؛ سازوکار اصلی برای این منظور، قلاب‌های ایمپورت است. دو نوع قلاب ایمپورت وجود دارد: فراقلاب‌ها و قلاب‌های مسیر ایمپورت (import path hooks).

فراقلاب‌ها در آغاز پردازش ایمپورت فراخوانی می‌شوند، پیش از آنکه هرگونه پردازش ایمپورت دیگری، به‌جز جست‌وجو در نهانگاه sys.modules رخ داده باشد. این امر به فراقلاب‌ها امکان می‌دهد که بر پردازش sys.path، ماژول‌های فریزشده، یا حتی ماژول‌های توکار تقدم داشته باشند. فراقلاب‌ها با افزودن اشیای یابنده‌ی جدید (finder objects) به sys.meta_path ثبت می‌شوند، همان‌طور که در ادامه توضیح داده شده است.

قلاب‌های مسیر ایمپورت به‌عنوان بخشی از پردازش sys.path (یا package.__path__) فراخوانی می‌شوند، در نقطه‌ای که آیتم مسیر مرتبط با آن‌ها مشاهده می‌شود. قلاب‌های مسیر ایمپورت با افزودن فراخوانی‌پذیرهای جدید به sys.path_hooks همان‌طور که در زیر توضیح داده شده است، ثبت می‌شوند.

5.3.4. فرامسیر

هنگامی که ماژول نام‌برده‌شده در sys.modules یافت نشود، پایتون سپس sys.meta_path را جستجو می‌کند، که شامل فهرستی از اشیاء یابنده‌ی مسیر متا (meta path finder) است. این یابنده‌ها به‌ترتیب مورد پرس‌وجو قرار می‌گیرند تا مشخص شود که آیا می‌دانند چگونه ماژول نام‌برده‌شده را مدیریت کنند یا خیر. یابنده‌های فرامسیر باید متدی به نام find_spec() را پیاده‌سازی کنند که سه آرگومان دریافت می‌کند: یک نام، یک مسیر ایمپورت، و (به‌صورت اختیاری) یک ماژول هدف. یابنده‌ی فرامسیر می‌تواند برای تعیین اینکه آیا می‌تواند ماژول نام‌برده‌شده را مدیریت کند یا خیر، از هر راهبردی که بخواهد استفاده کند.

اگر یابنده‌ی فرامسیر (meta path finder) بداند چگونه ماژول نام‌برده را مدیریت کند، یک شیء مشخصات برمی‌گرداند. اگر نتواند ماژول نام‌برده را مدیریت کند، None برمی‌گرداند. اگر پردازش sys.meta_path بدون برگرداندن یک spec به انتهای فهرست خود برسد، آنگاه یک ModuleNotFoundError پرتاب می‌شود. هر استثنای دیگری که پرتاب شود، صرفاً به بالا منتشر می‌شود و فرآیند ایمپورت را متوقف می‌کند.

متد find_spec() مربوط به فرایابنده‌های مسیربا دو یا سه آرگومان فراخوانی می‌شود. اولین آرگومان، نام کامل ماژولی است که ایمپورت می‌شود، برای مثال foo.bar.baz. دومین آرگومان، ورودی‌های مسیر مورد استفاده برای جست‌وجوی ماژول است. برای ماژول‌های سطح بالا، دومین آرگومان None است، اما برای زیرماژول‌ها یا زیربسته‌ها، دومین آرگومان مقدار ویژگی __path__ بسته والد است. اگر ویژگی __path__ مربوطه قابل دسترسی نباشد، استثنای ModuleNotFoundError پرتاب می‌شود. سومین آرگومان، یک شیء ماژول موجود است که بعداً هدف بارگذاری خواهد بود. سیستم ایمپورت تنها در هنگام بارگذاری مجدد، یک ماژول هدف را ارسال می‌کند.

فرامسیرممکن است برای یک درخواست ایمپورت واحد چندین بار پیمایش شود. برای مثال، با فرض این‌که هیچ‌کدام از ماژول‌های دخیل از قبل در نهانگاه ذخیره‌نشده باشند، ایمپورت کردن foo.bar.baz ابتدا یک ایمپورت سطح‌بالا انجام می‌دهد و mpf.find_spec("foo", None, None) را روی هر یابنده مسیر متا (mpf) فراخوانی می‌کند. پس از این‌که foo ایمپورت شد، foo.bar با پیمایش فرامسیر برای بار دوم ایمپورت می‌شود و mpf.find_spec("foo.bar", foo.__path__, None) فراخوانی می‌شود. هنگامی که foo.bar ایمپورت شد، پیمایش نهایی mpf.find_spec("foo.bar.baz", foo.bar.__path__, None) را فراخوانی می‌کند.

برخی از یابنده های فرامسیر فقط از ایمپورت‌های سطح بالا پشتیبانی می‌کنند. این ایمپورت‌کننده‌ها هرگاه هر چیزی غیر از None به‌عنوان آرگومان دوم ارسال شود، همیشه None را برمی‌گردانند.

sys.meta_path پیش‌فرض پایتون دارای سه فرایابنده‌ی مسیر (meta path finders) است: یکی که می‌داند چگونه ماژول‌های توکار را ایمپورت کند، یکی که می‌داند چگونه ماژول‌های فریزشده (frozen modules) را ایمپورت کند، و یکی که می‌داند چگونه ماژول‌ها را از یک import path ایمپورت کند (یعنی یابنده‌ی مبتنی بر مسیر).

تغییر یافته در نسخه‌ی 3.4: متد find_spec() در یابنده‌های فرا مسیر جایگزین find_module() شد که اکنون منسوخ است. اگرچه این متد همچنان بدون تغییر به کار خود ادامه خواهد داد، سازوکار ایمپورت تنها در صورتی آن را امتحان می‌کند که یابنده، find_spec() را پیاده‌سازی نکرده باشد.

تغییر یافته در نسخه‌ی 3.10: استفاده از find_module() توسط سامانه‌ی ایمپورت اکنون ImportWarning را پرتاب می‌کند.

تغییر یافته در نسخه‌ی 3.12: find_module() حذف شده است. به‌جای آن از find_spec() استفاده کنید.

5.4. بارگذاری

اگر و زمانی که مشخصات ماژول یافت شود، سازوکار ایمپورت از آن (و بارگذار درون آن) هنگام بارگذاری ماژول استفاده می‌کند. در ادامه تقریبی از آنچه در بخش بارگذاری ایمپورت رخ می‌دهد آمده است:

module = None
if spec.loader is not None and hasattr(spec.loader, 'create_module'):
    # It is assumed 'exec_module' will also be defined on the loader.
    module = spec.loader.create_module(spec)
if module is None:
    module = ModuleType(spec.name)
# The import-related module attributes get set here:
_init_module_attrs(spec, module)

if spec.loader is None:
    # unsupported
    raise ImportError
if spec.origin is None and spec.submodule_search_locations is not None:
    # namespace package
    sys.modules[spec.name] = module
elif not hasattr(spec.loader, 'exec_module'):
    module = spec.loader.load_module(spec.name)
else:
    sys.modules[spec.name] = module
    try:
        spec.loader.exec_module(module)
    except BaseException:
        try:
            del sys.modules[spec.name]
        except KeyError:
            pass
        raise
return sys.modules[spec.name]

به جزئیات زیر توجه کنید:

  • اگر یک شیء ماژول موجود با نام داده‌شده در sys.modules وجود داشته باشد، ایمپورت آن را از قبل برگردانده است.

  • ماژول پیش از آنکه بارگذار کد ماژول را اجرا کند، در sys.modules وجود خواهد داشت. این موضوع حیاتی است، زیرا کد ماژول ممکن است (به‌صورت مستقیم یا غیرمستقیم) خودش را ایمپورت کند؛ افزودن آن از پیش به sys.modules، در بدترین حالت از بازگشت نامحدود و در بهترین حالت از بارگذاری چندباره جلوگیری می‌کند.

  • اگر بارگذاری ناموفق باشد، ماژول ناموفق — و فقط ماژول ناموفق — از sys.modules حذف می‌شود. هر ماژولی که از قبل در نهانگاه sys.modules وجود دارد، و هر ماژولی که به‌عنوان اثر جانبی با موفقیت بارگذاری شده باشد، باید در نهانگاه باقی بماند. این برخلاف بارگذاری مجدد است، که در آن حتی ماژول ناموفق نیز در sys.modules باقی می‌ماند.

  • پس از ایجاد ماژول اما پیش از اجرا، سازوکار ایمپورت ویژگی‌های ماژول مرتبط با ایمپورت را تنظیم می‌کند ("_init_module_attrs" در مثال شبه‌کد بالا)، همان‌طور که در بخش بعدی خلاصه شده است.

  • اجرای ماژول، لحظه کلیدی بارگذاری است که در آن فضای نام ماژول پر می‌شود. اجرا به‌طور کامل به بارگذار واگذار می‌شود، که تصمیم می‌گیرد چه چیزی و چگونه پر شود.

  • ممکن است ماژولی که در حین بارگذاری ایجاد می‌شود و به exec_module() پاس داده می‌شود، همان ماژولی نباشد که در پایان ایمپورت برگردانده می‌شود [2].

تغییر یافته در نسخه‌ی 3.4: سیستم ایمپورت مسئولیت‌های قالبی بارگذارها را بر عهده گرفته است. این مسئولیت‌ها پیش‌تر توسط متد importlib.abc.Loader.load_module() انجام می‌شدند.

5.4.1. بارگذارها

بارگذارهای ماژول، تابع حیاتی بارگذاری را فراهم می‌کنند: اجرای ماژول. سازوکار ایمپورت، متد importlib.abc.Loader.exec_module() را با یک آرگومان فراخوانی می‌کند: شیء ماژول برای اجرا. هر مقدار بازگردانده‌شده از exec_module() نادیده گرفته می‌شود.

بارگذارها باید الزامات زیر را برآورده کنند:

  • اگر ماژول یک ماژول پایتون باشد (در مقابل ماژول توکار یا افزونه‌ای که به‌صورت پویا بارگذاری می‌شود)، بارگذار باید کد ماژول را در فضای نام سراسری ماژول (module.__dict__) اجرا کند.

  • اگر بارگذار نتواند ماژول را اجرا کند، باید یک ImportError پرتاب کند، اگرچه هر استثنای دیگری که در حین exec_module() پرتاب شود، منتشر خواهد شد.

در بسیاری از موارد، یابنده و بارگذار می‌توانند یک شیء باشند؛ در چنین مواردی متد find_spec() فقط یک مشخصه با بارگذار تنظیم‌شده روی self برمی‌گرداند.

بارگذارهای ماژول می‌توانند با پیاده‌سازی متد create_module()، ایجاد شیء ماژول در حین بارگذاری را انتخاب کنند. این متد یک آرگومان، یعنی مشخصات ماژول، را می‌گیرد و شیء ماژول جدیدی را برای استفاده در حین بارگذاری برمی‌گرداند. create_module() نیازی ندارد هیچ ویژگی‌ای را روی شیء ماژول تنظیم کند. اگر این متد None برگرداند، سازوکار ایمپورت، خودش ماژول جدید را ایجاد خواهد کرد.

اضافه شده در نسخه‌ی 3.4: متد create_module() بارگذارها.

تغییر یافته در نسخه‌ی 3.4: متد load_module() با exec_module() جایگزین شد و سازوکار ایمپورت تمام مسئولیت‌های قالبی بارگذاری را بر عهده گرفت.

برای سازگاری با بارگذارهای موجود، سازوکار ایمپورت در صورتی از متد load_module() بارگذارها استفاده می‌کند که این متد وجود داشته باشد و بارگذار exec_module() را نیز پیاده‌سازی نکرده باشد. با این حال، load_module() منسوخ شده است و بارگذارها باید به‌جای آن exec_module() را پیاده‌سازی کنند.

متد load_module() باید علاوه بر اجرای ماژول، تمام عملکرد بارگذاری پیش‌ساخته توصیف‌شده در بالا را پیاده‌سازی کند. همه‌ی همان محدودیت‌ها اعمال می‌شوند، به‌همراه چند توضیح تکمیلی:

  • اگر یک شیء ماژول موجود با نام داده‌شده در sys.modules وجود داشته باشد، بارگذار باید از همان ماژول موجود استفاده کند. (در غیر این صورت، importlib.reload() به‌درستی کار نخواهد کرد.) اگر ماژول نام‌برده‌شده در sys.modules وجود نداشته باشد، بارگذار باید یک شیء ماژول جدید ایجاد کند و آن را به sys.modules اضافه کند.

  • ماژول باید پیش از اجرای کد ماژول توسط بارگذار، در sys.modules وجود داشته باشد تا از بازگشت بی‌حد یا بارگذاری چندگانه جلوگیری شود.

  • اگر بارگذاری ناموفق باشد، بارگذار باید هر ماژولی را که در sys.modules درج کرده است حذف کند، اما باید فقط ماژول(های) ناموفق را حذف کند، و فقط در صورتی که خود بارگذار آن ماژول(ها) را به‌صراحت بارگذاری کرده باشد.

تغییر یافته در نسخه‌ی 3.5: هنگامی که exec_module() تعریف شده باشد اما create_module() تعریف نشده باشد، یک DeprecationWarning پرتاب می‌شود.

تغییر یافته در نسخه‌ی 3.6: هنگامی که exec_module() تعریف شده باشد اما create_module() تعریف نشده باشد، یک ImportError پرتاب می‌شود.

تغییر یافته در نسخه‌ی 3.10: استفاده از load_module() منجر به پرتاب ImportWarning می‌شود.

5.4.2. زیرماژول‌ها

هنگامی که یک زیرماژول با استفاده از هر مکانیزمی بارگذاری می‌شود (مثلاً APIهای importlib، دستورهای import یا import-from، یا __import__() توکار)، یک اتصال به شیء زیرماژول در فضای نام ماژول والد قرار می‌گیرد. برای مثال، اگر بسته spam یک زیرماژول foo داشته باشد، پس از ایمپورت کردن spam.foo، spam دارای ویژگی foo خواهد بود که به زیرماژول متصل است. فرض کنید ساختار پوشه‌ی زیر را دارید:

spam/
    __init__.py
    foo.py

و spam/__init__.py شامل خط زیر است:

from .foo import Foo

سپس اجرای دستور زیر، پیوندهای نام برای foo و Foo را در ماژول spam قرار می‌دهد:

>>> import spam
>>> spam.foo
<module 'spam.foo' from '/tmp/imports/spam/foo.py'>
>>> spam.Foo
<class 'spam.foo.Foo'>

با توجه به قوانین آشنای انتساب نام در پایتون، ممکن است این موضوع عجیب به نظر برسد، اما در واقع ویژگی بنیادین سیستم ایمپورت است. ناوردا این است که اگر sys.modules['spam'] و sys.modules['spam.foo'] را داشته باشید (همان‌طور که پس از ایمپورت بالا خواهید داشت)، دومی باید به‌عنوان ویژگی foo در اولی وجود داشته باشد.

5.4.3. مشخصات ماژول

سازوکار ایمپورت از اطلاعات گوناگونی درباره هر ماژول در فرآیند ایمپورت، به‌ویژه پیش از بارگذاری، استفاده می‌کند. بیشتر این اطلاعات برای همه ماژول‌ها مشترک است. هدف از مشخصات یک ماژول، دربرگرفتن این اطلاعات مرتبط با ایمپورت به ازای هر ماژول است.

استفاده از مشخصات در حین ایمپورت اجازه می‌دهد وضعیت بین کامپوننت‌های سیستم ایمپورت منتقل شود، برای مثال بین یابنده‌ای که مشخصات ماژول را ایجاد می‌کند و بارگذاری که آن را اجرا می‌کند. مهم‌تر از همه، این امر به سازوکار ایمپورت اجازه می‌دهد عملیات تکراری بارگذاری را انجام دهد، در حالی که بدون مشخصات ماژول، بارگذار آن مسئولیت را بر عهده داشت.

مشخصات ماژول به‌صورت module.__spec__ در دسترس قرار می‌گیرد. تنظیم مناسب __spec__ به‌طور یکسان در مورد ماژول‌هایی که در حین راه‌اندازی مفسر مقداردهی اولیه می‌شوند اعمال می‌شود. تنها استثنا __main__ است، که در آن __spec__ در برخی موارد روی None تنظیم می‌شود.

برای جزئیات درباره‌ی محتوای مشخصات ماژول، ModuleSpec را ببینید.

اضافه شده در نسخه‌ی 3.4.

5.4.4. ویژگی‌های __path__ در ماژول‌ها

ویژگی __path__ باید یک دنباله (احتمالاً خالی) از رشته‌ها باشد که محل‌هایی را که زیرماژول‌های بسته در آن‌ها یافت می‌شوند، فهرست می‌کند. طبق تعریف، اگر یک ماژول ویژگی __path__ داشته باشد، یک بسته است.

ویژگی __path__ یک بسته در هنگام ایمپورت زیربسته‌های آن استفاده می‌شود. در سازوکار ایمپورت، این ویژگی بسیار مشابه sys.path عمل می‌کند، یعنی فهرستی از محل‌ها را برای جستجوی ماژول‌ها در هنگام ایمپورت فراهم می‌کند. با این حال، __path__ معمولاً بسیار محدودتر از sys.path است.

همان قواعدی که برای sys.path به‌کار می‌روند، برای __path__ یک بسته نیز اعمال می‌شوند. هنگام پیمایش __path__ یک بسته، به sys.path_hooks (که در ادامه توضیح داده شده‌اند) مراجعه می‌شود.

پرونده __init__.py یک بسته می‌تواند ویژگی __path__ بسته را تنظیم یا تغییر دهد، و این معمولاً روشی بود که بسته‌های فضای نام پیش از PEP 420 پیاده‌سازی می‌شدند. با تصویب PEP 420، بسته‌های فضای نام دیگر نیازی به ارائه پرونده‌های __init__.py که فقط حاوی کد دستکاری __path__ هستند ندارند؛ سازوکار ایمپورت به‌طور خودکار __path__ را به‌درستی برای بسته فضای نام تنظیم می‌کند.

5.4.5. reprهای ماژول

به‌طور پیش‌فرض، همه‌ی ماژول‌ها یک بازنمایی (repr) قابل‌استفاده دارند؛ با این حال، بسته به ویژگی‌های تنظیم‌شده در بالا و در مشخصات ماژول، می‌توانید بازنمایی (repr) اشیای ماژول را به‌طور صریح‌تری کنترل کنید.

اگر ماژول دارای مشخصات یعنی __spec__ باشد، سازوکار ایمپورت تلاش می‌کند یک بازنمایی (repr) از آن تولید کند. اگر این کار ناموفق باشد یا مشخصات وجود نداشته باشد، سیستم ایمپورت یک بازنمایی پیش‌فرض را با استفاده از هر اطلاعاتی که درباره ماژول موجود است ایجاد می‌کند. این سیستم تلاش می‌کند از module.__name__، module.__file__ و module.__loader__ به‌عنوان ورودی بازنمایی استفاده کند و برای هر اطلاعاتی که موجود نیست، مقادیر پیش‌فرض قرار دهد.

در اینجا قواعد دقیق استفاده‌شده آمده است:

  • اگر ماژول دارای ویژگی __spec__ باشد، از اطلاعات موجود در مشخصات برای تولید repr استفاده می‌شود. ویژگی‌های "name"، "loader"، "origin" و "has_location" بررسی می‌شوند.

  • اگر ماژول دارای ویژگی __file__ باشد، از آن به‌عنوان بخشی از repr ماژول استفاده می‌شود.

  • اگر ماژول __file__ نداشته باشد اما دارای __loader__ باشد که None نباشد، آنگاه بازنمایی (repr) بارگذار به‌عنوان بخشی از بازنمایی (repr) ماژول استفاده می‌شود.

  • در غیر این صورت، فقط از __name__ ماژول در repr استفاده کنید.

تغییر یافته در نسخه‌ی 3.12: استفاده از module_repr()، که از پایتون 3.4 منسوخ شده بود، در پایتون 3.12 حذف شد و دیگر در جریان تعیین repr یک ماژول فراخوانی نمی‌شود.

5.4.6. بی‌اعتبارسازی بایت‌کد نهان‌شده

پیش از آنکه پایتون بایت‌کد نهان‌شده را از یک پرونده .pyc بارگذاری کند، بررسی می‌کند که نهانگاه نسبت به پرونده منبع .py به‌روز باشد. به‌طور پیش‌فرض، پایتون این کار را با ذخیره‌ی برچسب زمانی آخرین تغییر و اندازه‌ی پرونده منبع در پرونده نهانگاه، در زمان نوشتن آن انجام می‌دهد. در ران‌تایم، سامانه‌ی ایمپورت سپس پرونده نهانگاه را با مقایسه‌ی فراداده‌ی ذخیره‌شده در پرونده نهانگاه با فراداده‌ی منبع اعتبارسنجی می‌کند.

پایتون همچنین از پرونده‌های نهانگاه «مبتنی بر هش» پشتیبانی می‌کند، که به‌جای فراداده‌های پرونده منبع، یک هش از محتوای آن را ذخیره می‌کنند. دو گونه از پرونده‌های .pyc مبتنی بر هش وجود دارد: بررسی‌شده و بررسی‌نشده. برای پرونده‌های .pyc مبتنی بر هش بررسی‌شده، پایتون با محاسبه‌ی هش پرونده منبع و مقایسه‌ی هش به‌دست‌آمده با هش موجود در پرونده نهانگاه، پرونده نهانگاه را اعتبارسنجی می‌کند. اگر یک پرونده نهانگاه مبتنی بر هش بررسی‌شده نامعتبر تشخیص داده شود، پایتون آن را بازتولید می‌کند و یک پرونده نهانگاه مبتنی بر هش بررسی‌شده‌ی جدید می‌نویسد. برای پرونده‌های .pyc مبتنی بر هش بررسی‌نشده، پایتون صرفاً فرض می‌کند که پرونده نهانگاه در صورت وجود معتبر است. می‌توان رفتار اعتبارسنجی پرونده‌های .pyc مبتنی بر هش را با پرچم --check-hash-based-pycs تغییر داد.

تغییر یافته در نسخه‌ی 3.7: پرونده‌های .pyc مبتنی بر هش افزوده شدند. پیش از این، پایتون تنها از ابطال مبتنی بر برچسب زمانی نهانگاه‌های بایت‌کد پشتیبانی می‌کرد.

5.5. یابنده‌ی مبتنی بر مسیر

همان‌طور که پیش‌تر ذکر شد، پایتون دارای چندین فرایابنده‌ی مسیر پیش‌فرض است. یکی از این‌ها، که یابنده‌ی مبتنی بر مسیر (PathFinder) نامیده می‌شود، یک import path را جستجو می‌کند، که شامل فهرستی از مدخل‌های مسیر است. هر مدخل مسیر، مکانی را برای جستجوی ماژول‌ها مشخص می‌کند.

خودِ یابنده مبتنی بر مسیر نمی‌داند چگونه چیزی را ایمپورت کند. در عوض، تک‌تک ورودی‌های مسیر را پیمایش می‌کند و هر یک را به یک یابنده ورودی مسیر مرتبط می‌کند که می‌داند چگونه آن نوع خاص از مسیر را مدیریت کند.

مجموعه‌ی پیش‌فرضی از یابنده‌های ورودی مسیر، تمام رفتارهای معنایی لازم برای یافتن ماژول‌ها در سامانه فایل‌بندی را پیاده‌سازی می‌کنند و از انواع خاص پرونده مانند کد منبع پایتون (پرونده‌های .py)، بایت‌کد پایتون (پرونده‌های .pyc) و کتابخانه‌های اشتراکی (برای مثال پرونده‌های .so) پشتیبانی می‌کنند. در صورتی که ماژول zipimport در کتابخانه‌ی استاندارد پشتیبانی شود، یابنده‌های ورودی مسیر پیش‌فرض، بارگذاری همه‌ی این انواع پرونده (به‌جز کتابخانه‌های اشتراکی) از پرونده‌های zip را نیز مدیریت می‌کنند.

ورودی‌های مسیر لازم نیست به مکان‌های سامانه فایل‌بندی محدود شوند. آن‌ها می‌توانند به URLها، پرس‌وجوهای پایگاه داده، یا هر مکان دیگری که بتوان آن را به‌صورت یک رشته مشخص کرد، اشاره کنند.

یابنده‌ی مبتنی بر مسیر، قلاب‌ها و پروتکل‌های اضافی را فراهم می‌کند تا بتوانید انواع ورودی‌های مسیر قابل‌جستجو را گسترش دهید و سفارشی‌سازی کنید. برای مثال، اگر بخواهید از ورودی‌های مسیر به‌عنوان URLهای شبکه پشتیبانی کنید، می‌توانید قلابی بنویسید که معناشناسی HTTP را برای یافتن ماژول‌ها در وب پیاده‌سازی کند. این قلاب (یک شیء فراخوانی‌پذیر) یک path entry finder برمی‌گرداند که از پروتکل توصیف‌شده در زیر پشتیبانی می‌کند و سپس از آن برای دریافت بارگذار ماژول از وب استفاده می‌شود.

یک هشدار: این بخش و بخش پیشین هر دو اصطلاح finder (یابنده) را به کار می‌برند و برای تمایز میان این دو، از اصطلاحات meta path finder و path entry finder استفاده می‌کنند. این دو نوع از یابنده‌ها بسیار شبیه هم هستند، از پروتکل‌های مشابهی پشتیبانی می‌کنند و در طول فرآیند ایمپورت به روش‌های مشابهی عمل می‌کنند، اما مهم است که به خاطر داشته باشید آن‌ها تفاوت‌های ظریفی دارند. به‌ویژه، یابنده‌های meta path در آغاز فرآیند ایمپورت بر پایه‌ی پیمایش sys.meta_path عمل می‌کنند.

در مقابل، یابنده‌های ورودی مسیر به‌نوعی جزئیاتی از پیاده‌سازی یابنده‌ی مبتنی بر مسیر هستند، و در واقع، اگر یابنده‌ی مبتنی بر مسیر از sys.meta_path حذف شود، هیچ‌یک از رفتارهای معنایی یابنده‌ی ورودی مسیر فراخوانی نمی‌شود.

5.5.1. یابنده‌های ورودی مسیر

یابنده‌ی مبتنی بر مسیر مسئول یافتن و بارگذاری ماژول‌ها و بسته‌های پایتون است که مکان آن‌ها با یک ورودی مسیر رشته‌ای تعیین شده است. بیشتر ورودی‌های مسیر، مکان‌هایی را در سیستم پرونده مشخص می‌کنند، اما لازم نیست به این محدود شوند.

به‌عنوان یک meta path finder، یابنده‌ی مبتنی بر مسیر پروتکل find_spec() را که پیش‌تر توصیف شد پیاده‌سازی می‌کند؛ با این حال، قلاب‌های اضافی را در معرض قرار می‌دهد که می‌توان از آن‌ها برای سفارشی‌سازی چگونگی یافتن و بارگذاری ماژول‌ها از import path استفاده کرد.

سه متغیر توسط یابنده‌ی مبتنی بر مسیر به کار می‌روند: sys.path، sys.path_hooks و sys.path_importer_cache. ویژگی‌های __path__ در اشیای بسته نیز به کار می‌روند. این‌ها روش‌های بیشتری برای سفارشی‌سازی سازوکار ایمپورت فراهم می‌کنند.

sys.path شامل فهرستی از رشته‌ها است که مکان‌های جستجو برای ماژول‌ها و بسته‌ها را فراهم می‌کنند. این فهرست از متغیر محیطی PYTHONPATH و چندین پیش‌فرض دیگرِ خاص نصب و پیاده‌سازی مقداردهی اولیه می‌شود. ورودی‌های sys.path می‌توانند پوشه‌هایی روی سامانه فایل‌بندی، پرونده‌های zip و به‌طور بالقوه «مکان‌های» دیگری را نام ببرند (به ماژول site مراجعه کنید) که باید برای ماژول‌ها جستجو شوند، مانند URLها یا پرسمان‌های پایگاه داده. فقط رشته‌ها باید در sys.path وجود داشته باشند؛ تمام انواع داده‌ی دیگر نادیده گرفته می‌شوند.

یابنده‌ی مبتنی بر مسیر یک meta path finder است، بنابراین سازوکار ایمپورت، جستجوی import path را با فراخوانی متد find_spec() یابنده‌ی مبتنی بر مسیر، همان‌طور که پیش‌تر شرح داده شد، آغاز می‌کند. هنگامی که آرگومان path برای find_spec() داده شود، این آرگومان فهرستی از مسیرهای رشته‌ای برای پیمایش خواهد بود — معمولاً ویژگی __path__ یک بسته برای یک ایمپورت درون آن بسته. اگر آرگومان path برابر None باشد، این نشان‌دهنده‌ی یک ایمپورت سطح بالا است و از sys.path استفاده می‌شود.

یابنده‌ی مبتنی بر مسیر همه‌ی ورودی‌های مسیر جستجو را پیمایش می‌کند، و برای هر یک از این‌ها، به دنبال یک path entry finder مناسب (PathEntryFinder) برای آن ورودی مسیر می‌گردد. از آنجا که این کار می‌تواند عملیاتی پرهزینه باشد (برای نمونه، ممکن است سربارهای فراخوانی stat() برای این جستجو وجود داشته باشد)، یابنده‌ی مبتنی بر مسیر نهانگاهی را نگه‌داری می‌کند که ورودی‌های مسیر را به یابنده‌های ورودی مسیر نگاشت می‌کند. این نهانگاه در sys.path_importer_cache نگه‌داری می‌شود (با وجود این نام، این نهانگاه در واقع اشیای یابنده را ذخیره می‌کند، نه اینکه به اشیای importer محدود باشد). بدین ترتیب، جستجوی پرهزینه برای path entry finder مربوط به مکان یک path entry خاص، تنها یک بار لازم است انجام شود. کد کاربر آزاد است ورودی‌های نهانگاه را از sys.path_importer_cache حذف کند و یابنده‌ی مبتنی بر مسیر را وادار کند که جستجوی ورودی مسیر را دوباره انجام دهد.

اگر ورودی مسیر در نهانگاه وجود نداشته باشد، یابنده‌ی مبتنی بر مسیر هر شیء فراخوانی‌پذیر در sys.path_hooks را پیمایش می‌کند. هر یک از قلاب‌های ورودی مسیر در این فهرست با یک آرگومان فراخوانی می‌شود: ورودی مسیری که باید جستجو شود. این شیء فراخوانی‌پذیر ممکن است یک path entry finder را که می‌تواند ورودی مسیر را مدیریت کند برگرداند، یا ممکن است ImportError را پرتاب کند. یابنده‌ی مبتنی بر مسیر از ImportError استفاده می‌کند تا نشان دهد که قلاب نمی‌تواند یک path entry finder برای آن path entry پیدا کند. این استثنا نادیده گرفته می‌شود و پیمایش import path ادامه می‌یابد. قلاب باید انتظار یک شیء رشته یا بایت را داشته باشد؛ کدگذاری اشیاء بایت بر عهده‌ی قلاب است (برای مثال، ممکن است کدگذاری سامانه فایل‌بندی، UTF-8 یا چیز دیگری باشد)، و اگر قلاب نتواند آرگومان را کدگشایی کند، باید ImportError را پرتاب کند.

اگر پیمایش sys.path_hooks بدون این که هیچ path entry finder بازگردانده شود به پایان برسد، متد find_spec() در یابنده مبتنی بر مسیر، None را در sys.path_importer_cache ذخیره می‌کند (تا نشان دهد که هیچ یابنده‌ای برای این ورودی مسیر وجود ندارد) و None را برمی‌گرداند، که نشان می‌دهد این meta path finder نتوانسته است ماژول را پیدا کند.

اگر یکی از فراخوانی‌پذیرهای path entry hook در sys.path_hooks یک path entry finder را برگرداند، از پروتکل زیر برای درخواست یک مشخصه‌ی ماژول از یابنده استفاده می‌شود؛ سپس هنگام بارگذاری ماژول از آن مشخصه استفاده می‌شود.

با پوشه کاری جاری — که با یک رشته خالی نشان داده می‌شود — کمی متفاوت‌تر از دیگر ورودی‌های sys.path رفتار می‌شود. نخست، اگر پوشه کاری جاری قابل تعیین نباشد یا مشخص شود که وجود ندارد، هیچ مقداری در sys.path_importer_cache ذخیره نمی‌شود. دوم، مقدار مربوط به پوشه کاری جاری برای هر جستجوی ماژول، از نو جستجو می‌شود. سوم، مسیری که برای sys.path_importer_cache استفاده می‌شود و توسط importlib.machinery.PathFinder.find_spec() برگردانده می‌شود، پوشه کاری جاری واقعی خواهد بود، نه رشته خالی.

5.5.2. پروتکل یابنده‌ی ورودی مسیر

برای پشتیبانی از ایمپورت ماژول‌ها و بسته‌های مقداردهی اولیه‌شده و همچنین برای افزودن بخش‌هایی به بسته‌های فضای نام، یابنده‌های ورودی مسیر (path entry finders) باید متد find_spec() را پیاده‌سازی کنند.

find_spec() دو آرگومان می‌گیرد: نام کامل ماژولی که ایمپورت می‌شود، و ماژول هدف (اختیاری). find_spec() مشخصات کاملاً مقداردهی‌شده‌ای برای ماژول برمی‌گرداند. این مشخصات همیشه "loader" را تنظیم‌شده خواهد داشت (با یک استثنا).

برای آنکه به سازوکار ایمپورت نشان داده شود که مشخصه نشان‌دهنده‌ی یک بخش از فضای نام است، یابنده‌ی ورودی مسیر submodule_search_locations را برابر با فهرستی حاوی همان بخش قرار می‌دهد.

تغییر یافته در نسخه‌ی 3.4: find_spec() جایگزین find_loader() و find_module() شد؛ هر دوی آن‌ها اکنون منسوخ شده‌اند، اما در صورتی که find_spec() تعریف نشده باشد، از آن‌ها استفاده خواهد شد.

یابنده‌های قدیمی‌تر ورودی مسیر ممکن است به‌جای find_spec() یکی از این دو متد منسوخ را پیاده‌سازی کنند. این متدها همچنان به‌خاطر سازگاری با عقب‌گرد به‌رسمیت شناخته می‌شوند. با این حال، اگر find_spec() در یابنده‌ی ورودی مسیر پیاده‌سازی شده باشد، متدهای قدیمی نادیده گرفته می‌شوند.

find_loader() یک آرگومان می‌گیرد، نام کامل ماژولی که ایمپورت می‌شود. find_loader() یک تاپل دوتایی برمی‌گرداند که اولین آیتم آن بارگذار و دومین آیتم آن یک بخش از فضای نام است.

برای حفظ سازگاری با نسخه‌های قبلی سایر پیاده‌سازی‌های پروتکل ایمپورت، بسیاری از یابنده‌های ورودی مسیر نیز از همان متد سنتی find_module() که یابنده‌های فرامسیر (meta path finder) از آن پشتیبانی می‌کنند، پشتیبانی می‌کنند. با این حال، متدهای find_module() یابنده‌های ورودی مسیر هرگز با آرگومان path فراخوانی نمی‌شوند (انتظار می‌رود اطلاعات مناسب مسیر از فراخوانی اولیه‌ی قلاب مسیرثبت شود).

متد find_module() در یابنده‌های ورودی مسیر منسوخ شده است، زیرا به یابنده‌ی ورودی مسیر اجازه نمی‌دهد بخش‌هایی را به بسته‌های فضای نام اضافه کند. اگر هر دو find_loader() و find_module() در یک یابنده‌ی ورودی مسیر وجود داشته باشند، سیستم ایمپورت همیشه find_loader() را با اولویت نسبت به find_module() فراخوانی می‌کند.

تغییر یافته در نسخه‌ی 3.10: فراخوانی‌های find_module() و find_loader() توسط سیستم ایمپورت، باعث پرتاب ImportWarning می‌شوند.

تغییر یافته در نسخه‌ی 3.12: find_module() و find_loader() حذف شده‌اند.

5.6. جایگزینی سامانه‌ی ایمپورت استاندارد

مطمئن‌ترین سازوکار برای جایگزینی کل سامانه‌ی ایمپورت، حذف محتوای پیش‌فرض sys.meta_path و جایگزینی کامل آن‌ها با یک قلاب سفارشی فرامسیر (meta path hook) است.

اگر تنها تغییر رفتار دستورهای ایمپورت، بدون تأثیرگذاری بر سایر APIهایی که به سیستم ایمپورت دسترسی دارند، قابل قبول باشد، ممکن است جایگزینی تابع توکار __import__() کافی باشد.

برای جلوگیری انتخابی از ایمپورت برخی ماژول‌ها از طریق یک قلاب در اوایل فرا مسیر (به‌جای غیرفعال کردن کامل سیستم ایمپورت استاندارد)، کافی است ModuleNotFoundError را مستقیماً از find_spec() پرتاب کنید، نه اینکه None را برگردانید. مورد اخیر نشان می‌دهد که جستجوی فرامسیر باید ادامه یابد، در حالی که پرتاب یک استثنا آن را بلافاصله متوقف می‌کند.

5.7. ایمپورت‌های نسبی بسته

ایمپورت‌های نسبی از نقطه‌های ابتدایی استفاده می‌کنند. یک نقطه ابتدایی نشان‌دهنده یک ایمپورت نسبی است که از بسته جاری شروع می‌شود. دو یا چند نقطه ابتدایی نشان‌دهنده ایمپورت نسبی به بسته(های) والد بسته جاری است؛ هر نقطه پس از نقطه اول، یک سطح را مشخص می‌کند. برای مثال، با فرض چیدمان بسته زیر:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
        moduleY.py
    subpackage2/
        __init__.py
        moduleZ.py
    moduleA.py

در هر یک از subpackage1/moduleX.py یا subpackage1/__init__.py، ایمپورت‌های نسبی زیر معتبر هستند:

from .moduleY import spam
from .moduleY import spam as ham
from . import moduleY
from ..subpackage1 import moduleY
from ..subpackage2.moduleZ import eggs
from ..moduleA import foo

ایمپورت‌های مطلق می‌توانند از سینتکس import <> یا from <> import <> استفاده کنند، اما ایمپورت‌های نسبی تنها می‌توانند از شکل دوم استفاده کنند؛ دلیل این موضوع این است که:

import XXX.YYY.ZZZ

باید XXX.YYY.ZZZ را به‌عنوان یک عبارت قابل‌استفاده در دسترس قرار دهد، اما .moduleY یک عبارت معتبر نیست.

5.8. ملاحظات ویژه برای __main__

ماژول __main__ یک مورد خاص نسبت به سیستم ایمپورت پایتون است. همان‌طور که در جای دیگر اشاره شد، ماژول __main__ به‌طور مستقیم در آغاز به کار مفسر مقداردهی اولیه می‌شود، بسیار شبیه به sys و builtins. با این حال، برخلاف آن دو، به‌طور دقیق به‌عنوان یک ماژول توکار محسوب نمی‌شود. این به این دلیل است که نحوه‌ی مقداردهی اولیه __main__ به پرچم‌ها و سایر گزینه‌هایی بستگی دارد که مفسر با آن‌ها فراخوانی می‌شود.

5.8.1. __main__.__spec__

بسته به اینکه __main__ چگونه راه‌اندازی شود، __main__.__spec__ به‌طور مناسب یا روی None تنظیم می‌شود.

هنگامی که پایتون با گزینه‌ی -m راه‌اندازی می‌شود، __spec__ به مشخصات ماژول آن ماژول یا بسته تنظیم می‌شود. __spec__ همچنین هنگامی که ماژول __main__ به‌عنوان بخشی از اجرای یک پوشه، پرونده فشرده (zipfile) یا ورودی دیگری از sys.path بارگذاری می‌شود، پر می‌شود.

در موارد باقی‌مانده، __main__.__spec__ روی None تنظیم می‌شود، زیرا کدی که برای پر کردن __main__ استفاده می‌شود، مستقیماً با یک ماژول قابل ایمپورت مطابقت ندارد:

  • اعلان تعاملی

  • گزینه‌ی -c

  • اجرا از stdin

  • اجرای مستقیم از یک پرونده منبع یا پرونده بایت‌کد

توجه داشته باشید که __main__.__spec__ در حالت آخر همیشه None است، حتی اگر از نظر فنی بتوان پرونده را به‌جای آن به‌صورت مستقیم به‌عنوان یک ماژول ایمپورت کرد. اگر فراداده‌ی معتبر ماژول در __main__ مطلوب باشد، از سوئیچ -m استفاده کنید.

همچنین توجه داشته باشید که حتی وقتی __main__ با یک ماژول قابل ایمپورت متناظر باشد و __main__.__spec__ متناسب با آن تنظیم شده باشد، آن‌ها همچنان ماژول‌های متمایز در نظر گرفته می‌شوند. این به این دلیل است که بلوک‌های محافظت‌شده با بررسی‌های if __name__ == "__main__": فقط زمانی اجرا می‌شوند که از ماژول برای پر کردن فضای نام __main__ استفاده شود، نه در حین ایمپورت معمولی.

5.9. ارجاعات

سازوکار ایمپورت از روزهای نخست پایتون به‌طور قابل‌توجهی تکامل پیدا کرده است. مشخصات اصلی بسته‌ها هنوز برای خواندن در دسترس است، اگرچه برخی جزئیات از زمان نگارش آن سند تغییر کرده‌اند.

مشخصه اولیه برای sys.meta_path در PEP 302 ارائه شد، با گسترش بعدی در PEP 420.

PEP 420 بسته‌های فضای نام را برای پایتون 3.3 معرفی کرد. PEP 420 همچنین پروتکل find_loader() را به‌عنوان جایگزینی برای find_module() معرفی کرد.

PEP 366 افزودن ویژگی __package__ برای ایمپورت‌های نسبی صریح در ماژول‌های اصلی را توصیف می‌کند.

PEP 328 ایمپورت‌های مطلق و نسبی صریح را معرفی کرد و در ابتدا __name__ را برای معناشناسی‌ای پیشنهاد داد که در نهایت PEP 366 آن را برای __package__ مشخص کرد.

PEP 338 اجرای ماژول‌ها به‌عنوان اسکریپت را تعریف می‌کند.

PEP 451 کپسوله‌سازی وضعیت ایمپورت به‌ازای هر ماژول در اشیاء مشخصات را اضافه می‌کند. همچنین بیشتر مسئولیت‌های تکراری بارگذارها را از دوش آن‌ها برداشته و دوباره به سازوکار ایمپورت منتقل می‌کند. این تغییرات امکان منسوخ کردن چندین API در سیستم ایمپورت و همچنین افزودن متدهای جدید به یابنده‌ها و بارگذارها را فراهم می‌کنند.

پانویس‌ها