Modules/Packages:Charging a Battery
بايثون مشهورة بعبارة batteries included فاللغة نفسها لاتقدم سوى الsyntax ولكن لإستخدامها تم تقسيم الخدمات الى ملفات خارجية بإسم modules واذا كانت modules مترابطة تم تحزيمها فى package
على سبيل المثال بايثون لاتقوم بتوفير الدوال الخاصة بالرياضيات مباشرة
>>> cos(30)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'cos' is not defined
معنى السابق وجود خطأ فى السطر الأول من ال module “ابسط وحدة لتطبيق بايثون" ونوع الخطأ هو عدم وجود دالة ال cos فى مساحة البرنامج
اذا ؟ كيف اتعامل مع الدوال الرياضية ؟
يوجد فى مكتبات بايثون الأساسية modules لعمل معظم انواع التطبيقات قواعد بيانات شبكات حسابات معالجة بيانات .. الخ الخ
Importing a Module:
قم بإستدعاء الmodule المسماة math وهى وحدة تشمل العديد والعديد للقيام بالعمليات الحسابية بإستخدام import
هكذا قمنا بإستدعاء الوحدة لساحة البرنامج
لاحظ قد لاتكون ال module موجود فينتج ImportError
>>> try:
... import mymath
... except ImportError, e:
... print e
...
No module named mymath
تقدر تستخدم ال magical import لإستدعاء الموديلز ايضا
>>> msys=__import__('sys')
تقدر تستخدم as لعمل تسميه مختلفة( لموديل او احد عناصر موديل الخ) هنا للموديل مثل المثال السابق msys كبديل ل sys
Finding Nemo
اين توجد هذه الوحدات؟ هل يوجد شروط لإستدعاءها ام ماذا ؟
المفسر لايعلم عن كل ملف بايثون على جهازك ولكن هناك بعض الأماكن اللتى يبحث فيها قبل ان يرسل لك ال ImportError مثل المجلد الحالى او مجلد بايثون الإفتراضى او مجلد site-packages (يفضل استخدامه عند اضافة اى وحدات خارجية لبايثون)
للحصول على القائمة كاملة التى يبحث فيها المفسر استدعى sys.path
>>> import sys
>>> sys.path
['', '/usr/lib/python2.5/site-packages/Tempita-0.2-py2.5.egg', '/usr/lib/python2.5/site-packages/Mako-0.2.2-py2.5.egg',
....
....
>>>
First Module
يفضل دائما عند انشاء موديل او اى سكربت انك تنشئ هيدر مشابه للتالى
##################################################
# Author: Ahmed Youssef
# License: GPL V3
# Module: firstmodule
# Purpose: Learning modules
# Date: 12-20-2008
###################################################
انشئ ملف firstmodule.py وعرف فيها مجموعة من الدوال كالتالى
def aloha():
print "Aloha!"
def adios():
print "Adios!"
ايه الهدف من ال modules قلنا ؟ فصل التطبيق لأكثر من جزء لإمكانية استخدام خدماته اكثر من مرة وايضا تسهيل تقسيم العمل مثال شخص يكون مسئول عن جزئية التعامل مع قاعدة البيانات وشخص اخر مسئول عن الواجهة وهكذا
اوكى كتبنا الموديل ماذا الآن ؟
انشئ ملف جديد وليكن greeter.py هيتم فيه استخدام الدوال aloha, adios الموجودة فى firstmodule.py
import firstmodule
firstmodule.aloha()
firstmodule.adios()
عند التنفيذ
striky@striky-desktop:~/workspace/pytut/src$ python greeter.py
Aloha!
Adios!
Using fromربما لاتريد ان تكتب اسم الموديل فى كل مرة او ربما ماتريده او دالة معينة فقط الخ
from firstmodule import aloha, adios
aloha()
adios()
او ربما تريد استدعاء جميع محتويات module ما قم فقط بتنفيذ جملة الإستدعاء
حيث * تعنى جميع المحتويات
Reloading ربما تستخدم بيئة تفاعلية او ربما نظام معين يكون عملية اعادة التشغيل فيه مكلفة او غيرهاا وقمت بتعديل module معينة قم بتنفيذ الدالة reload لإعادة تحميل ال module بالتعديلات الجديدة
__main__
احترس من اختبار الكود فى ال module وإلا سيتم تنفيذه بمجرد استدعائها
اذا عدلت كود ال firstmodule.py الى
def aloha():
print "Aloha!"
def adios():
print "Adios!"
aloha()
وقمت بتنفيذ ال greeter.py ستجد الناتج كالتالى
striky@striky-desktop:~/workspace/pytut/src$ python greeter.py
Aloha!
Aloha!
Adios!
مع اننا استدعينا aloha مرة واحدة ولكن تم تنفيذها مرتان وذلك بسبب استدعائها فى ال firstmodule
والحل ؟ الااستطيع اختبار الكود ؟
اليس الأفضل ان تختبر اولا اذا كانت ال module هى اللتى يتم تنفيذها كملف رئيسى او مجرد مستدعاه فى ملف اخر ؟ كيف اعلم هذا ؟
بكل بساطة توفر لك ال modules متغير خاص بإسم __name__ يحوى اسم ال module الحالية وهو "__main__”
قم بإضافة شرط فى نهاية ال firstmodule كالتالى
if __name__=="__main__":
#Testing...
aloha()
وتم حل المشكلة عند تنفيذلك لل greeter.py سيتم طباعة Aloha! و Adios! وعند تنفيذك لل firstmodule فتكون هى السكربت الرئيسى اللذى سيتم تنفيذه او ال __main__ فيتم تنفيذ كود الإختبار او اىاكان
__all__للتحكم فيما يمكن استدعاءه من وحدة ما تستطيع استخدام متغير خاص بإسم __all__
__all__=["aloha"]
Packages للآن جيد ماذا لو زاد عدد ال modules تستطيع بالتأكيد انشاء المئات من تلك الملفات ولكن يجب عليك تحزيم الmodules المترابطة مثلا اذا كنا ننشئ مشروع عن التعامل مع قواعد بيانات مختلفة oracle, sqlite, mysql وغيرهم اليس الأفضل تجميعهم فى حزمة ما بإسم databases مثلا ؟ ويتم استدعاءها
او مثلا
from databases import mysql
بكل تأكيد هذا اكثر تنظيما
myfirstpackage/
|-- __init__.py
|-- mysql.py
|-- oracle.py
`-- sqlite.py
لدينا حزمة بإسم myfirstpackage وتشمل 4 ملفات
1- ال __init__ وفيه بيتم تحديد الوحدات اللتى نريد تحميلها مباشرة وربما بعض المتغيرات الأساسية ؟
2- 3 وحدات بإسم mysql.py, oracle.py, sqlite.py معرفين كالتالين
الملف mysql.py
def about():
print "mysql module."
الملف oracle.py
def about():
print "oracle module."
الملف sqlite.py
def about():
print "sqlite module."
الملف __init__
print "__init__ myfirstpackage"
import mysql
VERSION="1.42.0"
لاحظ اننا قمنا بعمل import ل mysql مباشرة وحددنا متغير بإسم VERSION
انشئ ملف dbtester.py
import myfirstpackage
print dir(myfirstpackage)
print myfirstpackage.VERSION
myfirstpackage.mysql.about()
myfirstpackage.sqlite.about()
ستجد الناتج مشابه للتالى
striky@striky-desktop:~/workspace/pytut/src$ python dbtester.py
__init__ myfirstpackage
['VERSION', '__builtins__', '__doc__', '__file__', '__name__', '__path__', 'mysql']
1.42.0
mysql module.
Traceback (most recent call last):
File "dbtester.py", line 6, in <module>
myfirstpackage.sqlite.about()
AttributeError: 'module' object has no attribute 'sqlite'
وبكل تأكيد لن سيتم رفع استثناء AttributeError بسبب عدم استدعاء sqlite للساحة إلا اذا قمت بإضافتها يدويا
import myfirstpackage.sqlite
Platformكثيرا مانحتاج للحصول على معلومات عن النظام الذي يعمل عليه البرنامج (لإختبار التوافقية ، الإعتماديات او ربما العلم بالشئ)
تقدم لنا بايثون وحدة بإسم platform
مثال
striky@striky-desktop:~/workspace/pytut/src$ python platformreport.py
[architecture => ('32bit', 'ELF')]
[dist => ('debian', 'lenny/sid', '')]
[java_ver => ('', '', ('', '', ''), ('', '', ''))]
[libc_ver => ('glibc', '2.4')]
[mac_ver => ('', ('', '', ''), '')]
[machine => i686]
[node => striky-desktop]
[platform => Linux-2.6.27-9-generic-i686-with-debian-lenny-sid]
[processor => ]
[python_build => ('r252:60911', 'Oct 5 2008 19:24:49')]
[python_compiler => GCC 4.3.2]
[python_version => 2.5.2]
[python_version_tuple => ['2', '5', '2']]
[release => 2.6.27-9-generic]
[system => Linux]
[uname => ('Linux', 'striky-desktop', '2.6.27-9-generic', '#1 SMP Thu Nov 20 21:57:00 UTC 2008', 'i686', '')]
[version => #1 SMP Thu Nov 20 21:57:00 UTC 2008]
[win32_ver => ('', '', '', '')]
تستطيع بكل تأكيد كتابة كل function مثلا
platform.dist()
platform.machine()
platform.uname()
الخ الخ
ولكن ربما نستخدم حيلة صغيرة لإستدعاءهم جميعا ؟
import platform
for s in dir(platform):
if not s.startswith("_"): #If it does not start with an underscore.
f=getattr(platform, s) #Fetch the attribute (should be a function..)
try:
print "[%s => %s]"%(s, f()) #Prints attr, returned value
except:
pass #Global catch for functions requires params(e.g popen).
هنا نقوم بعرض محتويات platform بإستخدام dir ونحصل على ال function object بإستخدام getattr من الوحدة وبإسم الدالة ونقوم بتنفيذها (مجرد استدعائها بعد الحصول عليها) وبس كدا