ماژول ها (Modules)
دیدید که چگونه می توان با تعریف یک بارۀ توابع، کد را در برنامۀ خود مجدداً مورد استفاده قرار دهید. اما اگر بخواهید تعدادی از توابع را در برنامه های دیگری که می نویسید نیز دوباره استفاده کنید، چه؟ همانطور که احتمالاً حدس زده اید، پاسخ ماژول ها هستند.
روش های متفاوتی برای نوشتن ماژول ها وجود دارد، اما ساده ترین روش ایجاد فایلی با پسوند py. است که شامل متغیرها و توابع نیز باشد.
روش دیگر، نوشتن ماژول ها به زبان بومی ای است که خود مفسر پایتون با آن نوشته شده است. برای مثال، می توانید ماژول ها را در C programming language بنویسید و پس از کامپایل، هنگام استفاده از مفسر پایتون، آنها را از داخل کد پایتون خود استفاده کنید.
یک ماژول می تواند از طریق برنامۀ دیگری وارد شود تا بتوان از قابلیت های آن استفاده کرد. به همین روش کتابخانۀ استاندارد پایتون نیز مورد استفاده قرار می گیرد. ابتدا نحوۀ استفاده از ماژول های کتابخانۀ استاندارد پایتون را مورد بررسی قرار می دهیم.
مثال را با نام module_using_sys.py ذخیره کنید:
import sys
print('The command line arguments are:')
for i in sys.argv:
print(i)
print('\n\nThe PYTHONPATH is', sys.path, '\n')
خروجی:
$ python module_using_sys.py we are arguments # each arg is separated by white space
The command line arguments are:
module_using_sys.py
we
are
arguments
The PYTHONPATH is ['/tmp/py',
# many entries here, not shown here
'/Library/Python/2.7/site-packages',
'/usr/local/lib/python2.7/site-packages']
نحوۀ عملکرد
ابتدا با استفاده از دستور import، ماژول sys را وارد می کنیم . در اصل، این کار به معنای این است که به پایتون اعلام می کنیم می خواهیم از این ماژول استفاده کنیم. ماژول sys شامل قابلیت هایی مرتبط با مفسر پایتون و محیط آن است؛ یعنی قابلیت هایی مربوط به سیستم (system).
زمانی که پایتون دستور import sys را اجرا می کند، به دنبال ماژول sys می گردد. در این حالت، sys یکی از ماژول های داخلی است. بنابراین، پایتون می داند آن را از کجا پیدا کند.
اگر این ماژول یک ماژول کامپایل شده نباشد، یعنی ماژولی باشد که با زبان پایتون نوشته شده است، مفسر پایتون آن را در پوشه هایی که در متغیر sys.path لیست شده اند جست و جو می کند. اگر ماژول پیدا شود، دستورات موجود در بدنۀ آن اجرا می شود و ماژول برای استفادۀ شما در دسترس قرار می گیرد. توجه داشته باشید که مقدار دهی اولیه ماژول تنها در اولین باری که آن را وارد می کنیم، انجام می شود.
متغیر argv در ماژول sys با استفاده از نشانه گذاری نقطه ای یعنی sys.argv در دسترس قرار می گیرد. این شیوه به روشنی نشان می دهد که این نام بخشی از ماژول sys است. مزیت دیگر این رویکرد این است که این نام با هیچ متغیر argv ای که در برنامۀ خود استفاده کرده اید، تداخل پیدا نمی کند.
متغیر sys.argv یک لیست (list) از رشته ها است (لیست ها در later chapter به طور مفصل بررسی خواهد شد). به طور مشخص، sys.argv شامل لیستی از آرگومان های خط فرمان است؛ یعنی آرگومان هایی که با استفاده از خط فرمان به برنامۀ شما فرستاده می شود.
اگر برای نوشتن و اجرای این برنامه ها از IDE استفاده می کنید، در منو های آن به دنبال راهی برای مشخص کردن آرگومان های خط فرمان برنامه باشید.
در اینجا، هنگامی که دستور python module_using_sys.py را اجرا می کنیم، ماژول module_using_sys.py را با دستور python اجرا کرده ایم و سایر مواردی که پس از آن آمده اند، آرگومان هایی هستند که به برنامه ارسال شده اند. پایتون این آرگومان های خط فرمان را در متغیر sys.argv ذخیره می کند تا بتوانیم از آنها استفاده کنیم.
به خاطر داشته باشید که نام اسکریپتی که در حال اجراست، همیشه اولین عنصر در لیست sys.argv است. بنابراین، در این مثال، 'module_using_sys.py' در sys.argv[0]، مقدار 'we' در sys.argv[1]، مقدار 'are' در sys.argv[2] و مقدار 'arguments' در sys.argv[3] قرار می گیرند. توجه کنید که پایتون شمارش را از 0 آغاز می کند و نه از 1.
ماژول sys.path شامل لیستی از نام پوشه هایی است که ماژول ها از آن ها وارد می شوند. توجه کنید که نخستین رشته در sys.path خالی است. این رشته خالی نشان می دهد که پوشۀ جاری نیز بخشی از sys.path است؛ این مورد با متغیر محیطی PYTHONPATH مرتبط است. بنابراین، این بدین معناست که می توانید مستقیماً ماژول هایی را که در پوشۀ جاری قرار گرفته اند را وارد کنید. در غیر این صورت، باید ماژول خود را در یکی از پوشه هایی قرار دهید که در sys.path فهرست شده اند.
توجه داشته باشید که پوشۀ جاری، پوشه ای است که برنامه از آن اجرا شده است. برای یافتن پوشۀ جاری برنامه خود، دستور import os; print(os.getcwd()) را اجرا کنید.
فایل های Byte-compiled .pyc
وارد کردن یک ماژول نسبتاً پر هزینه است؛ بنابراین پایتون برای سریع تر کردن این فرآیند، ترفندهایی را به کار می گیرد. یکی از روش ها ایجاد فایل های byte-compiled با پسوند pyc. است. این فایل ها شکلی میانی هستند که پایتون برنامه را به (بخش مقدمه را به خاطر داشته باشیدکه چگونه پایتون عمل می کند؟). فایل pyc. هنگام وارد کردن دوبارۀ ماژول از یک برنامۀ دیگر مفید تر است؛ زیرا این کار بسیار سریع تر انجام می شود، چرا که بخشی از پردازش مورد نیاز برای وارد کردن یک ماژول از قبل انجام شده است. علاوه بر این، این فایل های byte-compiled مستقل از پلتفرم هستند.
توجه: این فایل های pyc. معمولاً در همان پوشه ای ایجاد می شوند که فایل های pyc. متناظر در آن قرار دارند. اگر پایتون اجازۀ نوشتن فایل در آن پوشه را نداشته باشد، فایل های pyc. ایجاد نخواهند شد.
دستور from..import
اگر می خواهید مستقیماً متغیر argv را به برنامه خود وارد کنید (تا مجبور نباشید هر بار .sys را تایپ کنید)، می توانید از دستور from sys import argv استفاده کنید.
هشدار: به طور کلی، از دستور
from..importاجتناب کنید و به جای آن از دستورimportاستفاده کنید. دلیل این موضوع آن است که برنامۀ شما از تداخل نام ها پرهیز می کند و خوانایی بیشتری خواهد داشت.
مثال:
from math import sqrt
print("Square root of 16 is", sqrt(16))
A module's __name__
هر ماژول دارای یک نام است و دستورات موجود در یک ماژول می توانند نام ماژول خود را تشخیص دهند. این قابلیت به طور خاص برای تشخیص اینکه آیا ماژول به صورت مستقل در حال اجراست یا توسط ماژول دیگری وارد شده است، مفید واقع می شود. همان طور که پیش تر اشاره شد، هنگامی که یک ماژول برای نخستین بار وارد می شود، کدی که در آن قرار دارد اجرا می شود. می توانیم از این ویژگی استفاده کنیم تا ماژول با توجه به اینکه به صورت مستقل مورد استفاده قرار گرفته یا از ماژول دیگری وارد می شود، رفتار متفاوتی داشته باشد. این امر با استفاده از ویژگی __name__ ماژول امکان پذیر است.
مثال را با نام module_using_name.py ذخیره کنید:
if __name__ == '__main__':
print('This program is being run by itself')
else:
print('I am being imported from another module')
خروجی:
$ python module_using_name.py
This program is being run by itself
$ python
>>> import module_using_name
I am being imported from another module
>>>
نحوۀ عملکرد
هر ماژول پایتون دارای __name__ تعریف شده است. اگر مقدار آن '__main__' باشد، به این معناست که ماژول توسط کاربر به صورت مستقل در حال اجراست و می توانیم اقدامات مناسب را انجام دهیم.
ساخت ماژول های خودتان
ایجاد ماژول های شخصی آسان است، در واقع، شما تمام مدت مشغول انجام همین کار بودید! دلیل آن این است که هر برنامۀ پایتون، خود یک ماژول نیز هست. تنها کافیست مطمئن شوید که فایل دارای پسوند py. باشد. مثال زیر این موضوع را روشن تر خواهد کرد.
مثال را تحت عنوان mymodule_demo.py ذخیره کنید:
def say_hi():
print('Hi, this is mymodule speaking.')
__version__ = '0.1'
نمونۀ بالا یک ماژول بود. همان طور که مشاهده می کنید، در مقایسه با برنامه های معمول پایتونی که تاکنون نوشته ایم، چیز خاصی در آن وجود ندارد. در ادامه خواهیم دید که چگونه از این ماژول در سایر برنامه های پایتون خود استفاده کنیم.
به خاطر داشته باشید که ماژول باید یا در همان پوشه ای قرار داشته باشد که برنامه ای که آن را وارد می کند در آن قرار دارد، یا در یکی از پوشه هایی باشد که در sys.path لیست شده اند.
ماژول دیگر را با نام mymodule_demo.py ذخیره کنید:
import mymodule
mymodule.say_hi()
print('Version', mymodule.__version__)
خروجی:
$ python mymodule_demo.py
Hi, this is mymodule speaking.
Version 0.1
نحوۀ عملکرد
توجه کنید که برای دسترسی به اعضای ماژول، از همان نشانه گذاری نقطه ای استفاده می کنیم. پایتون از همین نشانه گذاری به خوبی و در بخش های مختلف استفاده می کند تا ویژگی متمایز 'Pythonic' خود را ایجاد کند؛ بنابراین مجبور نیستیم برای انجام کارهای مختلف، روش های جدید و متفاوتی را یاد بگیریم.
در اینجا نسخه ای را می بینیم که از نحو from..import استفاده می کند (آن را با نام mymodule_demo2.py ذخیره کنید):
from mymodule import say_hi, __version__
say_hi()
print('Version', __version__)
خروجی mymodule_demo2.py با خروجی mymodule_demo.py یکسان است.
توجه داشته باشید که اگر در ماژولی که mymodule را وارد می کند، از قبل نامی به نام __version__ تعریف شده باشد، تداخل نام ایجاد خواهد شد. این امر از آن جهت محتمل است که یک رویۀ رایج این است که هر ماژول شمارۀ نسخۀ خود را با استفاده از همین نام اعلام می کند. بنابراین، همواره پیشنهاد می شود که از دستور import استفاده کنید، حتی اگر این کار باعث می شود برناۀ شما کمی طولانی تر شود.
همچنین می توانید از دستور زیر استفاده کنید:
from mymodule import *
این دستور تمام نام های عمومی، مانند say_hi را وارد می کند؛ اما __version__ را وارد نمی کند، زیرا این نام با دو زیرخط آغاز می شود.
هشدار: به یاد داشته باشید که باید از استفاده از import-star، یعنی
*from mymodule import، پرهیز کنید.
Zen of Python
یکی از اصول راهنمای پایتون این است که «صریح بودن بهتر از ضمنی بودن است». برای یادگیری بیشتر با این اصول، دستور
import thisرا در پایتون اجرا کنید.
تابع dir
تابع داخلی ()dir لیستی از نام هایی را که توسط یک شئ تعریف شده اند را باز می گرداند. اگر شئ مورد نظر یک ماژول باشد، این لیست شامل توابع، کلاس ها و متغیرهایی است که درون آن ماژول تعریف شده اند.
این تابع می تواند آرگومان ها را دریافت کند. اگر آرگومان، نام یک ماژول باشد، تابع لیستی از نام های موجود در همان ماژول مشخص شده را بر می گرداند. اگر هیچ آرگومانی ارائه نشود، تابع لیستی از نام های موجود در ماژول جاری را باز می گرداند.
مثال:
$ python
>>> import sys
# sys دریافت نام ویژگی های موجود در ماژول
>>> dir(sys)
['__displayhook__', '__doc__',
'argv', 'builtin_module_names',
'version', 'version_info']
# در اینجا تنها چند مورد از لیست نمایش داده شده است
# دریافت نام ویژگی های ماژول جاری
>>> dir()
['__builtins__', '__doc__',
'__name__', '__package__', 'sys']
# 'a' ایجاد یک متغیر جدید با نام
>>> a = 5
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys', 'a']
# حذف یک نام
>>> del a
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys']
نحو عملکرد
ابتدا کاربرد dir را بر روی ماژول وارد شدۀ sys، مشاهده می کنیم. می توانیم لیست بسیار بزرگی از ویژگی هایی را که در این ماژول قرار دارد ببینیم.
سپس از تابع dir بدون ارسال هیچ پارامتری استفاده می کنیم. این تابع به طور پیش فرض لیستی از ویژگی های ماژول جاری را باز می گرداند. توجه کنید که ماژول های وارد شده نیز بخشی از این لیست هستند.
برای مشاهدۀ عملکرد dir، یک متغیر جدید با نام a تعریف می کنیم، مقداری را به آن نسبت می دهیم و سپس dir را بررسی می کنیم. مشاهده می کنیم که مقدار جدیدی با همان نام نیز در لیست ظاهر شده است. سپس متغیر/ویژگی ماژول جاری را با استفاده از دستور del حذف می کنیم و این تغییر نیز بار دیگر در خروجی تابع dir منعکس می شود.
یک نکته دربارۀ del: این دستور برای حذف یک متغیر/نام استفاده می شود و پس از اجرای دستور، در اینجا del a، دیگر نمی توانید به متغیر a دسترسی داشته باشید؛ گویی این متغیر هرگز وجود نداشته است.
توجه داشته باشید که تابع ()dir بر روی هر شئ ای کار می کند. برای مثال، dir(str) را اجرا کنید تا ویژگی های کلاس str (رشته) را مشاهده کنید.
همچنین تابع ()vars وجود دارد که می تواند ویژگی ها و مقادیر آن ها را در اختیار شما قرار دهد، اما در همۀ موارد قابل استفاده نیست.
بسته ها (Packages)
تا اینجا، احتمالاً متوجۀ سلسله مراتب نظم دهی برنامه های خود شده اید. متغیرها معمولاً درون توابع قرار می گیرند. توابع و متغیرهای سراسری معمولاً درون ماژول ها قرار می گیرند. اما اگر بخواهید ماژول ها را سازماندهی کنید، چه؟ اینجاست که بسته ها (packages) وارد می شوند.
بسته ها در اصل پوشه هایی از ماژول ها هستند که دارای یک فایل ویژۀ init__.py__ هستند. این فایل به پایتون نشان می دهد که این پوشه از اهمیت ویژه ای برخوردار است زیرا شامل ماژول های پایتون است.
فرض کنید می خواهید بسته ای به نام 'world' ایجاد کنید که دارای زیر بسته های 'asia' , 'africa' و غیره باشد و این زیر بسته ها نیز به نوبۀ خود شامل ماژول هایی مانند 'india' , 'madagascar' و غیره باشند.
ساختار پوشه ها به این صورت خواهد بود:
- <some folder present in the sys.path>/
- world/
- __init__.py
- asia/
- __init__.py
- india/
- __init__.py
- foo.py
- africa/
- __init__.py
- madagascar/
- __init__.py
- bar.py
بسته ها در واقع راهی برای سازماندهی سلسله مراتبی ماژول ها هستند. نمونه های بسیاری از این ساختار را در کتابخانۀ استاندارد مشاهده خواهید کرد.
جمع بندی
همانطور که توابع بخش های قابل استفادۀ مجدد برنامه ها هستند، ماژول ها نیز برنامه هایی با قابلیت استفاده مجدد هستند. بسته ها نیز سطح دیگری از سلسله مراتب را برای سازماندهی ماژول ها فراهم می کنند. کتابخانۀ استانداردی که همراه پایتون ارائه می شود، نمونه ای از چنین مجموعه ای از بسته ها و ماژول هاست.
در این بخش دیدیم که چگونه از این ماژول ها استفاده کنیم و ماژول های خودمان را ایجاد کنیم.
در بخش بعدی، با مفاهیم جذابی به نام ساختارهای داده (data structures) آشنا خواهیم شد.