منابع ترجمه · The Python Tutorial · فصل ۶

ماژول‌ها (آموزش رسمی پایتون، فصل ۶)

۱۷ دقیقه مطالعه · به‌روزرسانی ۳ آبان ۱۴۰۵

منبع اصلی: The Python Tutorial, Chapter 6: Modules — Python Software Foundation

💡 این صفحه مثل یک نوت‌بوک Jupyter است: متغیر و تابعی که در یک سلول تعریف شود، در سلول‌های بعدی همین صفحه در دسترس است. سلول‌ها را به ترتیب اجرا کنید، یا با «⏮ تا اینجا» همهٔ سلول‌های قبلیِ اجرانشده را یک‌جا اجرا کنید.

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

برای پشتیبانی از این کار، پایتون راهی دارد که تعریف‌ها را در یک فایل بگذارید و در یک اسکریپت یا در یک نمونهٔ تعاملی مفسر از آن‌ها استفاده کنید. چنین فایلی یک ماژول (module) نامیده می‌شود؛ تعریف‌های یک ماژول را می‌توان در ماژول‌های دیگر یا در ماژول اصلی (main) وارد (import) کرد (ماژول اصلی مجموعهٔ متغیرهایی است که در اسکریپتی که در سطح بالا اجرا می‌شود و در حالت ماشین‌حساب به آن‌ها دسترسی دارید).

یک ماژول فایلی است که شامل تعریف‌ها و دستورهای پایتون است. نام فایل همان نام ماژول با پسوند .py است. درون یک ماژول، نام ماژول (به‌صورت رشته) به‌عنوان مقدار متغیر سراسری __name__ در دسترس است. برای مثال، با ویرایشگر متن دلخواهتان فایلی به نام fibo.py را در پوشهٔ جاری با محتوای زیر بسازید:

# Fibonacci numbers module

def fib(n):
    """Write Fibonacci series up to n."""
    a, b = 0, 1
    while a < n:
        print(a, end=' ')
        a, b = b, a+b
    print()

def fib2(n):
    """Return Fibonacci series up to n."""
    result = []
    a, b = 0, 1
    while a < n:
        result.append(a)
        a, b = b, a+b
    return result

حالا وارد مفسر پایتون شوید و این ماژول را با دستور زیر وارد کنید:

>>> import fibo

این کار نام توابع تعریف‌شده در fibo را مستقیماً به فضای نام (namespace) جاری اضافه نمی‌کند (برای جزئیات بیشتر حوزه‌ها و فضاهای نام پایتون را ببینید)؛ فقط نام ماژول fibo را به آنجا اضافه می‌کند. با استفاده از نام ماژول می‌توانید به توابع دسترسی داشته باشید:

