الوحدات
Modules
الوحدات — Modules
كلما كبر البرنامج، صار من المستحيل عملياً أن تضع كل شيء في ملف واحد. تخيل تطبيقاً يضم دوال حسابية، ومنطق تحليل بيانات، وكوداً لعرض النتائج — كلها متشابكة في ألف سطر. ستجد نفسك تبحث عن كل دالة بالتمرير، وتخشى تعديل شيء لأنك لا تعرف ما الذي يعتمد عليه. الوحدات هي الحل الذي بنت عليه Python نظامها منذ البداية.
ما هي الوحدة؟
الوحدة (Module) في Python ببساطة هي ملف .py. أي ملف تكتبه ينتهي بـ .py هو وحدة يمكن استيرادها في ملفات أخرى. هذا كل شيء — لا إعداد خاص، لا تسجيل، لا إعلان. ملف math_tools.py هو وحدة اسمها math_tools.
عندما تكتب import math، فأنت تطلب من Python أن تجد ملفاً اسمه math.py (أو وحدة مدمجة بهذا الاسم)، وتحمّل محتواه في برنامجك تحت الاسم math. بعدها يمكنك الوصول لأي شيء داخله عبر نقطة: math.sqrt(9).
الفكرة الجوهرية هي الفصل بين الاهتمامات: كل وحدة تعرف شيئاً واحداً وتفعله جيداً. وحدة الحسابات لا تعرف شيئاً عن العرض، ووحدة التنسيق لا تعرف شيئاً عن قاعدة البيانات. هذا يجعل كل جزء قابلاً للاختبار والاستبدال بشكل مستقل.
استيراد وحدة كاملة — import
أبسط شكل للاستيراد هو جلب الوحدة كلها:
import math
# الآن كل دوال math متاحة عبر النقطة — access via dot notation
print(math.sqrt(16)) # 4.0
print(math.pi) # 3.141592653589793
print(math.floor(3.7)) # 3
print(math.ceil(3.2)) # 4
الاستيراد بهذا الشكل يُبقي الأسماء منفصلة — sqrt في كودك لا تتعارض مع sqrt في مكتبة أخرى لأن الأولى تُكتب math.sqrt دائماً. هذا أسلوب جيد لأنه يجعل القارئ يعرف فوراً من أين جاءت كل دالة.
استيراد أسماء محددة — from ... import
أحياناً تريد دالة واحدة فقط ولا تريد كتابة اسم الوحدة في كل مرة:
from math import sqrt, pi
# يمكن الآن استخدامها مباشرة — use directly without prefix
print(sqrt(25)) # 5.0
print(pi) # 3.141592653589793
هذا مناسب عندما تستخدم الدالة كثيراً وأنت متأكد أن اسمها لن يتعارض مع شيء آخر في ملفك. لكن انتبه: إذا عرّفت دالة بالاسم نفسه لاحقاً في ملفك، ستُلغي المستورَدة دون تحذير.
الاستيراد باسم مستعار — import ... as
عندما يكون اسم الوحدة طويلاً أو تريد اختصاراً متعارفاً عليه:
import math as m
import collections as col
print(m.sqrt(9)) # 3.0
print(m.factorial(5)) # 120
# في عالم البيانات هذه اتفاقيات يراها الجميع — industry conventions
# import numpy as np
# import pandas as pd
الاسم المستعار np لـ NumPy وpd لـ Pandas هما من أكثر الاتفاقيات انتشاراً في Python. عندما تقرأ كوداً لعلم البيانات وترى np.array(...) تعرف فوراً أنها NumPy.
from module import * — ولماذا لا تفعلها
يمكنك استيراد كل شيء من وحدة بنجمة واحدة:
from math import * # لا تفعل هذا — don't do this
هذا يجلب كل الأسماء من math إلى مساحة أسمائك المحلية — sqrt، pi، log، floor، كلها. المشكلة مزدوجة: أولاً، لا تعرف أين جاء كل اسم، مما يجعل الكود صعب القراءة والتصحيح. ثانياً، إذا استوردت مكتبتين بالنجمة وكلاهما يعرف دالة بالاسم نفسه، الثانية تُخفي الأولى بصمت تام.
استثناء مقبول: المكتبات التي صُمّمت صراحةً لهذا الغرض مثل tkinter.ttk وبعض أدوات الاختبار — لكن في الكود الإنتاجي العام، تجنّب النجمة.
نمط __name__ == "__main__"
هذا من أهم أنماط Python وأكثرها سوء فهم بين المبتدئين. لفهمه، عليك أن تعرف: عندما Python تُشغّل ملفاً مباشرة، تضع في المتغير الخاص __name__ القيمة "__main__". لكن عندما يُستورَد الملف من مكان آخر، تكون قيمة __name__ هي اسم الوحدة نفسه.
# في ملف my_module.py
def greet(name):
return f"أهلاً {name}!"
def compute_area(r):
import math
return math.pi * r * r
# هذا الكود يعمل فقط عند تشغيل الملف مباشرة — runs only when executed directly
if __name__ == "__main__":
print(greet("أحمد"))
print(f"مساحة الدائرة: {compute_area(5):.2f}")
هذا النمط يحل مشكلة عملية: تريد أن تكتب دوال قابلة للاستيراد، وفي نفس الوقت تجرّبها مباشرة عند تطوير الملف. بدون هذا النمط، كل مرة يستورد شخص وحدتك، ينفّذ Python كود التجربة كذلك — وهذا سلوك غير مرغوب.
القاعدة العملية: أي كود لا تريده أن يعمل عند الاستيراد، ضعه داخل if __name__ == "__main__":.
مسار البحث — sys.path
عندما تكتب import math، كيف تعرف Python أين تجد الملف؟ تبحث في قائمة من المسارات مُخزّنة في sys.path. هذه القائمة تشمل:
- المجلد الحالي لملفك
- مجلدات المكتبة القياسية (stdlib)
- مجلدات المكتبات المثبّتة (site-packages)
في الغالب لا تحتاج لتعديل sys.path يدوياً — يتولى ذلك pip عند تثبيت الحزم. لكن معرفتك بوجوده تساعدك على فهم رسائل الخطأ من نوع ModuleNotFoundError: إما الوحدة غير مثبّتة، أو ملفها ليس في أي من المسارات التي يبحث فيها Python.
المكتبة القياسية — Standard Library
Python تأتي مع مكتبة قياسية (stdlib) ضخمة جداً — أكثر من 200 وحدة جاهزة دون تثبيت أي شيء. من أبرزها:
| الوحدة | الاستخدام |
|---|---|
math | عمليات حسابية، مثلثات، لوغاريتمات |
random | أرقام عشوائية، اختيار عناصر |
datetime | تواريخ وأوقات |
os | العمليات مع نظام التشغيل والملفات |
sys | الوصول لمعلومات Python والنظام |
collections | هياكل بيانات متقدمة: Counter, deque, defaultdict |
json | قراءة وكتابة JSON |
re | التعابير النمطية (Regular Expressions) |
pathlib | التعامل مع مسارات الملفات |
itertools | أدوات للتكرار والتوليف |
هذه المكتبة هي ما يعنيه الناس حين يقولون Python “batteries-included” — البطاريات مدمجة. قبل أن تبحث عن مكتبة خارجية، تحقق أن المكتبة القياسية لا تحل المشكلة بالفعل.
استيراد المكتبة القياسية في العمل
دعنا نجرب بعض الوحدات الشائعة ونرى كيف تتشكل الوحدات في تطبيق حقيقي:
ملاحظة عن هيكل الوحدات المتعددة
في مشروع حقيقي، لو كانت لديك وحدة math_tools.py خاصة بك، هكذا يبدو الهيكل:
my_project/
├── main.py # البرنامج الرئيسي — main program
├── math_tools.py # وحدة الأدوات الحسابية — your module
└── formatter.py # وحدة التنسيق — formatting module
ثم في main.py:
import math_tools # يستورد ملف math_tools.py
from formatter import show # يستورد دالة show من formatter.py
هذا ما سيصبح أوضح حين نتحدث عن الحزم في الدرس القادم — عندما تنمو المشاريع ويصبح المجلد الواحد هو وحدة التنظيم.
تدريب
جرّب تطبيق math للتحقق من بعض الخصائص الرياضية، ثم أجب على التحدي أدناه: