6. ماژولها¶
اگر از مفسر پایتون خارج شوید و دوباره وارد آن شوید، تعریفهایی که ایجاد کردهاید (توابع و متغیرها) از بین میروند. بنابراین، اگر میخواهید برنامهای نسبتاً طولانیتر بنویسید، بهتر است بهجای آن از یک ویرایشگر متن برای آمادهسازی ورودی مفسر استفاده کنید و آن را با استفاده از آن پرونده بهعنوان ورودی اجرا کنید. این کار با نام ایجاد یک اسکریپت شناخته میشود. هرچه برنامهی شما طولانیتر شود، ممکن است بخواهید آن را برای نگهداری آسانتر به چندین پرونده تقسیم کنید. همچنین ممکن است بخواهید از یک تابع کاربردی که در چندین برنامه نوشتهاید، بدون کپی کردن تعریف آن در هر برنامه استفاده کنید.
برای پشتیبانی از این کار، پایتون روشی دارد که به شما اجازه میدهد تعریفها را در یک پرونده قرار دهید و از آنها در یک اسکریپت یا در یک نمونهی تعاملی از مفسر استفاده کنید. چنین پروندهای یک ماژول نامیده میشود؛ تعریفهای یک ماژول را میتوان به ماژولهای دیگر یا به ماژول اصلی (main) (مجموعهای از متغیرهایی که در یک اسکریپت اجراشده در سطح بالا و در حالت ماشینحساب به آنها دسترسی دارید) ایمپورت کرد.
یک ماژول پروندهای است که شامل تعریفها و دستورهای پایتون است. نام پرونده، نام ماژول بههمراه پسوند .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 تعریف شدهاند، مستقیماً به فضای نام فعلی اضافه نمیکند (برای جزئیات بیشتر به محدودهها و فضای نامها در پایتون مراجعه کنید)؛ بلکه فقط نام ماژول 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. بیشتر دربارهی ماژولها¶
یک ماژول میتواند علاوه بر تعریف توابع، شامل دستورهای اجرایی نیز باشد. هدف از این دستورها مقداردهی اولیهی ماژول است. این دستورها فقط در اولین باری که نام ماژول در یک دستور ایمپورت دیده میشود اجرا میشوند. [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مدیریت میشود).
جزئیات بیشتر در مقداردهی اولیهی مسیر جستجوی ماژول sys.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", # 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 را به فضای نام فعلی ایمپورت نمیکند؛ این دستور فقط اطمینان حاصل میکند که بستهی 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
توجه کنید که ایمپورت کردنهای نسبی بر اساس نام بستهی ماژول فعلی هستند. از آنجا که ماژول اصلی هیچ بستهای ندارد، ماژولهایی که قرار است بهعنوان ماژول اصلی یک برنامهی پایتون استفاده شوند، باید همیشه از ایمپورت کردنهای مطلق استفاده کنند.
پانویسها