المكتبة

الفصل الثالث عشر: تنظيم المشاريع – الوحدات (Modules) والحزم (Packages)

الفصل الثالث عشر: تنظيم المشاريع – الوحدات (Modules) والحزم (Packages)

مقدمة الفصل

عندما تعمل على مشاريع صغيرة، قد يكون من المقبول كتابة كل الكود في ملف واحد. لكن مع نمو المشروع وزيادة تعقيده، يصبح هذا الأسلوب كابوسًا. فكر في الأمر ككتابة رواية كاملة في فقرة واحدة عملاقة؛ سيصعب عليك العثور على أجزاء معينة، وستزداد احتمالية حدوث أخطاء، وستكون عملية التعديل شبه مستحيلة.

لهذا السبب، يستخدم المبرمجون المحترفون تقنيات لتقسيم مشاريعهم إلى أجزاء أصغر وأكثر قابلية للإدارة، تمامًا مثل تقسيم الرواية إلى فصول وفقرات. في هذا الفصل، سنتعلم أهم طريقتين لتنظيم الكود في بايثون:

  1. الوحدات (Modules): لتقسيم الكود إلى “فصول” أو ملفات منفصلة.
  2. الحزم (Packages): لتجميع “الفصول” المترابطة معًا في “أقسام” أو مجلدات.

1. ما هي الوحدة (Module)؟

في أبسط صورها، الوحدة هي أي ملف بايثون بامتداد .py. عندما تقوم بإنشاء دالة أو كلاس في ملف، يمكنك “استيراد” هذه الوحدة في ملف آخر واستخدام ما بداخلها. هذا يسمح لك بتقسيم منطق برنامجك إلى ملفات متعددة، كل ملف مسؤول عن مهمة محددة.

💡 كيف يجد بايثون وحداتك؟

عندما تكتب import my_module، يبحث بايثون عن ملف my_module.py في عدة أماكن بالترتيب:

  1. المجلد الحالي الذي تعمل فيه. (لهذا السبب يجب أن تكون ملفاتنا في نفس المجلد في المثال الأول).
  2. مجلدات المكتبة القياسية لبايثون.
  3. المجلدات التي تحتوي على المكتبات الخارجية التي قمت بتثبيتها.

كيفية إنشاء واستخدام وحدة

الخطوة الأولى: إنشاء الوحدة لنفترض أننا نعمل على برنامج يتطلب عمليات حسابية. بدلاً من كتابة هذه الدوال في ملفنا الرئيسي، سننشئ وحدة خاصة بها.

أنشئ ملفًا جديدًا باسم math_operations.py واكتب بداخله الكود التالي:

# هذا هو ملف الوحدة: math_operations.py

def add(x, y):
    """This function adds two numbers."""
    return x + y

def subtract(x, y):
    """This function subtracts two numbers."""
    return x - y

الخطوة الثانية: استيراد الوحدة في ملف آخر الآن، أنشئ ملفًا آخر في نفس المجلد باسم main.py. هذا سيكون برنامجنا الرئيسي. لاستخدام الدوال التي أنشأناها، سنقوم باستيراد الوحدة math_operations.

# هذا هو برنامجنا الرئيسي: main.py

# نستورد الوحدة بأكملها
import math_operations

# الآن يمكننا استخدام الدوال من الوحدة
# يجب أن نسبقها باسم الوحدة ونقطة
result1 = math_operations.add(10, 5)
result2 = math_operations.subtract(10, 5)

print(f"نتيجة الجمع: {result1}")
print(f"نتيجة الطرح: {result2}")

جعل وحداتك قابلة لإعادة الاستخدام والاختبار

ماذا لو أردت إضافة أكواد لاختبار دوالك داخل ملف الوحدة نفسه دون أن يؤثر ذلك على البرامج الأخرى التي تستورده؟

المشكلة: إذا أضفنا print(add(2, 3)) في نهاية ملف math_operations.py، فسيتم تنفيذ هذا السطر في كل مرة يتم فيها استيراد الوحدة في main.py، وهذا سلوك غير مرغوب فيه.

الحل هو استخدام “حارس التنفيذ” if __name__ == "__main__":

بايثون تعطي متغيرًا خاصًا اسمه __name__ لكل ملف.

  • إذا قمت بتشغيل الملف مباشرة، فإن قيمة __name__ تكون "__main__".
  • إذا قمت باستيراد الملف كوحدة، فإن قيمة __name__ تكون اسم الملف نفسه (e.g., "math_operations").
