6.1. حول الوحدات النمطية #
إذا خرجت من مُفسّر بايثون وأعدت تشغيله، فستفقد التعريفات التي أنشأتها (الدوال والمتغيرات). لذلك، إذا كنت ترغب في كتابة برنامج أطول، فمن الأفضل استخدام مُحرّر نصوص لتحضير المُدخلات للمُفسّر وتشغيله مع هذا الملف كمُدخلات. يُعرف هذا بإنشاء سكربت. مع ازدياد طول برنامجك، قد ترغب في تقسيمه إلى عدة ملفات لتسهيل صيانته. قد ترغب أيضًا في استخدام دالة مفيدة كتبتها في عدة برامج دون نسخ تعريفها في كل برنامج.
لدعم ذلك، تُتيح بايثون طريقة لوضع التعريفات في ملف واستخدامها في سكربت أو في نسخة تفاعلية من المُفسّر. يُسمى هذا الملف وحدة نمطية؛ ويمكن استيراد تعريفات الوحدة النمطية إلى وحدات أخرى أو إلى الوحدة النمطية الرئيسية (مجموعة المتغيرات التي يمكنك الوصول إليها في سكربت يُنفّذ في المستوى الأعلى وفي وضع الآلة الحاسبة).
الوحدة النمطية هي ملف يحتوي على تعريفات وجمل بايثون. اسم الملف هو اسم الوحدة النمطية مع إضافة اللاحقة .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
المزيد عن الوحدات النمطية #
يمكن أن تحتوي الوحدة النمطية على عبارات قابلة للتنفيذ، بالإضافة إلى تعريفات للوظائف. تهدف هذه العبارات إلى تهيئة الوحدة. تُنفَّذ هذه العبارات فقط عند أول ظهور لاسم الوحدة النمطية في عبارة الاستيراد. 1 (تُنفَّذ أيضًا عند تنفيذ الملف كبرنامج نصي).
لكل وحدة نمطية جدول رموز خاص بها، والذي تستخدمه جميع الدوال المُعرَّفة في الوحدة النمطية كجدول رموز عام. وبالتالي، يمكن لمُنشئ الوحدة النمطية استخدام المتغيرات العامة فيها دون القلق بشأن التعارضات العرضية مع المتغيرات العامة للمستخدم. من ناحية أخرى، إذا كنتَ مُلِمًّا بما تفعله، يمكنكَ استخدام نفس الترميز المُستخدم للإشارة إلى دوالها، modname.itemname.
يمكن للوحدات النمطية استيراد وحدات نمطية أخرى. من المُعتاد، ولكن ليس إلزاميًا، وضع جميع عبارات الاستيراد في بداية الوحدة النمطية (أو البرنامج النصي). تُوضَع أسماء الوحدات النمطية المستوردة في جدول الرموز العام للوحدة المُستوردة.
هناك صيغة مُتغيرة لعبارة الاستيراد تستورد الأسماء من الوحدة النمطية مباشرةً إلى جدول رموز الوحدة المُستوردة. على سبيل المثال:
>>> 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
هذا يُمثل استيرادًا فعليًا للوحدة بنفس طريقة استيراد 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]))
يمكنك جعل الملف قابلاً للاستخدام كبرنامج نصي بالإضافة إلى كونه وحدة نمطية قابلة للاستيراد، لأن الكود الذي يُحلل سطر الأوامر لا يعمل إلا إذا تم تنفيذ الوحدة النمطية كملف “main”:
$ python fibo.py 50
0 1 1 2 3 5 8 13 21 34
إذا تم استيراد الوحدة النمطية، فلن يتم تشغيل الكود:
>>> import fibo
>>>
غالبًا ما يُستخدم هذا إما لتوفير واجهة مستخدم سهلة الاستخدام للوحدة النمطية، أو لأغراض الاختبار (تشغيل الوحدة النمطية كبرنامج نصي يُنفذ حزمة اختبار).
6.1.2. مسار البحث عن الوحدة النمطية #
عند استيراد وحدة نمطية باسم spam، يبحث المُفسّر أولًا عن وحدة نمطية مُضمنة بهذا الاسم. إذا لم يتم العثور عليه، فسيبحث عن ملف باسم spam.py في قائمة المجلدات التي يحددها المتغير sys.path. يُهيأ sys.path من المواقع التالية:
- الدليل الذي يحتوي على البرنامج النصي المُدخل (أو الدليل الحالي عند عدم تحديد أي ملف).
- PYTHONPATH (قائمة بأسماء المجلدات، بنفس صيغة متغير shell PATH).
- الإعداد الافتراضي يعتمد على التثبيت.
ملاحظة: في أنظمة الملفات التي تدعم الروابط الرمزية، يُحسب الدليل الذي يحتوي على البرنامج النصي المُدخل بعد اتباع الرابط الرمزي. بمعنى آخر، لا يُضاف الدليل الذي يحتوي على الرابط الرمزي إلى مسار بحث الوحدة النمطية.
بعد التهيئة، يمكن لبرامج بايثون تعديل sys.path. يُوضع الدليل الذي يحتوي على البرنامج النصي قيد التشغيل في بداية مسار البحث، قبل مسار المكتبة القياسي. هذا يعني أنه سيتم تحميل البرامج النصية في هذا الدليل بدلاً من الوحدات النمطية التي تحمل الاسم نفسه في دليل المكتبة. هذا خطأ ما لم يكن الاستبدال مقصودًا. راجع قسم الوحدات النمطية القياسية لمزيد من المعلومات.
6.1.3. ملفات بايثون “المُجمّعة” #
لتسريع تحميل الوحدات، تُخزّن بايثون النسخة المُجمّعة من كل وحدة في مجلد __pycache__ تحت اسم module.version.pyc، حيث يُرمّز الإصدار تنسيق الملف المُجمّع؛ ويحتوي عادةً على رقم إصدار بايثون. على سبيل المثال، في إصدار CPython 3.3، تُخزّن النسخة المُجمّعة من spam.py مؤقتًا باسم __pycache__/spam.cpython-33.pyc. تسمح هذه التسمية للوحدات المُجمّعة من إصدارات وإصدارات بايثون المختلفة بالتواجد معًا.
تتحقق بايثون من تاريخ تعديل المصدر مقارنةً بالنسخة المُجمّعة لمعرفة ما إذا كانت قديمة وتحتاج إلى إعادة تجميع. هذه عملية تلقائية تمامًا. كما أن الوحدات المُجمّعة مستقلة عن النظام الأساسي، لذا يمكن مشاركة المكتبة نفسها بين الأنظمة ذات البنى المختلفة.
لا يتحقق بايثون من ذاكرة التخزين المؤقت في حالتين. أولاً، يُعيد التجميع دائمًا ولا يُخزّن نتيجة الوحدة التي تُحمّل مباشرةً من سطر الأوامر. ثانيًا، لا يتحقق من ذاكرة التخزين المؤقت في حال عدم وجود وحدة مصدر. لدعم توزيع غير مصدري (مُجمّع فقط)، يجب أن تكون الوحدة المُجمّعة في مجلد المصدر، ويجب ألا تكون هناك وحدة مصدر.
بعض النصائح للخبراء: #
يمكنك استخدام مفتاحي -O أو -OO في أمر بايثون لتقليل حجم الوحدة المُجمّعة. يُزيل مفتاح -O عبارات التأكيد، ويُزيل مفتاح -OO كلاً من عبارات التأكيد وسلاسل __doc__. نظرًا لأن بعض البرامج قد تعتمد على توفر هذه العبارات، يجب عليك استخدام هذا الخيار فقط إذا كنت تعرف ما تفعله. تحتوي الوحدات “المُحسّنة” على علامة اختيار وعادةً ما تكون أصغر حجمًا. قد تُغيّر الإصدارات المستقبلية تأثيرات التحسين.
لا يعمل البرنامج بشكل أسرع عند قراءته من ملف .pyc مقارنةً بقراءته من ملف .py؛ الشيء الوحيد الأسرع في ملفات .pyc هو سرعة تحميلها.
يمكن لوحدة compileall إنشاء ملفات .pyc لجميع الوحدات في مجلد.
يتوفر المزيد من التفاصيل حول هذه العملية، بما في ذلك مخطط انسيابي للقرارات، في PEP 3147.