تخطَّ إلى المحتوى

🐍 شرح Python

الدوال (Functions)

الدرس 9 من 29· ⏱ 4 دقائق قراءة· 🗓 آخر تحديث: ١٨ أكتوبر ٢٠٢٦

لماذا الدوال؟

تحسب السعر بعد الخصم والضريبة في خمسة أماكن من برنامجك، بنفس الأسطر منسوخة. ثم تتغيّر الضريبة: خمسة تعديلات، وستنسى واحدًا. الدالة تعطي مجموعة أسطر اسمًا، فتكتبها مرة وتستدعيها أينما احتجت. print وlen وinput كلها دوال كتبها غيرك؛ الآن تكتب دوالك.

def وreturn

def final_price(price, discount):
    after_discount = price - price * discount
    return round(after_discount * 1.16, 2)   # منزلتان عشريتان

total = final_price(100, 0.2)
print(total)                     # 92.8
print(final_price(50, 0))        # 58.0
  • def تعرّف الدالة، ثم الاسم، ثم المعاملات (Parameters) بين قوسين، ثم : وكتلة مزاحة.
  • return تُنهي الدالة فورًا وتعيد القيمة لمن استدعاها، فتحفظها أو تطبعها أو تمرّرها لدالة أخرى.
  • عند الاستدعاء، القيم 100 و0.2 تُسمّى الوسائط (Arguments)، وتُربط بالمعاملات بالترتيب.

التعريف وحده لا ينفّذ شيئًا؛ الأسطر تعمل فقط حين تستدعي الدالة. وتعريف الدالة يجب أن يسبق استدعاءها في الملف.

def add_print(a, b):
    print(a + b)

def add_return(a, b):
    return a + b

x = add_print(2, 3)       # يطبع 5
y = add_return(2, 3)      # لا يطبع شيئًا
print(x, y)               # None 5

دالة بلا return تعيد None تلقائيًّا. print تعرض القيمة على الشاشة ثم تختفي، أما return فتسلّمها للكود. الدالة التي تحسب يجب أن تعيد، ودع من يستدعيها يقرّر هل يطبع.

الخروج المبكّر

def grade(score):
    if not 0 <= score <= 100:
        return "درجة غير صالحة"
    if score >= 90:
        return "ممتاز"
    if score >= 75:
        return "جيد جدًا"
    return "جيد"

for s in (105, 95, 80, 60):
    print(s, grade(s))

بعد return لا يُنفَّذ شيء في الدالة، فلا حاجة لـ elif وelse هنا.

إعادة عدّة قيم

def stats(numbers):
    return min(numbers), max(numbers), sum(numbers) / len(numbers)

low, high, avg = stats([4, 8, 15, 16, 23, 42])
print(low, high, round(avg, 2))     # 4 42 18.0

الفاصلة تجعلها صفًّا (tuple) واحدًا، والتفكيك يوزّعه على ثلاثة متغيّرات (درس الصفوف).

القيم الافتراضية والوسائط بالاسم

def greet(name, greeting="مرحبًا", punctuation="!"):
    return f"{greeting} يا {name}{punctuation}"

print(greet("سارة"))                          # مرحبًا يا سارة!
print(greet("سارة", "أهلًا"))                 # أهلًا يا سارة!
print(greet("سارة", punctuation="."))         # بالاسم: تخطَّ ما في الوسط
print(greet(punctuation="؟", name="عمر"))     # بالاسم: الترتيب لا يهم

المعاملات ذات القيم الافتراضية تأتي بعد التي بلا قيم. والوسائط بالاسم تجعل الاستدعاء يشرح نفسه: send(user, retry=True) أوضح من send(user, True).

فخّ القيمة الافتراضية القابلة للتغيير

def add_item(item, cart=[]):        # ⚠️ قائمة واحدة لكل الاستدعاءات!
    cart.append(item)
    return cart