# ملف: math_operations.py (النسخة الاحترافية)
def add(x, y):
    return x + y

def subtract(x, y):
    return x - y

# هذا الكود لن يعمل إلا عند تشغيل هذا الملف مباشرة
if __name__ == "__main__":
    print("يتم الآن اختبار وحدة العمليات الحسابية...")
    result = add(5, 5)
    print(f"نتيجة الاختبار: {result == 10}")

الآن، وحدتك نظيفة وقابلة للاستخدام في أي مكان، ويمكنك اختبارها بشكل منفصل. هذه ممارسة أساسية ومهمة جدًا في بايثون.

طرق أخرى للاستيراد

استيراد دالة محددة باستخدام from:

# استيراد دالة 'add' فقط من الوحدة
from math_operations import add

# الآن يمكننا استخدام الدالة مباشرة بدون اسم الوحدة
result = add(20, 7)
print(f"النتيجة: {result}")

إعطاء اسم مستعار للوحدة باستخدام as:

# استيراد الوحدة مع اسم مستعار أقصر 'mo'
import math_operations as mo

result = mo.add(100, 50)
print(f"النتيجة: {result}")

2. ما هي الحزمة (Package)؟

عندما يصبح مشروعك كبيرًا جدًا ويحتوي على عشرات الوحدات، فإن وضعها كلها في مجلد واحد يصبح فوضويًا. الحزمة هي طريقة لتنظيم الوحدات المترابطة معًا داخل مجلد.

ببساطة، الحزمة هي أي مجلد يحتوي على ملف خاص باسم __init__.py.

وجود هذا الملف (حتى لو كان فارغًا) يخبر بايثون بأن هذا المجلد ليس مجرد مجلد عادي، بل هو حزمة يمكن استيراد الوحدات منها.

💡 هل تعلم؟ (القوة الخفية لملف __init__.py)

بالإضافة إلى تعريف المجلد كحزمة، يمكن استخدام ملف __init__.py لأمور متقدمة، مثل تنفيذ كود تهيئة عند استيراد الحزمة لأول مرة، أو تسهيل عملية الاستيراد عن طريق جعل دوال من وحدات داخلية متاحة على مستوى الحزمة مباشرة.

هيكل مشروع يستخدم الحزم

لنتخيل مشروع آلة حاسبة يشمل عمليات بسيطة وأخرى معقدة. يمكننا تنظيمه هكذا:

calculator_project/
│
├── main.py
│
└── operations/
    ├── __init__.py      # يجعل 'operations' حزمة
    ├── simple.py        # يحتوي على add, subtract
    └── complex.py       # يحتوي على power, square_root

ملف main.py الرئيسي: الآن، من ملف main.py، يمكننا استيراد الدوال التي نحتاجها من داخل الحزمة operations.

# استيراد دوال محددة من وحدات داخل حزمة 'operations'
from operations.simple import add
from operations.complex import square_root

# استخدام الدوال المستوردة
sum_result = add(15, 10)
sqrt_result = square_root(64)

print(f"ناتج الجمع هو: {sum_result}")
print(f"الجذر التربيعي للرقم 64 هو: {sqrt_result}")

⚠️ تحذير من الاستيرادات الدائرية (Circular Imports)

كن حذرًا من إنشاء موقف حيث يقوم ملف module_a.py باستيراد module_b.py، وفي نفس الوقت يقوم module_b.py باستيراد module_a.py. هذا يسمى “استيراد دائري” وسيؤدي إلى توقف برنامجك مع خطأ.


3. تمرين تطبيقي: تنظيم تطبيق لتحويل الوحدات

المطلوب:

لديك برنامج يقوم بتحويلات بين وحدات مختلفة. حاليًا، كل الكود موجود في ملف واحد. مهمتك هي إعادة تنظيمه باستخدام حزمة ووحدات منفصلة.