>>> fibo.fib(1000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> fibo.__name__
'fibo'

اگر قصد دارید از یک تابع اغلب استفاده کنید، می‌توانید آن را به یک نام محلی نسبت دهید:

>>> fib = fibo.fib
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

۶٫۱. بیشتر دربارهٔ ماژول‌ها

یک ماژول می‌تواند علاوه بر تعریف‌های تابع، دستورهای اجرایی هم داشته باشد. این دستورها برای مقداردهی اولیهٔ ماژول در نظر گرفته شده‌اند. آن‌ها فقط اولین بار که نام ماژول در یک دستور import با آن روبه‌رو می‌شویم اجرا می‌شوند. [۱] (اگر فایل به‌صورت اسکریپت اجرا شود هم اجرا می‌شوند.)

هر ماژول فضای نام خصوصی خودش را دارد که توسط همهٔ توابع تعریف‌شده در ماژول به‌عنوان فضای نام سراسری به کار می‌رود. بنابراین، نویسندهٔ یک ماژول می‌تواند بدون نگرانی از تصادف‌های تصادفی با متغیرهای سراسری کاربر، از متغیرهای سراسری در ماژول استفاده کند. از سوی دیگر، اگر می‌دانید چه می‌کنید، می‌توانید با همان نمادگذاری که برای ارجاع به توابع آن به کار می‌رود، یعنی modname.itemname، به متغیرهای سراسری ماژول دست بزنید.

ماژول‌ها می‌توانند ماژول‌های دیگر را import کنند. معمول است، اما الزامی نیست، که همهٔ دستورهای import را در ابتدای ماژول (یا در این مورد، اسکریپت) قرار دهید. نام ماژول‌های واردشده، اگر در سطح بالای یک ماژول (بیرون از هر تابع یا کلاس) قرار گیرند، به فضای نام سراسری ماژول اضافه می‌شوند.

یک گونه از دستور import وجود دارد که نام‌ها را از یک ماژول مستقیماً به فضای نام ماژول واردکننده وارد می‌کند. برای مثال:

>>> from fibo import fib, fib2
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

این کار نام ماژولی را که import از آن گرفته شده در فضای نام محلی وارد نمی‌کند (بنابراین در مثال، fibo تعریف نشده است).

حتی گونه‌ای هست که همهٔ نام‌هایی را که یک ماژول تعریف می‌کند وارد می‌کند:

>>> from fibo import *
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

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

توجه کنید که به‌طور کلی، رویهٔ import کردن * از یک ماژول یا بسته نامطلوب شمرده می‌شود، چون اغلب باعث کدی با خوانایی ضعیف می‌شود. با این حال، استفاده از آن برای صرفه‌جویی در تایپ در نشست‌های تعاملی اشکالی ندارد.

اگر بعد از نام ماژول as بیاید، نام بعد از as مستقیماً به ماژول واردشده متصل می‌شود.

>>> import fibo as fib
>>> fib.fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

این در عمل ماژول را همان‌طور وارد می‌کند که import fibo می‌کند، با این تفاوت که با نام fib در دسترس است.

همچنین هنگام استفاده از from با اثرهای مشابه می‌توان از آن استفاده کرد:

>>> from fibo import fib as fibonacci
>>> fibonacci(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

توجه: به دلایل کارایی، هر ماژول در هر نشست مفسر فقط یک بار وارد می‌شود. بنابراین، اگر ماژول‌هایتان را تغییر دادید، باید مفسر را مجدداً راه‌اندازی کنید — یا، اگر فقط یک ماژول است که می‌خواهید به‌صورت تعاملی آزمایش کنید، از importlib.reload() استفاده کنید، مثلاً import importlib; importlib.reload(modulename).

۶٫۱٫۱. اجرای ماژول‌ها به‌عنوان اسکریپت

وقتی یک ماژول پایتون را با

python fibo.py <arguments>

اجرا می‌کنید، کد موجود در ماژول اجرا می‌شود، درست مانند اینکه آن را import کرده باشید، اما با __name__ برابر "__main__". این یعنی با افزودن این کد به انتهای ماژولتان:

if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))

می‌توانید فایل را هم به‌عنوان اسکریپت و هم به‌عنوان ماژولی قابل import به کار برید، چون کدی که خط فرمان را تجزیه می‌کند فقط وقتی اجرا می‌شود که ماژول به‌عنوان فایل «اصلی» اجرا شود:

$ python fibo.py 50
0 1 1 2 3 5 8 13 21 34

اگر ماژول import شود، کد اجرا نمی‌شود:

>>> import fibo
>>>

این اغلب یا برای ارائهٔ یک رابط کاربری مناسب برای یک ماژول، یا برای اهداف آزمایش (اجرای ماژول به‌عنوان اسکریپت یک مجموعه‌آزمون را اجرا می‌کند) به کار می‌رود.

۶٫۱٫۲. مسیر جست‌وجوی ماژول

وقتی ماژولی با نام spam import می‌شود، مفسر ابتدا دنبال یک ماژول درون‌ساخت با آن نام می‌گردد. این نام‌های ماژول در sys.builtin_module_names فهرست شده‌اند. اگر پیدا نشد، سپس دنبال فایلی با نام spam.py در فهرستی از پوشه‌ها که توسط متغیر sys.path داده می‌شود می‌گردد. sys.path از این مکان‌ها مقداردهی اولیه می‌شود:

  • پوشه‌ای که اسکریپت ورودی در آن است (یا پوشهٔ جاری وقتی فایلی مشخص نشده باشد).
  • PYTHONPATH (فهرستی از نام پوشه‌ها، با همان نحو متغیر شل PATH).
  • پیش‌فرض وابسته به نصب (طبق قرارداد شامل یک پوشهٔ site-packages که توسط ماژول site مدیریت می‌شود).

جزئیات بیشتر در مقداردهی اولیهٔ sys.path آمده است.

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

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

۶٫۱٫۳. فایل‌های پایتون «کامپایل‌شده»

برای سرعت بخشیدن به بارگذاری ماژول‌ها، پایتون نسخهٔ کامپایل‌شدهٔ هر ماژول را در پوشهٔ __pycache__ با نام module.version.pyc ذخیره موقت (cache) می‌کند، که در آن version قالب فایل کامپایل‌شده را کدگذاری می‌کند؛ معمولاً شامل شمارهٔ نسخهٔ پایتون است. برای مثال، در CPython نسخهٔ ۳٫۳ نسخهٔ کامپایل‌شدهٔ spam.py با نام __pycache__/spam.cpython-33.pyc ذخیره می‌شود. این قرارداد نام‌گذاری اجازه می‌دهد ماژول‌های کامپایل‌شده از نسخه‌ها و ویرایش‌های مختلف پایتون هم‌زیست باشند.

پایتون تاریخ تغییر منبع را با نسخهٔ کامپایل‌شده مقایسه می‌کند تا ببیند قدیمی شده و باید دوباره کامپایل شود یا نه. این فرایندی کاملاً خودکار است. همچنین ماژول‌های کامپایل‌شده مستقل از پلتفرم هستند، بنابراین یک کتابخانه را می‌توان بین سیستم‌هایی با معماری‌های مختلف به اشتراک گذاشت.

پایتون در دو شرایط حافظهٔ پنهان را بررسی نمی‌کند. اول، ماژولی را که مستقیماً از خط فرمان بارگذاری می‌شود همیشه دوباره کامپایل می‌کند و نتیجه را ذخیره نمی‌کند. دوم، اگر ماژول منبع وجود نداشته باشد حافظهٔ پنهان را بررسی نمی‌کند. برای پشتیبانی از یک توزیع غیرمنبع (فقط کامپایل‌شده)، ماژول کامپایل‌شده باید در پوشهٔ منبع باشد و نباید ماژول منبعی وجود داشته باشد.

چند نکته برای متخصصان:

  • می‌توانید از کلیدهای -O یا -OO در فرمان پایتون برای کاهش اندازهٔ ماژول کامپایل‌شده استفاده کنید. کلید -O دستورهای assert را حذف می‌کند، کلید -OO هم دستورهای assert و هم رشته‌های __doc__ را حذف می‌کند. از آنجا که برخی برنامه‌ها ممکن است به در دسترس بودن این‌ها وابسته باشند، فقط وقتی باید از این گزینه استفاده کنید که بدانید چه می‌کنید. ماژول‌های «بهینه‌شده» برچسب opt- دارند و معمولاً کوچک‌ترند. نسخه‌های آینده ممکن است اثرهای بهینه‌سازی را تغییر دهند.

  • برنامه وقتی از فایل .pyc خوانده می‌شود سریع‌تر از وقتی که از فایل .py خوانده می‌شود اجرا نمی‌شود؛ تنها چیزی که دربارهٔ فایل‌های .pyc سریع‌تر است سرعت بارگذاری آن‌هاست.

  • ماژول compileall می‌تواند فایل‌های .pyc را برای همهٔ ماژول‌های یک پوشه بسازد.

  • جزئیات بیشتری دربارهٔ این فرایند، از جمله یک فلوچارت از تصمیم‌ها، در PEP 3147 هست.

۶٫۲. ماژول‌های استاندارد

پایتون با کتابخانه‌ای از ماژول‌های استاندارد همراه است که در سند جداگانه‌ای به نام «مرجع کتابخانهٔ پایتون» (از این پس «مرجع کتابخانه») توضیح داده شده‌اند. برخی ماژول‌ها در مفسر تعبیه شده‌اند؛ این‌ها دسترسی به عملیاتی را فراهم می‌کنند که بخشی از هستهٔ زبان نیستند اما با این حال درون‌ساخت هستند، یا برای کارایی یا برای فراهم کردن دسترسی به اولیه‌های سیستم‌عامل مانند فراخوانی‌های سیستمی. مجموعهٔ چنین ماژول‌هایی یک گزینهٔ پیکربندی است که به پلتفرم زیربنایی هم بستگی دارد. برای مثال، ماژول winreg فقط در سیستم‌های ویندوز ارائه می‌شود. یک ماژول خاص شایستهٔ توجه است: sys که در هر مفسر پایتون تعبیه شده است. متغیرهای sys.ps1 و sys.ps2 رشته‌های مورد استفاده برای اعلان‌های اصلی و ثانویه را تعریف می‌کنند:

>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print('Yuck!')
Yuck!
C>

این دو متغیر فقط وقتی تعریف می‌شوند که مفسر در حالت تعاملی باشد.

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

>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')

۶٫۳. تابع dir()

تابع درون‌ساخت dir() برای فهمیدن اینکه یک ماژول چه نام‌هایی تعریف می‌کند به کار می‌رود. این تابع یک فهرست مرتب‌شده از رشته‌ها برمی‌گرداند:

>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)  # doctest: +NORMALIZE_WHITESPACE
['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__',
 '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__',
 '__stderr__', '__stdin__', '__stdout__', '__unraisablehook__',
 '_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework',
 '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook',
 'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix',
 'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing',
 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info',
 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info',
 'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth',
 'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags',
 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile',
 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval',
 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
 'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value',
 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks',
 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix',
 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags',
 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr',
 'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info',
 'warnoptions']

بدون آرگومان، dir() نام‌هایی را که در حال حاضر تعریف کرده‌اید فهرست می‌کند:

>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir()
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']

توجه کنید که همهٔ انواع نام‌ها را فهرست می‌کند: متغیرها، ماژول‌ها، توابع و غیره.

dir() نام توابع و متغیرهای درون‌ساخت را فهرست نمی‌کند. اگر فهرست آن‌ها را می‌خواهید، در ماژول استاندارد builtins تعریف شده‌اند:

>>> import builtins
>>> dir(builtins)  # doctest: +NORMALIZE_WHITESPACE
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException',
 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning',
 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError',
 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning',
 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False',
 'FileExistsError', 'FileNotFoundError', 'FloatingPointError',
 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError',
 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError',
 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError',
 'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented',
 'NotImplementedError', 'OSError', 'OverflowError',
 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError',
 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning',
 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError',
 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError',
 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError',
 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning',
 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__',
 '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs',
 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable',
 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits',
 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit',
 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr',
 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass',
 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview',
 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property',
 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars',
 'zip']

۶٫۴. بسته‌ها

بسته‌ها (packages) راهی برای ساختاردهی به فضای نام ماژول‌های پایتون با استفاده از «نام‌های ماژول نقطه‌دار» هستند. برای مثال، نام ماژول A.B یک زیرماژول به نام B را در بسته‌ای به نام A مشخص می‌کند. درست همان‌طور که استفاده از ماژول‌ها نویسندگان ماژول‌های مختلف را از نگرانی دربارهٔ نام متغیرهای سراسری یکدیگر نجات می‌دهد، استفاده از نام‌های ماژول نقطه‌دار نویسندگان بسته‌های چندماژولی مانند NumPy یا Pillow را از نگرانی دربارهٔ نام ماژول‌های یکدیگر نجات می‌دهد.

فرض کنید می‌خواهید مجموعه‌ای از ماژول‌ها (یک «بسته») را برای مدیریت یکنواخت فایل‌های صوتی و داده‌های صوتی طراحی کنید. قالب‌های فایل صوتی متفاوت زیادی وجود دارد (که معمولاً با پسوندشان شناخته می‌شوند، مثلاً: .wav، .aiff، .au)، بنابراین ممکن است لازم باشد مجموعه‌ای رو به رشد از ماژول‌ها را برای تبدیل بین قالب‌های مختلف فایل بسازید و نگهداری کنید. همچنین عملیات متفاوت زیادی هست که ممکن است بخواهید روی داده‌های صوتی انجام دهید (مانند میکس، افزودن پژواک، اعمال تابع اکولایزر، ساخت جلوهٔ استریوی مصنوعی)، پس علاوه بر آن جریان بی‌پایانی از ماژول‌ها برای انجام این عملیات خواهید نوشت. در اینجا ساختار ممکنی برای بستهٔ شما آمده است (که بر حسب یک سیستم‌فایل سلسله‌مراتبی بیان شده):

sound/                          Top-level package
      __init__.py               Initialize the sound package
      formats/                  Subpackage for file format conversions
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Subpackage for sound effects
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Subpackage for filters
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

