6. ماژولها¶
اگر از مفسر پایتون خارج شوید و دوباره وارد آن شوید، تعریفهایی که ایجاد کردهاید (تابعها و متغیرها) از بین میروند. بنابراین، اگر میخواهید برنامهای نسبتاً طولانیتر بنویسید، بهتر است بهجای آن از یک ویرایشگر متن برای آمادهسازی ورودی مفسر استفاده کنید و آن را با استفاده از آن فایل بهعنوان ورودی اجرا کنید. این کار با نام ایجاد یک اسکریپت شناخته میشود. هرچه برنامهٔ شما طولانیتر شود، ممکن است بخواهید آن را برای نگهداری آسانتر به چندین فایل تقسیم کنید. همچنین ممکن است بخواهید از یک تابع کاربردی که در چندین برنامه نوشتهاید، بدون کپی کردن تعریف آن در هر برنامه استفاده کنید.
برای پشتیبانی از این کار، پایتون روشی دارد که به شما اجازه میدهد تعریفها را در یک فایل قرار دهید و از آنها در یک اسکریپت یا در یک نمونهٔ تعاملی از مفسر استفاده کنید. چنین فایلی یک ماژول نامیده میشود؛ تعریفهای یک ماژول را میتوان به ماژولهای دیگر یا به ماژول اصلی (main) (مجموعهای از متغیرهایی که در یک اسکریپت اجراشده در سطح بالا و در حالت ماشینحساب به آنها دسترسی دارید) وارد کرد.
یک ماژول فایلی است که شامل تعریفها و دستورهای پایتون است. نام فایل، نام ماژول بههمراه پسوند .py است. درون یک ماژول، نام ماژول (بهصورت یک رشته) بهعنوان مقدار متغیر سراسری __name__ در دسترس است. برای نمونه، با استفاده از ویرایشگر متن دلخواه خود فایلی با نام fibo.py را در پوشهٔ جاری ایجاد کنید که محتوای زیر را داشته باشد:
# ماژول اعداد فیبوناچی
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 تعریف شدهاند، مستقیماً به فضای نام فعلی اضافه نمیکند (برای جزئیات بیشتر به محدودهها و فضای نامها در پایتون مراجعه کنید)؛ بلکه فقط نام ماژول 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
6.1. بیشتر دربارهی ماژولها¶
یک ماژول میتواند علاوه بر تعریف تابعها، شامل دستورهای اجرایی نیز باشد. هدف از این دستورها مقداردهی اولیهٔ ماژول است. این دستورها فقط در اولین باری که نام ماژول در یک دستور import دیده میشود اجرا میشوند. [1] (همچنین اگر فایل بهعنوان یک اسکریپت اجرا شود، این دستورها اجرا خواهند شد.)
هر ماژول فضای نام خصوصی خود را دارد که توسط تمام تابعهای تعریفشده در ماژول، بهعنوان فضای نام سراسری استفاده میشود. بنابراین، نویسندهٔ یک ماژول میتواند بدون نگرانی از تداخل تصادفی با متغیرهای سراسری کاربر، از متغیرهای سراسری درون ماژول استفاده کند. از طرف دیگر، اگر بدانید چه کاری انجام میدهید، میتوانید با همان نگارشی که برای ارجاع به تابعهای ماژول استفاده میکنید، به متغیرهای سراسری ماژول دسترسی پیدا کنید: modname.itemname.
ماژولها میتوانند ماژولهای دیگری را وارد کنند. معمول است، اما اجباری نیست، که تمام دستورهای import در ابتدای یک ماژول (یا بهطور کلی یک اسکریپت) قرار داده شوند. نام ماژولهای واردشده، اگر در سطح بالای یک ماژول (خارج از هر تابع یا کلاس) قرار گرفته باشند، به فضای نام سراسری ماژول اضافه میشوند.
گونهای از دستور import وجود دارد که نامها را از یک ماژول مستقیماً به فضای نام ماژولی که عمل وارد کردن را انجام میدهد وارد میکند. برای مثال:
>>> from fibo import fib, fib2
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
این کار نام ماژولی را که وارد کردن از آن انجام شده است، به فضای نام محلی اضافه نمیکند (بنابراین در مثال، fibo تعریف نشده است).
همچنین گونهای وجود دارد که تمام نامهایی را که یک ماژول تعریف میکند وارد میکند:
>>> from fibo import *
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
این دستور تمام نامها را بهجز نامهایی که با یک زیرخط (_) شروع میشوند وارد میکند. در بیشتر موارد، برنامهنویسان پایتون از این قابلیت استفاده نمیکنند، زیرا مجموعهای نامشخص از نامها را وارد مفسر میکند و ممکن است برخی مواردی را که قبلاً تعریف کردهاید پنهان کند.
توجه داشته باشید که بهطور کلی، وارد کردن * از یک ماژول یا بسته عملی نامطلوب محسوب میشود، زیرا اغلب باعث ایجاد کدی با خوانایی ضعیف میشود. بااینحال، استفاده از آن برای کاهش تایپ در نشستهای تعاملی اشکالی ندارد.
اگر نام ماژول پس از 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).
6.1.1. اجرای ماژولها بهعنوان اسکریپت¶
وقتی یک ماژول پایتون را با استفاده از این دستور اجرا میکنید
python fibo.py <arguments>
کد موجود در ماژول اجرا خواهد شد، درست همانند زمانی که آن را وارد کردهاید، اما مقدار __name__ برابر با "__main__" تنظیم میشود. این یعنی با افزودن کد زیر در انتهای ماژول خود:
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
میتوانید فایل را هم بهعنوان یک اسکریپت و هم بهعنوان یک ماژول قابل وارد کردن استفاده کنید، زیرا کدی که خط فرمان را تجزیه میکند فقط زمانی اجرا میشود که ماژول بهعنوان فایل «اصلی» اجرا شود:
$ python fibo.py 50
0 1 1 2 3 5 8 13 21 34
اگر ماژول وارد شود، کد اجرا نمیشود:
>>> import fibo
>>>
این روش اغلب برای فراهم کردن یک رابط کاربری مناسب برای یک ماژول یا برای اهداف آزمایشی استفاده میشود (اجرای ماژول بهعنوان یک اسکریپت باعث اجرای یک مجموعهٔ آزمون میشود).
6.1.2. مسیر جستوجوی ماژولها¶
هنگامی که یک ماژول با نام spam وارد میشود، مفسر ابتدا به دنبال یک ماژول داخلی با همان نام میگردد. نام این ماژولها در sys.builtin_module_names فهرست شدهاند. اگر یافت نشود، سپس به دنبال فایلی با نام spam.py در فهرستی از پوشهها میگردد که توسط متغیر sys.path مشخص شدهاند. sys.path از این مکانها مقداردهی اولیه میشود:
پوشهای که اسکریپت ورودی در آن قرار دارد (یا پوشهٔ جاری، زمانی که هیچ فایلی مشخص نشده باشد).
PYTHONPATH(فهرستی از نام پوشهها با همان نحوی که متغیر پوستهٔPATHدارد).مقدار پیشفرض وابسته به نصب (که طبق قرارداد شامل یک پوشهٔ
site-packagesاست و توسط ماژولsiteمدیریت میشود).
جزئیات بیشتر در The initialization of the sys.path module search path موجود است.
توجه
در سیستمهای فایلی که از پیوندهای نمادین پشتیبانی میکنند، پوشهٔ شامل اسکریپت ورودی پس از دنبال کردن پیوند نمادین محاسبه میشود. به عبارت دیگر، پوشهای که پیوند نمادین در آن قرار دارد به مسیر جستوجوی ماژولها اضافه نمیشود.
پس از مقداردهی اولیه، برنامههای پایتون میتوانند sys.path را تغییر دهند. پوشهای که اسکریپت در حال اجرا در آن قرار دارد، در ابتدای مسیر جستوجو و پیش از مسیر کتابخانهٔ استاندارد قرار داده میشود. این بدان معناست که اسکریپتهای موجود در آن پوشه، بهجای ماژولهایی با همان نام در پوشهٔ کتابخانه بارگذاری خواهند شد. این کار یک خطا محسوب میشود، مگر اینکه جایگزینی موردنظر باشد. برای اطلاعات بیشتر، بخش ماژولهای استاندارد را ببینید.
6.1.3. فایلهای پایتون «کامپایلشده»¶
برای افزایش سرعت بارگذاری ماژولها، پایتون نسخهٔ کامپایلشدهٔ هر ماژول را در پوشهٔ __pycache__ و با نام module.version.pyc ذخیره میکند؛ در این نام، نسخه قالب فایل کامپایلشده را مشخص میکند و معمولاً شامل شمارهٔ نسخهٔ پایتون است. برای مثال، در انتشار CPython نسخهٔ 3.3، نسخهٔ کامپایلشدهٔ spam.py بهصورت __pycache__/spam.cpython-33.pyc ذخیره میشود. این قرارداد نامگذاری اجازه میدهد ماژولهای کامپایلشده از انتشارها و نسخههای مختلف پایتون در کنار یکدیگر وجود داشته باشند.
پایتون تاریخ تغییر فایل منبع را با نسخهٔ کامپایلشده بررسی میکند تا مشخص شود آیا قدیمی شده است و نیاز به کامپایل مجدد دارد یا خیر. این فرایند کاملاً خودکار است. همچنین، ماژولهای کامپایلشده مستقل از پلتفرم هستند، بنابراین یک کتابخانهٔ یکسان میتواند بین سیستمهایی با معماریهای متفاوت به اشتراک گذاشته شود.
پایتون در دو حالت، حافظهٔ نهان را بررسی نمیکند. نخست، همیشه ماژولی را که مستقیماً از خط فرمان بارگذاری شده است دوباره کامپایل میکند و نتیجه را ذخیره نمیکند. دوم، اگر هیچ ماژول منبعی وجود نداشته باشد، حافظهٔ نهان را بررسی نمیکند. برای پشتیبانی از توزیعهای بدون منبع (فقط کامپایلشده)، ماژول کامپایلشده باید در پوشهٔ منبع قرار داشته باشد و نباید ماژول منبعی وجود داشته باشد.
چند نکته برای متخصصان:
میتوانید از گزینههای
-Oیا-OOدر دستور پایتون برای کاهش اندازهٔ یک ماژول کامپایلشده استفاده کنید. گزینهٔ-Oدستورهای assert را حذف میکند و گزینهٔ-OOهم دستورهای assert و هم رشتههای doc را حذف میکند. از آنجا که برخی برنامهها ممکن است به وجود این موارد وابسته باشند، فقط زمانی باید از این گزینه استفاده کنید که بدانید چه کاری انجام میدهید. ماژولهای «بهینهشده» دارای برچسبopt-هستند و معمولاً کوچکترند. انتشارهای آینده ممکن است اثرات بهینهسازی را تغییر دهند.یک برنامه زمانی که از یک فایل
.pycخوانده میشود، سریعتر از زمانی که از یک فایل.pyخوانده میشود اجرا نمیشود؛ تنها چیزی که در فایلهای.pycسریعتر است، سرعت بارگذاری آنهاست.ماژول
compileallمیتواند برای تمام ماژولهای موجود در یک پوشه فایلهای .pyc ایجاد کند.جزئیات بیشتری دربارهٔ این فرایند، از جمله نمودار جریان تصمیمها، در PEP 3147 آمده است.
6.2. ماژولهای استاندارد¶
پایتون همراه با مجموعهای از ماژولهای استاندارد ارائه میشود که در یک سند جداگانه، یعنی مرجع کتابخانهٔ پایتون ("مرجع کتابخانه" از اینجا به بعد)، توضیح داده شدهاند. برخی ماژولها در مفسر داخلی هستند؛ این ماژولها دسترسی به عملیاتی را فراهم میکنند که بخشی از هستهٔ زبان نیستند، اما بااینحال، یا برای افزایش کارایی یا برای فراهم کردن دسترسی به امکانات سیستمعامل مانند فراخوانیهای سیستمی، بهصورت داخلی وجود دارند. مجموعهٔ این ماژولها یک گزینهٔ پیکربندی است که به پلتفرم زیربنایی نیز وابسته است. برای مثال، ماژول 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')
6.3. تابع dir()¶
تابع داخلی dir() برای یافتن نامهایی که یک ماژول تعریف میکند استفاده میشود. این تابع یک فهرست مرتبشده از رشتهها برمیگرداند:
>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)
['__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)
['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']
6.4. بستهها¶
بستهها روشی برای ساختاربندی فضای نام ماژولهای پایتون با استفاده از «نامهای نقطهدار ماژول» هستند. برای مثال، نام ماژول A.B یک زیرماژول با نام B در بستهای با نام A را مشخص میکند. همانطور که استفاده از ماژولها باعث میشود نویسندگان ماژولهای مختلف نگران نام متغیرهای سراسری یکدیگر نباشند، استفاده از نامهای نقطهدار ماژول نیز باعث میشود نویسندگان بستههای چندماژولهای مانند NumPy یا Pillow نگران نام ماژولهای یکدیگر نباشند.
فرض کنید میخواهید مجموعهای از ماژولها (یک «بسته») را برای مدیریت یکپارچهٔ فایلهای صوتی و دادههای صوتی طراحی کنید. قالبهای فایل صوتی بسیار متفاوتی وجود دارند (که معمولاً با پسوندشان شناخته میشوند، برای مثال: .wav، .aiff، .au)؛ بنابراین ممکن است نیاز داشته باشید مجموعهای در حال رشد از ماژولها را برای تبدیل بین قالبهای مختلف فایل ایجاد و نگهداری کنید. همچنین عملیاتهای مختلف بسیاری وجود دارند که ممکن است بخواهید روی دادههای صوتی انجام دهید (مانند ترکیب کردن، افزودن پژواک، اعمال تابع اکولایزر، ایجاد یک اثر استریوی مصنوعی)، بنابراین علاوه بر این، جریان بیپایانی از ماژولها برای انجام این عملیاتها خواهید نوشت. ساختار احتمالی زیر برای بستهٔ شما وجود دارد (که بهصورت یک سیستم فایل سلسلهمراتبی بیان شده است):
sound/ بستههای سطح بالا
__init__.py راهاندازی اولیه بسته صوتی
formats/ زیربسته برای تبدیل فرمتهای فایل
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ زیربسته برای جلوههای صوتی
__init__.py
echo.py
surround.py
reverse.py
...
filters/ زیربسته برای فیلترها
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
هنگام وارد کردن بسته، پایتون پوشههای موجود در sys.path را جستوجو میکند تا زیرپوشهٔ بسته را پیدا کند.
فایلهای __init__.py لازم هستند تا پایتون پوشههایی را که شامل این فایل هستند بهعنوان بسته در نظر بگیرد (مگر اینکه از یک namespace package استفاده شود که قابلیتی نسبتاً پیشرفته است). این کار از پنهان شدن ناخواستهٔ ماژولهای معتبر که بعداً در مسیر جستوجوی ماژول قرار دارند، توسط پوشههایی با نام مشترک مانند string جلوگیری میکند. در سادهترین حالت، __init__.py میتواند فقط یک فایل خالی باشد، اما میتواند کد مقداردهی اولیهٔ بسته را نیز اجرا کند یا متغیر __all__ را که بعداً توضیح داده میشود، تنظیم کند.
کاربران بسته میتوانند ماژولهای جداگانهای را از بسته وارد کنند، برای مثال:
import sound.effects.echo
این کار زیرماژول sound.effects.echo را بارگذاری میکند. این ماژول باید با نام کامل خود ارجاع داده شود.
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
روش جایگزین برای وارد کردن زیرماژول به شکل زیر است:
from sound.effects import echo
این روش نیز زیرماژول echo را بارگذاری میکند و آن را بدون پیشوند بسته در دسترس قرار میدهد، بنابراین میتوان از آن به شکل زیر استفاده کرد:
echo.echofilter(input, output, delay=0.7, atten=4)
یک حالت دیگر، وارد کردن مستقیم تابع یا متغیر موردنظر است:
from sound.effects.echo import echofilter
دوباره، این کار زیرماژول echo را بارگذاری میکند، اما باعث میشود تابع آن، یعنی echofilter()، مستقیماً در دسترس باشد:
echofilter(input, output, delay=0.7, atten=4)
توجه کنید که هنگام استفاده از from package import item، مورد item میتواند یک زیرماژول (یا زیربسته) از بسته باشد، یا نام دیگری باشد که در بسته تعریف شده است، مانند یک تابع، کلاس یا متغیر. دستور import ابتدا بررسی میکند که آیا item در بسته تعریف شده است یا خیر؛ اگر تعریف نشده باشد، فرض میکند که یک ماژول است و تلاش میکند آن را بارگذاری کند. اگر نتواند آن را پیدا کند، یک استثنای ImportError پرتاب میشود.
برعکس، هنگام استفاده از نحوی مانند import item.subitem.subsubitem، هر مورد بهجز آخرین مورد باید یک بسته باشد؛ آخرین مورد میتواند یک ماژول یا یک بسته باشد، اما نمیتواند یک کلاس، تابع یا متغیر تعریفشده در مورد قبلی باشد.
6.4.1. وارد کردن * از یک بسته¶
اکنون اگر کاربر بنویسد from sound.effects import * چه اتفاقی میافتد؟ در حالت ایدهآل، انتظار میرود این دستور بهنحوی به سیستم فایل مراجعه کند، زیرماژولهای موجود در بسته را پیدا کند و همهٔ آنها را وارد کند. این کار میتواند زمان زیادی طول بکشد و وارد کردن زیرماژولها ممکن است عوارض جانبی ناخواستهای پرتاب کند که باید فقط هنگام وارد کردن صریح زیرماژول رخ دهند.
تنها راهحل این است که نویسندهٔ بسته یک فهرست صریح از بسته فراهم کند. دستور import از قرارداد زیر استفاده میکند: اگر کد __init__.py یک بسته، فهرستی با نام __all__ تعریف کند، این فهرست بهعنوان فهرست نام ماژولهایی در نظر گرفته میشود که هنگام مشاهدهٔ from package import * باید وارد شوند. این وظیفهٔ نویسندهٔ بسته است که هنگام انتشار نسخهٔ جدید بسته، این فهرست را بهروز نگه دارد. نویسندگان بسته همچنین ممکن است تصمیم بگیرند از این قابلیت پشتیبانی نکنند، اگر کاربردی برای وارد کردن * از بستهٔ خود نمیبینند. برای مثال، فایل sound/effects/__init__.py میتواند شامل کد زیر باشد:
__all__ = ["echo", "surround", "reverse"]
این یعنی from sound.effects import * سه زیرماژول نامبردهٔ بستهٔ sound.effects را وارد خواهد کرد.
توجه داشته باشید که زیرماژولها ممکن است توسط نامهایی که بهصورت محلی تعریف شدهاند، پوشانده شوند. برای مثال، اگر یک تابع reverse به فایل sound/effects/__init__.py اضافه کنید، دستور from sound.effects import * فقط دو زیرماژول echo و surround را وارد خواهد کرد، اما زیرماژول reverse را وارد نمیکند، زیرا توسط تابع reverse تعریفشده در همان فضا پوشانده شده است:
__all__ = [
"echo", # به فایل «echo.py» اشاره میکند
"surround", # به فایل «surround.py» اشاره میکند
"reverse", # !!! اکنون به تابع «reverse» اشاره میکند !!!
]
def reverse(msg: str): # <-- این نام، زیرماژول «reverse.py» را میپوشاند
return msg[::-1] # در صورت استفاده از «from sound.effects import *»
اگر __all__ تعریف نشده باشد، دستور from sound.effects import * تمام زیرماژولهای بستهٔ sound.effects را به فضای نام فعلی وارد نمیکند؛ این دستور فقط اطمینان حاصل میکند که بستهٔ sound.effects وارد شده است (و احتمالاً هر کد مقداردهی اولیهای در __init__.py اجرا میشود) و سپس هر نامی را که در بسته تعریف شده است وارد میکند. این شامل هر نامی است که توسط __init__.py تعریف شده (و زیرماژولهایی که بهصورت صریح بارگذاری شدهاند) نیز میشود. همچنین شامل هر زیرماژول بستهای است که قبلاً توسط دستورهای import بهصورت صریح بارگذاری شدهاند. این کد را در نظر بگیرید:
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
در این مثال، ماژولهای echo و surround در فضای نام فعلی وارد میشوند، زیرا هنگام اجرای دستور from...import در بستهٔ sound.effects تعریف شدهاند. (این حالت زمانی که __all__ تعریف شده باشد نیز کار میکند.)
اگرچه برخی ماژولها طوری طراحی شدهاند که هنگام استفاده از import * فقط نامهایی مطابق با الگوهای خاص صادر کنند، همچنان این کار در کدهای تولیدی روشی نامناسب محسوب میشود.
به یاد داشته باشید که استفاده از from package import specific_submodule هیچ مشکلی ندارد! در واقع، این نشانهگذاری توصیهشده است، مگر اینکه ماژول واردکننده نیاز داشته باشد از زیرماژولهایی با نام یکسان از بستههای مختلف استفاده کند.
6.4.2. ارجاعهای درونبستهای¶
هنگامی که بستهها به زیربستهها ساختاربندی میشوند (مانند بستهٔ sound در مثال)، میتوانید از وارد کردن مطلق برای ارجاع به زیرماژولهای بستههای همسطح استفاده کنید. برای مثال، اگر ماژول sound.filters.vocoder نیاز داشته باشد از ماژول echo در بستهٔ sound.effects استفاده کند، میتواند از from sound.effects import echo استفاده کند.
همچنین میتوانید وارد کردن نسبی بنویسید، با استفاده از شکل from module import name از دستور import. این وارد کردنها از نقطههای ابتدایی برای نشان دادن بستههای فعلی و والد درگیر در وارد کردن نسبی استفاده میکنند. برای مثال، از ماژول surround ممکن است از دستور زیر استفاده کنید:
from . import echo
from .. import formats
from ..filters import equalizer
توجه کنید که وارد کردنهای نسبی بر اساس نام بستهٔ ماژول فعلی هستند. از آنجا که ماژول اصلی هیچ بستهای ندارد، ماژولهایی که قرار است بهعنوان ماژول اصلی یک برنامهٔ پایتون استفاده شوند، باید همیشه از وارد کردنهای مطلق استفاده کنند.
6.4.3. بستهها در چندین پوشه¶
بستهها یک ویژگی ویژهٔ دیگر را نیز پشتیبانی میکنند: __path__. این ویژگی مقداردهی اولیه میشود تا پیش از اجرای کد موجود در آن فایل، یک sequence از رشتهها باشد که نام پوشهٔ شامل __init__.py بسته را در خود دارد. این متغیر قابل تغییر است؛ تغییر آن بر جستوجوهای آینده برای ماژولها و زیربستههای موجود در بسته تأثیر میگذارد.
اگرچه این قابلیت معمولاً موردنیاز نیست، میتوان از آن برای گسترش مجموعهٔ ماژولهایی که در یک بسته پیدا میشوند استفاده کرد.
پانویسها