إرشادات الحل:

  1. أنشئ هيكل المشروع: قم بإنشاء مجلد رئيسي للمشروع (e.g., converter_app). بداخله، أنشئ ملف main.py ومجلدًا جديدًا (حزمة) باسم converters.
  2. أنشئ ملف __init__.py: داخل المجلد converters، أنشئ ملفًا فارغًا باسم __init__.py.
  3. أنشئ الوحدات: داخل الحزمة converters، أنشئ وحدتين:
    • temperature.py: ضع فيه دالة celsius_to_fahrenheit. المعادلة: (C * 9/5) + 32.
    • distance.py: ضع فيه دالة km_to_miles. المعادلة: KM * 0.621371.
  4. اكتب الكود الرئيسي: في main.py، قم باستيراد واستدعاء الدوال التي أنشأتها.

الحل المقترح للتمرين

الخطوة 1: هيكل الملفات والمجلدات

converter_app/
│
├── main.py
│
└── converters/
    ├── __init__.py
    ├── distance.py
    └── temperature.py

الخطوة 2: كتابة كود الوحدات

ملف converters/temperature.py:

def celsius_to_fahrenheit(celsius):
    """يحول درجة الحرارة من مئوية إلى فهرنهايت."""
    fahrenheit = (celsius * 9/5) + 32
    return fahrenheit

# حارس التنفيذ لاختبار الوحدة بشكل منفصل
if __name__ == '__main__':
    temp_c = 100
    print(f"{temp_c} درجة مئوية تساوي {celsius_to_fahrenheit(temp_c)} فهرنهايت.")

ملف converters/distance.py:

def km_to_miles(kilometers):
    """يحول المسافة من كيلومتر إلى ميل."""
    miles = kilometers * 0.621371
    return miles

# حارس التنفيذ لاختبار الوحدة بشكل منفصل
if __name__ == '__main__':
    dist_km = 10
    print(f"{dist_km} كيلومتر تساوي {km_to_miles(dist_km):.2f} ميل.")

الخطوة 3: كتابة الكود الرئيسي

ملف main.py:

# استيراد الدوال التي نحتاجها من حزمة 'converters'
from converters.temperature import celsius_to_fahrenheit
from converters.distance import km_to_miles

print("--- تطبيق تحويل الوحدات ---")

# --- تحويل درجة الحرارة ---
celsius_value = 25
fahrenheit_value = celsius_to_fahrenheit(celsius_value)
print(f"{celsius_value} درجة مئوية تساوي {fahrenheit_value:.2f} درجة فهرنهايت.")

# --- تحويل المسافة ---
km_value = 100
miles_value = km_to_miles(km_value)
print(f"{km_value} كيلومتر تساوي {miles_value:.2f} ميل.")

شرح الحل: الآن أصبح ملفنا الرئيسي main.py نظيفًا جدًا ومركزًا على منطق التطبيق. كل ما يفعله هو استدعاء الأدوات التي يحتاجها من الوحدات المتخصصة. إذا أردنا تعديل معادلة تحويل الحرارة، سنذهب مباشرة إلى ملف temperature.py دون لمس أي جزء آخر من البرنامج. هذا هو جوهر التنظيم والقابلية للصيانة.


4. خلاصة الفصل

  • الوحدات (Modules) هي ملفات بايثون (.py) تسمح لك بتقسيم الكود إلى أجزاء منطقية.
  • الحزم (Packages) هي مجلدات تحتوي على وحدات مترابطة، ويجب أن تحتوي على ملف __init__.py (حتى لو كان فارغًا).
  • if __name__ == "__main__" هو حارس أساسي يمنع تنفيذ كود الاختبار عند استيراد الوحدة، مما يجعلها قابلة لإعادة الاستخدام بأمان.
  • استخدام الوحدات والحزم هو الممارسة الاحترافية لتنظيم المشاريع الكبيرة، مما يجعلها أسهل في الصيانة، الاختبار، والتطوير.

تمارين تطبيقية مع شرح مفصل

التمرين 1: وحدة الترحيب الأساسية

المطلوب:

  1. أنشئ ملفًا باسم greeter.py يحتوي على دالة واحدة say_hello(name) تقوم بطباعة رسالة ترحيب (مثلاً: مرحباً، [name]!).
  2. أنشئ ملفًا آخر باسم app.py في نفس المجلد.
  3. في app.py، قم باستيراد الوحدة greeter واستدعِ الدالة say_hello مع اسم من اختيارك.

الحل:

ملف greeter.py:

def say_hello(name):
    """تطبع رسالة ترحيب مخصصة."""
    print(f"مرحباً، {name}!")

ملف app.py:

# استيراد الوحدة بأكملها
import greeter

print("--- بدء البرنامج الرئيسي ---")
# استدعاء الدالة من الوحدة
greeter.say_hello("علي")

شرح الحل:

  • greeter.py: هذا الملف يعمل الآن كـ “وحدة” أو مكتبة صغيرة. يحتوي على أداة واحدة (دالة say_hello) يمكننا إعادة استخدامها.
  • app.py: هذا هو برنامجنا الرئيسي.
  • import greeter: هذا السطر يخبر برنامج app.py بالبحث عن ملف اسمه greeter.py في نفس المجلد وجعل كل الكود الذي بداخله متاحًا للاستخدام.
  • greeter.say_hello("علي"): للوصول إلى الدالة داخل الوحدة المستوردة، يجب أن نكتب اسم الوحدة (greeter)، متبوعًا بنقطة، ثم اسم الدالة (say_hello). هذا الأسلوب يمنع تضارب الأسماء ويجعل الكود واضحًا، حيث نعرف بالضبط من أين تأتي كل دالة.

التمرين 2: استيراد دالة محددة

المطلوب:

  1. أنشئ ملفًا باسم text_tools.py يحتوي على دالتين: uppercase_text(text) و count_words(text).
  2. في ملف main.py، قم باستيراد دالة uppercase_text فقط باستخدام from...import.
  3. استدعِ الدالة uppercase_text مباشرة واطبع نتيجتها.

الحل:

ملف text_tools.py:

def uppercase_text(text):
    """تعيد النص بأحرف كبيرة."""
    return text.upper()

def count_words(text):
    """تعيد عدد الكلمات في النص."""
    return len(text.split())

ملف main.py:

# استيراد دالة واحدة محددة فقط
from text_tools import uppercase_text

message = "هذه جملة للتجربة."
# استدعاء الدالة مباشرة بدون اسم الوحدة
upper_message = uppercase_text(message)

print(f"النص الأصلي: {message}")
print(f"النص بعد التعديل: {upper_message}")

شرح الحل:

  • from text_tools import uppercase_text: هذه طريقة مختلفة للاستيراد. بدلاً من استيراد الوحدة بأكملها، نحن نخبر بايثون: “اذهب إلى وحدة text_tools وأحضر منها دالة uppercase_text فقط وضعها في النطاق الحالي لبرنامجنا”.
  • uppercase_text(message): لأننا استوردنا الدالة مباشرة، يمكننا الآن استدعاؤها باسمها مباشرة دون الحاجة إلى كتابة text_tools. قبلها. هذا يجعل الكود أقصر، وهو مفيد عندما تحتاج إلى استخدام دالة معينة بشكل متكرر.

التمرين 3: استخدام الاسم المستعار (Alias)

المطلوب: لديك وحدة باسم complex_math_operations.py. هذا الاسم طويل جدًا.

  1. أنشئ هذه الوحدة وضع بداخلها دالة power(base, exponent).
  2. في ملف main.py، قم باستيراد الوحدة complex_math_operations باستخدام الاسم المستعار cmo.
  3. استخدم الاسم المستعار لاستدعاء الدالة power.

الحل:

ملف complex_math_operations.py:

def power(base, exponent):
    """تحسب قوة عدد (الأس)."""
    return base ** exponent

ملف main.py:

# استيراد الوحدة مع اسم مستعار قصير
import complex_math_operations as cmo

# استخدام الاسم المستعار لاستدعاء الدالة
result = cmo.power(2, 5) # 2 أس 5

print(f"نتيجة 2 أس 5 هي: {result}")

شرح الحل:

  • import complex_math_operations as cmo: هذا السطر يستورد الوحدة بأكملها، ولكنه يعطيها “اسمًا مستعارًا” أو “لقبًا” قصيرًا هو cmo.
  • cmo.power(2, 5): الآن، بدلاً من كتابة الاسم الطويل في كل مرة، نستخدم الاسم المستعار cmo للوصول إلى الدوال الموجودة داخل الوحدة. هذه الممارسة شائعة جدًا لجعل الكود أنظف وأسهل في الكتابة.

التمرين 4: حارس التنفيذ __main__