هنگام import کردن بسته، پایتون پوشه‌های موجود در sys.path را برای پیدا کردن زیرپوشهٔ بسته جست‌وجو می‌کند.

فایل‌های __init__.py لازم‌اند تا پایتون پوشه‌هایی را که این فایل را دارند به‌عنوان بسته در نظر بگیرد (مگر آنکه از بستهٔ فضای نام (namespace package) استفاده شود، ویژگی نسبتاً پیشرفته‌ای). این از آن جلوگیری می‌کند که پوشه‌هایی با نام رایج، مانند string، ناخواسته ماژول‌های معتبری را که بعداً در مسیر جست‌وجوی ماژول می‌آیند پنهان کنند. در ساده‌ترین حالت، __init__.py می‌تواند فقط یک فایل خالی باشد، اما می‌تواند کد مقداردهی اولیه را هم برای بسته اجرا کند یا متغیر __all__ را که بعداً توضیح داده می‌شود تنظیم کند.

کاربران بسته می‌توانند ماژول‌های منفرد را از بسته import کنند، برای مثال:

import sound.effects.echo

این کار زیرماژول sound.effects.echo را بارگذاری می‌کند. باید با نام کامل آن به آن ارجاع داد.

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

یک روش جایگزین برای import کردن زیرماژول این است:

from sound.effects import echo

این هم زیرماژول echo را بارگذاری می‌کند و آن را بدون پیشوند بسته در دسترس قرار می‌دهد، بنابراین می‌توان آن را این‌گونه به کار برد:

echo.echofilter(input, output, delay=0.7, atten=4)

گونهٔ دیگر، import مستقیم تابع یا متغیر موردنظر است:

from sound.effects.echo import echofilter

باز هم این زیرماژول echo را بارگذاری می‌کند، اما تابع echofilter آن را مستقیماً در دسترس قرار می‌دهد:

echofilter(input, output, delay=0.7, atten=4)

توجه کنید که هنگام استفاده از from package import item، آیتم می‌تواند یک زیرماژول (یا زیربسته) از بسته باشد، یا نامی دیگر که در بسته تعریف شده، مانند تابع، کلاس یا متغیر. دستور import ابتدا آزمایش می‌کند که آیا آیتم در بسته تعریف شده است؛ اگر نه، فرض می‌کند که یک ماژول است و سعی می‌کند آن را بارگذاری کند. اگر نتواند آن را پیدا کند، یک استثنای ImportError پرتاب می‌شود.

برعکس، هنگام استفاده از نحوی مانند import item.subitem.subsubitem، هر آیتم به‌جز آخری باید یک بسته باشد؛ آیتم آخر می‌تواند یک ماژول یا یک بسته باشد اما نمی‌تواند کلاس یا تابع یا متغیری باشد که در آیتم قبلی تعریف شده است.

۶٫۴٫۱. import کردن * از یک بسته

حالا وقتی کاربر from sound.effects import * را بنویسد چه اتفاقی می‌افتد؟ در حالت ایدئال، امید می‌رود که این به‌نحوی به سیستم‌فایل برود، ببیند کدام زیرماژول‌ها در بسته حاضرند، و همهٔ آن‌ها را import کند. این ممکن است زمان زیادی ببرد و import کردن زیرماژول‌ها ممکن است عوارض جانبی ناخواسته‌ای داشته باشد که فقط باید وقتی رخ دهند که زیرماژول به‌صراحت import می‌شود.

تنها راه‌حل این است که نویسندهٔ بسته یک فهرست صریح از بسته ارائه دهد. دستور import از این قرارداد پیروی می‌کند: اگر کد __init__.py یک بسته فهرستی به نام __all__ تعریف کند، آن فهرست نام ماژول‌هایی در نظر گرفته می‌شود که وقتی from package import * پیش می‌آید باید import شوند. نگه داشتن این فهرست به‌روز هنگام انتشار نسخهٔ جدید بسته بر عهدهٔ نویسندهٔ بسته است. نویسندگان بسته همچنین می‌توانند تصمیم بگیرند از آن پشتیبانی نکنند، اگر کاربردی در import کردن * از بستهٔ خود نمی‌بینند. برای مثال، فایل sound/effects/__init__.py می‌تواند شامل کد زیر باشد:

__all__ = ["echo", "surround", "reverse"]

این یعنی from sound.effects import * سه زیرماژول نام‌برده از بستهٔ sound.effects را import می‌کند.

آگاه باشید که زیرماژول‌ها ممکن است توسط نام‌های تعریف‌شدهٔ محلی سایه‌دار (shadowed) شوند. برای مثال، اگر تابع reverse را به فایل sound/effects/__init__.py اضافه کنید، from sound.effects import * فقط دو زیرماژول echo و surround را import می‌کند، اما زیرماژول reverse را نه، چون توسط تابع reverse که به‌صورت محلی تعریف شده سایه‌دار می‌شود:

__all__ = [
    "echo",      # refers to the 'echo.py' file
    "surround",  # refers to the 'surround.py' file
    "reverse",   # !!! refers to the 'reverse' function now !!!
]

def reverse(msg: str):  # <-- this name shadows the 'reverse.py' submodule
    return msg[::-1]    #     in the case of a 'from sound.effects import *'

اگر __all__ تعریف نشده باشد، دستور from sound.effects import * همهٔ زیرماژول‌ها را از بستهٔ sound.effects به فضای نام جاری import نمی‌کند؛ فقط تضمین می‌کند که بستهٔ sound.effects import شده است (احتمالاً با اجرای هر کد مقداردهی اولیه در __init__.py) و سپس هر نامی را که در بسته تعریف شده import می‌کند. این شامل هر نام تعریف‌شده (و زیرماژول‌هایی که صراحتاً بارگذاری شده) توسط __init__.py است. همچنین شامل هر زیرماژولی از بسته می‌شود که صراحتاً توسط دستورهای import قبلی بارگذاری شده است. این کد را در نظر بگیرید:

import sound.effects.echo
import sound.effects.surround
from sound.effects import *

در این مثال، ماژول‌های echo و surround در فضای نام جاری import می‌شوند چون وقتی دستور from...import اجرا می‌شود در بستهٔ sound.effects تعریف شده‌اند. (این وقتی __all__ تعریف شده باشد هم کار می‌کند.)

اگرچه برخی ماژول‌ها طوری طراحی شده‌اند که وقتی از import * استفاده می‌کنید فقط نام‌هایی را که از الگوهای خاصی پیروی می‌کنند صادر کنند، با این حال در کد تولیدی همچنان رویهٔ بدی شمرده می‌شود.

به یاد داشته باشید، استفاده از from package import specific_submodule هیچ اشکالی ندارد! در واقع، این نمادگذاری توصیه‌شده است مگر آنکه ماژول واردکننده نیاز داشته باشد از زیرماژول‌هایی با نام یکسان از بسته‌های مختلف استفاده کند.

۶٫۴٫۲. ارجاع‌های درون‌بسته‌ای

وقتی بسته‌ها به زیربسته‌ها ساختار داده شده‌اند (مانند بستهٔ sound در مثال)، می‌توانید از import مطلق (absolute import) برای ارجاع به زیرماژول‌های بسته‌های هم‌تراز استفاده کنید. برای مثال، اگر ماژول sound.filters.vocoder نیاز دارد از ماژول echo در بستهٔ sound.effects استفاده کند، می‌تواند از from sound.effects import echo استفاده کند.

همچنین می‌توانید import نسبی (relative import) بنویسید، با شکل from module import name از دستور import. این importها از نقطه‌های پیشین برای نشان دادن بسته‌های جاری و والد که در import نسبی دخیل‌اند استفاده می‌کنند. برای مثال، از درون ماژول surround ممکن است از این‌ها استفاده کنید:

from . import echo
from .. import formats
from ..filters import equalizer

توجه کنید که importهای نسبی بر اساس نام بستهٔ ماژول جاری هستند. از آنجا که ماژول اصلی بسته ندارد، ماژول‌هایی که قرار است به‌عنوان ماژول اصلی یک برنامهٔ پایتون به کار روند باید همیشه از importهای مطلق استفاده کنند.


پاورقی‌ها

[۱] در واقع تعریف‌های تابع هم «دستورهایی» هستند که «اجرا می‌شوند»؛ اجرای تعریف یک تابع در سطح ماژول، نام تابع را به فضای نام سراسری ماژول اضافه می‌کند.