print(add_item("كتاب"))   # ['كتاب']
print(add_item("قلم"))    # ['كتاب', 'قلم']  ← من أين جاء الكتاب؟

def add_item_ok(item, cart=None):
    if cart is None:
        cart = []                    # قائمة جديدة في كل استدعاء
    cart.append(item)
    return cart

print(add_item_ok("كتاب"))  # ['كتاب']
print(add_item_ok("قلم"))   # ['قلم']

القيمة الافتراضية تُنشأ مرة واحدة حين تُعرَّف الدالة، لا في كل استدعاء. مع رقم أو نص لا مشكلة، أما القائمة أو القاموس فيتراكمان بين الاستدعاءات. القاعدة: الافتراضي القابل للتغيير يُكتب None، ويُنشأ داخل الدالة.

وللدوال التي تقبل عددًا متغيّرًا من الوسائط (*args و**kwargs)، والدوال المختصرة lambda، تابع في درس lambda وrange والوسائط.

النطاق (Scope)

discount = 0.1                  # عام: خارج كل الدوال

def apply(price):
    result = price * (1 - discount)   # يقرأ العام بلا مشكلة
    return result

print(apply(200))               # 180.0
# print(result)                 ← NameError: محلي داخل الدالة فقط

المتغيّرات التي تُنشأ داخل الدالة محلية: تولد مع الاستدعاء وتختفي بعده، ولا تتعارض مع متغيّرات بنفس الاسم في مكان آخر. والدالة تستطيع قراءة المتغيّرات العامة، لكن الإسناد إليها من الداخل ينشئ متغيّرًا محليًّا جديدًا. يمكن تغيير ذلك بـ global، لكن الأوضح دائمًا: مرّر القيمة كمعامل وأعد النتيجة.

التوثيق وتلميحات الأنواع

def bmi(weight_kg: float, height_m: float) -> float:
    """احسب مؤشّر كتلة الجسم: الوزن ÷ مربّع الطول."""
    return weight_kg / height_m ** 2

print(round(bmi(70, 1.75), 1))   # 22.9
print(bmi.__doc__)
  • السلسلة الوثائقية (Docstring): نص بين """ في أول سطر من الدالة. تعرضه المحرّرات حين تمرّ على اسم الدالة، وhelp(bmi) تطبعه.
  • تلميحات الأنواع (Type Hints): : float و-> float توثيق لما تتوقّعه الدالة وما تعيده. بايثون لا تفرضها وقت التشغيل، لكن المحرّرات وأدوات مثل mypy تستخدمها لاكتشاف الأخطاء قبل التشغيل.

جرّب بنفسك

🐍 محرّر بايثون
📥 المدخلات (input) — قيمة في كل سطر
وحدة التحكّم
اضغط «تشغيل» لرؤية النتيجة هنا.

أخطاء شائعة

  • نسيان الأقواس عند الاستدعاء: greet بلا () لا تنفّذ الدالة، بل تشير إليها.
  • print بدل return: النتيجة None في الكود الذي استدعاها.
  • قائمة أو قاموس كقيمة افتراضية: يتراكمان بين الاستدعاءات. استخدم None.
  • عدد وسائط خاطئ: TypeError: final_price() missing 1 required positional argument: 'discount'.
  • الاعتماد على متغيّرات عامة تتغيّر: الدالة تعطي نتائج مختلفة لنفس المدخلات. مرّر ما تحتاجه كمعاملات.
  • دالة تفعل كل شيء: إن احتجت "و" لتصفها (تقرأ وتحسب وتحفظ)، قسّمها.

🎯 التالي: القوائم (Lists): تخزين عدّة قيم معًا.

شرح الدوال (Functions) — Python بالعربي
الدوال (Functions) — Python بالعربي · The Code Fix

📚 لمزيد من التعمّق في Python، راجِع التوثيق الرسمي لـ Python.

هل كان هذا الدرس مفيدًا؟