المطلوب:

  1. أنشئ وحدة validator.py تحتوي على دالة is_positive(number).
  2. في نفس الملف، أضف كود اختبار يتأكد من أن الدالة تعمل.
  3. استخدم حارس التنفيذ if __name__ == "__main__" للتأكد من أن كود الاختبار يعمل فقط عند تشغيل ملف validator.py مباشرة، وليس عند استيراده.

الحل:

ملف validator.py:

def is_positive(number):
    """تتحقق مما إذا كان الرقم موجبًا."""
    return number > 0

# هذا الكود موجود داخل حارس التنفيذ
if __name__ == "__main__":
    print("--- بدء اختبار وحدة Validator ---")
    print(f"هل الرقم 5 موجب؟ {is_positive(5)}")
    print(f"هل الرقم -3 موجب؟ {is_positive(-3)}")
    print("--- انتهاء الاختبار ---")

ملف main.py (لإثبات أن الاختبار لا يعمل عند الاستيراد):

from validator import is_positive

print("--- بدء البرنامج الرئيسي ---")
# كود الاختبار من validator.py لن يتم طباعته هنا
print(f"التحقق من الرقم 10 في البرنامج الرئيسي: {is_positive(10)}")

شرح الحل:

  • المتغير الخاص __name__ له قيمة تعتمد على كيفية استخدام الملف.
  • عند تشغيل python validator.py مباشرة، تكون قيمة __name__ هي __main__، لذلك يتم تنفيذ كتلة if ويظهر ناتج الاختبار.
  • عند تشغيل main.py الذي يستورد validator، تكون قيمة __name__ داخل ملف validator.py هي "validator" (اسم الوحدة). بما أن "validator" لا تساوي "__main__"، يتم تجاهل كتلة if بالكامل. هذا هو الأسلوب الاحترافي لفصل منطق الوحدة عن كود اختبارها.

التمرين 5: إنشاء حزمة بسيطة

المطلوب: أنشئ حزمة بسيطة لإدارة المستخدمين بالهيكل التالي:

user_manager/
│
├── main.py
│
└── auth/
    ├── __init__.py
    └── login.py
  1. في login.py، أنشئ دالة validate_password(password) تتحقق إذا كان طول كلمة المرور 8 أحرف على الأقل.
  2. في main.py، استورد الدالة واستخدمها.

الحل:

ملف auth/login.py:

def validate_password(password):
    """تتحقق إذا كان طول كلمة المرور 8 أحرف على الأقل."""
    return len(password) >= 8

ملف auth/__init__.py:

# هذا الملف يمكن أن يبقى فارغًا

ملف main.py:

from auth.login import validate_password

password_to_check = "12345678"
is_valid = validate_password(password_to_check)

if is_valid:
    print(f"كلمة المرور '{password_to_check}' صالحة.")
else:
    print(f"كلمة المرور '{password_to_check}' غير صالحة.")

شرح الحل:

  • auth/: هذا مجلد عادي.
  • __init__.py: وجود هذا الملف الفارغ يخبر بايثون بأن مجلد auth ليس مجرد مجلد، بل هو حزمة (package) يمكننا استيراد وحدات منها.
  • from auth.login import ...: صيغة الاستيراد تستخدم النقطة (.) لتمثيل التسلسل الهرمي للمجلدات. هذه الجملة تعني: “اذهب إلى حزمة auth، ثم ابحث عن وحدة login بداخلها، ومنها قم باستيراد دالة validate_password”.

التمرين 6: توسيع الحزمة

المطلوب: بناءً على التمرين السابق، أضف وحدة جديدة باسم register.py داخل حزمة auth.

  1. في register.py، أنشئ دالة validate_email(email) تتحقق إذا كان البريد الإلكتروني يحتوي على الرمز @.
  2. في main.py، استورد واستخدم الدالة الجديدة validate_email بالإضافة إلى validate_password.

الحل:

ملف auth/register.py:

def validate_email(email):
    """تتحقق إذا كان البريد الإلكتروني يحتوي على '@'."""
    return '@' in email

ملف main.py (النسخة المحدثة):

from auth.login import validate_password
from auth.register import validate_email

# التحقق من كلمة المرور
password_to_check = "123"
print(f"كلمة المرور صالحة: {validate_password(password_to_check)}")

# التحقق من البريد الإلكتروني
email_to_check = "[email protected]"
print(f"البريد الإلكتروني صالح: {validate_email(email_to_check)}")

