الفصل الثالث عشر: تنظيم المشاريع – الوحدات (Modules) والحزم (Packages)
مقدمة الفصل
عندما تعمل على مشاريع صغيرة، قد يكون من المقبول كتابة كل الكود في ملف واحد. لكن مع نمو المشروع وزيادة تعقيده، يصبح هذا الأسلوب كابوسًا. فكر في الأمر ككتابة رواية كاملة في فقرة واحدة عملاقة؛ سيصعب عليك العثور على أجزاء معينة، وستزداد احتمالية حدوث أخطاء، وستكون عملية التعديل شبه مستحيلة.
لهذا السبب، يستخدم المبرمجون المحترفون تقنيات لتقسيم مشاريعهم إلى أجزاء أصغر وأكثر قابلية للإدارة، تمامًا مثل تقسيم الرواية إلى فصول وفقرات. في هذا الفصل، سنتعلم أهم طريقتين لتنظيم الكود في بايثون:
- الوحدات (Modules): لتقسيم الكود إلى “فصول” أو ملفات منفصلة.
- الحزم (Packages): لتجميع “الفصول” المترابطة معًا في “أقسام” أو مجلدات.
1. ما هي الوحدة (Module)؟
في أبسط صورها، الوحدة هي أي ملف بايثون بامتداد .py
. عندما تقوم بإنشاء دالة أو كلاس في ملف، يمكنك “استيراد” هذه الوحدة في ملف آخر واستخدام ما بداخلها. هذا يسمح لك بتقسيم منطق برنامجك إلى ملفات متعددة، كل ملف مسؤول عن مهمة محددة.
💡 كيف يجد بايثون وحداتك؟
عندما تكتب
import my_module
، يبحث بايثون عن ملفmy_module.py
في عدة أماكن بالترتيب:
- المجلد الحالي الذي تعمل فيه. (لهذا السبب يجب أن تكون ملفاتنا في نفس المجلد في المثال الأول).
- مجلدات المكتبة القياسية لبايثون.
- المجلدات التي تحتوي على المكتبات الخارجية التي قمت بتثبيتها.
كيفية إنشاء واستخدام وحدة
الخطوة الأولى: إنشاء الوحدة لنفترض أننا نعمل على برنامج يتطلب عمليات حسابية. بدلاً من كتابة هذه الدوال في ملفنا الرئيسي، سننشئ وحدة خاصة بها.
أنشئ ملفًا جديدًا باسم 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. تمرين تطبيقي: تنظيم تطبيق لتحويل الوحدات
المطلوب:
لديك برنامج يقوم بتحويلات بين وحدات مختلفة. حاليًا، كل الكود موجود في ملف واحد. مهمتك هي إعادة تنظيمه باستخدام حزمة ووحدات منفصلة.
إرشادات الحل:
- أنشئ هيكل المشروع: قم بإنشاء مجلد رئيسي للمشروع (e.g.,
converter_app
). بداخله، أنشئ ملفmain.py
ومجلدًا جديدًا (حزمة) باسمconverters
. - أنشئ ملف
__init__.py
: داخل المجلدconverters
، أنشئ ملفًا فارغًا باسم__init__.py
. - أنشئ الوحدات: داخل الحزمة
converters
، أنشئ وحدتين:temperature.py
: ضع فيه دالةcelsius_to_fahrenheit
. المعادلة:(C * 9/5) + 32
.distance.py
: ضع فيه دالةkm_to_miles
. المعادلة:KM * 0.621371
.
- اكتب الكود الرئيسي: في
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: وحدة الترحيب الأساسية
المطلوب:
- أنشئ ملفًا باسم
greeter.py
يحتوي على دالة واحدةsay_hello(name)
تقوم بطباعة رسالة ترحيب (مثلاً:مرحباً، [name]!
). - أنشئ ملفًا آخر باسم
app.py
في نفس المجلد. - في
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: استيراد دالة محددة
المطلوب:
- أنشئ ملفًا باسم
text_tools.py
يحتوي على دالتين:uppercase_text(text)
وcount_words(text)
. - في ملف
main.py
، قم باستيراد دالةuppercase_text
فقط باستخدامfrom...import
. - استدعِ الدالة
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
. هذا الاسم طويل جدًا.
- أنشئ هذه الوحدة وضع بداخلها دالة
power(base, exponent)
. - في ملف
main.py
، قم باستيراد الوحدةcomplex_math_operations
باستخدام الاسم المستعارcmo
. - استخدم الاسم المستعار لاستدعاء الدالة
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__
المطلوب:
- أنشئ وحدة
validator.py
تحتوي على دالةis_positive(number)
. - في نفس الملف، أضف كود اختبار يتأكد من أن الدالة تعمل.
- استخدم حارس التنفيذ
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
- في
login.py
، أنشئ دالةvalidate_password(password)
تتحقق إذا كان طول كلمة المرور 8 أحرف على الأقل. - في
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
.
- في
register.py
، أنشئ دالةvalidate_email(email)
تتحقق إذا كان البريد الإلكتروني يحتوي على الرمز@
. - في
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
لا تحتوي على دوال، بل على متغيرات فقط تمثل إعدادات التطبيق.
- في
settings.py
، عرّف متغيرين:APP_NAME = "تطبيقي الرائع"
وVERSION = "1.0"
. - في
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
data.py
: أنشئ دالةget_sales_data()
تعيد قائمة من القواميس (بيانات مبيعات وهمية).generator.py
: أنشئ دالةgenerate_report(sales_data)
تأخذ البيانات، تنسقها في نص جميل، وتعيده.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)
شرح الحل: هذا التمرين يوضح تدفق البيانات والمنطق في تطبيق منظم بشكل جيد:
main.py
هو “العقل المدبر” أو نقطة البداية.- عندما يحتاج إلى بيانات، يطلبها من الوحدة المتخصصة
data.py
. - عندما يريد تنسيق هذه البيانات، يرسلها إلى الوحدة المتخصصة
generator.py
. - وأخيرًا، يقوم
main.py
بالمهمة الأخيرة وهي عرض النتيجة النهائية. كل جزء من البرنامج له مسؤولية واحدة ومحددة، مما يجعل الكود نظيفًا وسهل الفهم والتطوير.
ماذا بعد؟
لقد وصلت الآن إلى نهاية رحلتنا في أساسيات بايثون. لقد بنيت أساسًا متينًا جدًا. الخطوات التالية في عالم البرمجة لا حصر لها: يمكنك الآن البدء في تعلم أطر عمل تطوير الويب مثل Django أو Flask، أو مكتبات علم البيانات مثل Pandas و NumPy، أو بناء تطبيقات سطح المكتب. الأساس الذي لديك الآن سيؤهلك لكل ذلك.