الدوال والوحدات
درس 3 من 8
3الدوال والوحدات (Functions & Modularity)
الصورة الكبيرةكيف تغلّف منطقًا متكررًا في دالة تُرجع قيمة بدل أن تطبعها، وكيف تقسّم كودك على ملفات تستوردها، وما فائدة حارس __main__.
ماذا تشاهد في الرسم: نداء area(3, 4) يفتح إطارًا فيه w وh، تُحسب القيمة وتعود بـreturn، ثم يختفي الإطار وتبقى النتيجة في result.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
الدالة مثل وصفة مكتوبة على بطاقة: تعطيها المقادير فتعطيك الطبق، وتستعملها كلما احتجت دون أن تعيد كتابتها. والوحدة دفتر وصفات كامل في ملف، تستعير منه ما تريد. الوصفة الجيدة تعيد لك الطبق، لا تأكله أمامك (أي ترجع النتيجة لا تطبعها).
def يعرّف دالة بمعاملات وقيم افتراضية، وreturn يعيد قيمة قابلة للاستخدام. كل ملف .py وحدة تُستورد بـimport أو from x import y. وحارس if __name__ == "__main__": يمنع تنفيذ كود التجربة حين يُستورد الملف من ملف آخر.
قبل أن تبدأ
🔁درس التحكم بالتدفق
تكتب if وfor وتفهم المسافة البادئة.
🧮الدالة في الرياضيات
شيء يأخذ مُدخلًا ويعطي ناتجًا، مثل ضعف الرقم.
الفكرة الجوهرية
عندما يتكرّر نفس العمل في مكانين، تضعه في دالة وتناديها باسمها. وعندما يكتب غيرك دالة مفيدة، تستوردها. هذا كل ما في هذا الجزء.
1عرّف دالة ونادِها
def تعرّف دالة باسم ومُدخل بين قوسين. return تعيد النتيجة لمن نادى. تعريف الدالة لا يشغّلها؛ النداء بالاسم هو الذي يشغّلها.
def greet(name):
return f"أهلًا {name}"
message = greet("خالد")
print(message)
print(greet("نورة"))
ناديناها مرتين بمُدخلين مختلفين فأعطت نتيجتين. والنتيجة تُحفظ في متغيّر أو تُطبع مباشرة.
2قيمة افتراضية
إن أعطيت المُدخل قيمة في التعريف، صار اختياريًا. من ينادي الدالة يستطيع أن يذكره أو يتركه.
def area(width, height=1):
return width * height
print(area(3, 4))
print(area(5))
area(5) استخدمت height=1 الافتراضي فأعطت 5، وarea(3, 4) أعطت 12.
3استورد ما كتبه غيرك
import تجلب وحدة جاهزة. math وحدة من بايثون نفسها فيها الجذر والثوابت. تنادي ما فيها بالنقطة: اسم الوحدة ثم نقطة ثم اسم الدالة.
import math print(math.sqrt(16)) print(math.pi) print(round(math.pi, 2))
لم تكتب الجذر التربيعي بنفسك. أغلب برمجتك ستكون هكذا: تجد الوحدة المناسبة وتستخدمها.
return وطباعة النتيجة داخل الدالة بدلًا منه. الطباعة تعرض فقط، أما return فتعطيك قيمة تستطيع حفظها وحسابها. دالة بلا return تعيد None.كيف يعمل، خطوة بخطوة: ماذا يحدث عندما تنادي دالة
بايثون يحسب المُدخلات أولًا، مثل 3 و4.
يفتح إطارًا جديدًا ويربط كل مُدخل باسمه.
ينفّذ جسم الدالة حتى يصل إلى return أو نهايتها.
يغلق الإطار ويضع القيمة مكان النداء.
أعمق قليلًا
الدوال تُغلّف منطقًا متكررًا في وحدة قابلة لإعادة الاستخدام. الوحدات (Modules) تنظّم هذه الدوال عبر ملفات منفصلة قابلة للاستيراد.
print مقابل return
print() يعرض قيمة على الشاشة فقط ولا يمكن استخدامها لاحقًا في الكود. return يُخرج قيمة من الدالة يمكن تخزينها ومعالجتها ببرنامجك.
المعاملات والقيم الافتراضية
الدالة تستقبل مدخلات (parameters) وقد تمنحها قيمًا افتراضية تُستخدم إن لم يُمرَّر شيء.
الاستيراد بين الملفات
from utils import greet يجعل الدالة greet المعرَّفة في utils.py متاحة في ملفك الحالي. عمليًا، بايثون تفتح utils.py وتُنفّذ كل كودها العلوي من الأعلى للأسفل مرة واحدة فقط (تعريفات الدوال والمتغيرات)، ثم تُخزّن الوحدة كاملة في ذاكرة مؤقتة داخلية؛ أي استيراد لاحق لها من ملف آخر في نفس التشغيل يسترجع تلك النسخة المخزَّنة مباشرة دون إعادة التنفيذ.
حارس if __name__ == "__main__"
عندما تُشغّل الملف مباشرة، تكون قيمة __name__ هي "__main__". عند استيراده من ملف آخر، تكون قيمة __name__ اسم الملف نفسه.
الرسم 4: نفس الملف، مسارين مختلفين لتشغيله: بايثون تُنفّذ كل كود الوحدة في الحالتين، لكن قيمة __name__ تختلف حسب طريقة البدء: وهذا وحده ما يُفعّل الحارس أو يتجاهله.
## utils.py — وحدة قابلة لإعادة الاستخدام
def calculate_area(length, width=1):
"""يُرجع مساحة مستطيل؛ العرض افتراضيًا 1 (خط)."""
return length * width
def greet(name="صديقي"):
print(f"مرحبًا يا {name}!")
if __name__ == "__main__":
# هذا الجزء يُنفَّذ فقط عند تشغيل utils.py مباشرة،
# ويُتجاهَل تمامًا عند استيراده من ملف آخر
print("اختبار سريع للوحدة:", calculate_area(4, 3))
## main.py — يستورد من utils.py
from utils import calculate_area, greet
greet("معاذ")
area = calculate_area(5, 2) # نُخزّن الناتج بفضل return
print(f"المساحة: {area}")
print(x) و return x داخل دالة ثم محاولة استخدام ناتجها: result = my_func() ستكون None إذا كانت الدالة تطبع فقط ولا تُرجع شيئًا صراحة.
اختبر فهمك: لو استوردت utils.py في main.py، هل ستُطبع رسالة "اختبار سريع للوحدة"؟
لا. لأن __name__ عند الاستيراد يساوي "utils" وليس "__main__"، فشرط الحارس لا يتحقق، وذلك الكود لا يُنفَّذ، هذا بالضبط سبب وجود هذا الحارس: فصل كود "التجربة الذاتية" عن الكود المُعاد استخدامه.
مثال محلول
def with_tax(price, rate=0.15): return price * (1 + rate). الآن with_tax(100) تعطي 115.0، وتغيير النسبة صار في مكان واحد.خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| print وreturn يفعلان الشيء نفسه. | print تعرض على الشاشة فقط، وreturn تعيد قيمة تستطيع حفظها في متغيّر أو استخدامها في حساب آخر. |
| المتغيّر داخل الدالة يُرى من خارجها. | يعيش داخل إطار الدالة ويختفي معه. ما تريده خارجها أعِده بـreturn. |
| دالة طويلة تفعل كل شيء أسهل من عدة دوال. | أسهل في الكتابة الأولى فقط. الدوال الصغيرة باسم واضح تُختبر وتُصلح وتُعاد كل واحدة وحدها. |
جرّب بنفسك
دالة فيها print(x * 2) ولا تحتوي return. كتبت y = double(4). ما قيمة y؟
الدالة طبعت 8 على الشاشة، لكنها لم تُعِد شيئًا، فحصل y على None. الطباعة والإعادة شيئان مختلفان: الأولى للعين، والثانية للبرنامج.
بطاقات مراجعة سريعة
تحقق من فهمك
ماذا تعيد دالة ليس فيها return؟
تعيد None.
ما فائدة if __name__ == "__main__":؟
تجعل الكود تحتها يعمل عند تشغيل الملف مباشرة فقط، لا عند استيراده من ملف آخر.
دالة def greet(name, msg="أهلًا"): كيف تناديها باسم فقط؟
greet("سارة")؛ msg تأخذ قيمتها الافتراضية.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
توثيق رسمي2
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.