شرح الحل: هذا التمرين يوضح مدى سهولة توسيع المشاريع المنظمة. كل ما فعلناه هو إضافة ملف وحدة جديد (register.py) داخل نفس الحزمة (auth). بقي منطق كل وحدة منفصلاً ونظيفًا. في main.py، قمنا ببساطة بإضافة سطر استيراد جديد من الوحدة الجديدة للوصول إلى وظائفها.


التمرين 7: وحدة البيانات (Config)

المطلوب: أنشئ وحدة settings.py لا تحتوي على دوال، بل على متغيرات فقط تمثل إعدادات التطبيق.

  1. في settings.py، عرّف متغيرين: APP_NAME = "تطبيقي الرائع" و VERSION = "1.0".
  2. في main.py، استورد الوحدة settings واطبع رسالة ترحيب تستخدم هذه المتغيرات.

الحل:

ملف settings.py:

# هذا الملف يحتوي على بيانات الإعدادات فقط
APP_NAME = "تطبيقي الرائع"
VERSION = "1.0"
DEBUG_MODE = False

ملف main.py:

import settings

print(f"مرحبًا بك في {settings.APP_NAME} - الإصدار {settings.VERSION}")
if settings.DEBUG_MODE:
    print("وضع التصحيح مفعل.")

شرح الحل: الوحدات ليست فقط للدوال والكلاسات. يمكن استخدامها بشكل فعال جدًا لتخزين الإعدادات أو البيانات الثابتة. عندما نكتب import settings، يقوم بايثون بتشغيل ملف settings.py، مما يؤدي إلى إنشاء المتغيرات APP_NAME و VERSION و DEBUG_MODE داخل نطاق تلك الوحدة. بعد ذلك، يمكننا الوصول إليها من main.py باستخدام settings.APP_NAME وهكذا. هذا يجعل تغيير الإعدادات مركزيًا وسهلاً.


التمرين 8: إعادة هيكلة الكود (Refactoring)

المطلوب: ابدأ بملف واحد app.py يحتوي على دالتين. مهمتك هي فصلهما إلى وحدتين منفصلتين. الكود الأولي في app.py:

# app.py (قبل إعادة الهيكلة)
def format_user_data(name, age): return f"User: {name}, Age: {age}"
def save_to_file(text, filename):
    with open(filename, 'w', encoding='utf-8') as f: f.write(text)
user_info = format_user_data("Nasser", 30)
save_to_file(user_info, "user.txt")
print("تم حفظ البيانات.")

المهمة: أنشئ وحدتين formatting.py و file_handler.py، وضع كل دالة في مكانها المناسب، ثم اجعل app.py يستوردهما.

الحل:

ملف formatting.py:

def format_user_data(name, age):
    """تنسق بيانات المستخدم في نص واحد."""
    return f"User: {name}, Age: {age}"

ملف file_handler.py:

def save_to_file(text, filename):
    """تحفظ النص في ملف."""
    with open(filename, 'w', encoding='utf-8') as f:
        f.write(text)

ملف app.py (بعد إعادة الهيكلة):

from formatting import format_user_data
from file_handler import save_to_file

# منطق البرنامج الرئيسي أصبح الآن واضحًا جدًا
user_info = format_user_data("Nasser", 30)
save_to_file(user_info, "user.txt")

print("تم حفظ البيانات بنجاح بعد إعادة الهيكلة.")

شرح الحل: هذا التمرين يوضح مبدأ فصل الاهتمامات (Separation of Concerns). الكود الأصلي كان يخلط بين منطق تنسيق البيانات ومنطق التعامل مع الملفات. بفصلهما، أصبح لدينا:

  • formatting.py: وحدة مسؤولة فقط عن كل ما يتعلق بالتنسيق.
  • file_handler.py: وحدة مسؤولة فقط عن عمليات الملفات.
  • app.py: أصبح الآن مثل “المدير” الذي ينسق العمل. هو لا يعرف كيف تتم عملية التنسيق أو الحفظ، بل يطلب فقط من الوحدات المتخصصة القيام بذلك. هذا يجعل الكود أسهل بكثير في الصيانة والتطوير.

التمرين 9: حزمة للعمليات الحسابية

المطلوب: أنشئ حزمة calculator تحتوي على وحدتين: basic.py (للجمع والطرح) و advanced.py (للأس والجذر التربيعي). استخدم هذه الحزمة في main.py لإجراء عملية من كل وحدة.

الحل:

ملف calculator/basic.py:

def add(x, y): return x + y
def subtract(x, y): return x - y

ملف calculator/advanced.py:

import math
def power(x, y): return x ** y
def sqrt(x): return math.sqrt(x)

ملف calculator/__init__.py: (فارغ)

ملف main.py:

from calculator.basic import add
from calculator.advanced import sqrt

sum_result = add(10, 5)
sqrt_result = sqrt(144)

print(f"نتيجة الجمع: {sum_result}")
print(f"نتيجة الجذر التربيعي: {sqrt_result}")

شرح الحل: هذا التمرين يجمع بين كل المفاهيم. أنشأنا حزمة (calculator) تحتوي على وحدتين (basic, advanced). ملف main.py، الموجود في المستوى الأعلى، يمكنه الآن استيراد أي دالة يحتاجها من أي وحدة داخل الحزمة باستخدام مسارها الكامل (package.module). هذا هو الأسلوب المتبع في بناء المكتبات والتطبيقات الكبيرة.


التمرين 10: مشروع تقرير بسيط

المطلوب: أنشئ تطبيقًا صغيرًا يقوم بتوليد تقرير بسيط، منظمًا في حزمة. الهيكل:

reporter_app/
│
├── main.py
│
└── report_lib/
    ├── __init__.py
    ├── data.py
    └── generator.py
  1. data.py: أنشئ دالة get_sales_data() تعيد قائمة من القواميس (بيانات مبيعات وهمية).
  2. generator.py: أنشئ دالة generate_report(sales_data) تأخذ البيانات، تنسقها في نص جميل، وتعيده.
  3. main.py: استورد الدالتين، استدع get_sales_data، مرر نتيجتها إلى generate_report، ثم اطبع التقرير النهائي.

الحل:

ملف report_lib/data.py:

def get_sales_data():
    """تعيد بيانات مبيعات وهمية."""
    return [
        {"product": "لابتوب", "amount": 4500},
        {"product": "شاشة", "amount": 1200},
        {"product": "طابعة", "amount": 750},
    ]

ملف report_lib/generator.py:

def generate_report(sales_data):
    """تأخذ بيانات المبيعات وتنشئ تقريرًا نصيًا."""
    report_lines = ["--- تقرير المبيعات الشهري ---"]
    total = 0
    for sale in sales_data:
        report_lines.append(f"المنتج: {sale['product']}, المبلغ: {sale['amount']} درهم")
        total += sale['amount']
    report_lines.append("---------------------------")
    report_lines.append(f"الإجمالي: {total} درهم")
    return "\n".join(report_lines)

ملف main.py:

from report_lib.data import get_sales_data
from report_lib.generator import generate_report

# 1. جلب البيانات من وحدة البيانات
sales = get_sales_data()

# 2. توليد التقرير باستخدام وحدة التوليد
report_text = generate_report(sales)

# 3. طباعة التقرير النهائي
print(report_text)

شرح الحل: هذا التمرين يوضح تدفق البيانات والمنطق في تطبيق منظم بشكل جيد:

  1. main.py هو “العقل المدبر” أو نقطة البداية.
  2. عندما يحتاج إلى بيانات، يطلبها من الوحدة المتخصصة data.py.
  3. عندما يريد تنسيق هذه البيانات، يرسلها إلى الوحدة المتخصصة generator.py.
  4. وأخيرًا، يقوم main.py بالمهمة الأخيرة وهي عرض النتيجة النهائية. كل جزء من البرنامج له مسؤولية واحدة ومحددة، مما يجعل الكود نظيفًا وسهل الفهم والتطوير.

ماذا بعد؟

لقد وصلت الآن إلى نهاية رحلتنا في أساسيات بايثون. لقد بنيت أساسًا متينًا جدًا. الخطوات التالية في عالم البرمجة لا حصر لها: يمكنك الآن البدء في تعلم أطر عمل تطوير الويب مثل Django أو Flask، أو مكتبات علم البيانات مثل Pandas و NumPy، أو بناء تطبيقات سطح المكتب. الأساس الذي لديك الآن سيؤهلك لكل ذلك.

×

إعدادات القراءة

الوضع الليلي
حجم الخط 20px
نوع الخط
×

فهرس الكتاب