🔒

سجّل للوصول إلى الدروس

للاستمرار في تعلم بايثون، يرجى إنشاء حساب مجاني أو تسجيل الدخول إلى حسابك الحالي.

وصول مجاني لجميع دروس بايثون
حفظ تقدمك تلقائياً
بيئة تجربة بايثون التفاعلية
الدرس 14

الدوال المتقدمة

اكتشف القوة الحقيقية للدوال في بايثون مع الوسيطات المتغيرة ودوال lambda والإغلاقات

📚 الوحدة 4: الدوال ⏱️ 20 دقيقة قراءة

*args - الوسيطات الموضعية المتغيرة

*args يسمح للدالة بقبول أي عدد من الوسيطات الموضعية. تُجمع الوسيطات في صف (tuple).

Python استخدام *args
# *args تجمع الوسيطات الموضعية الإضافية
def مجموع_الكل(*args):
    """جمع أي عدد من الوسيطات."""
    print(f"نوع args: {type(args)}")  # tuple
    return sum(args)

print(مجموع_الكل(1, 2))           # 3
print(مجموع_الكل(1, 2, 3, 4, 5))  # 15
print(مجموع_الكل())               # 0

# دمج معامل عادي مع *args
def تحية_الجميع(التحية, *الأسماء):
    for اسم in الأسماء:
        print(f"{التحية} يا {اسم}!")

تحية_الجميع("مرحباً", "أحمد", "سارة", "محمد")
# مرحباً يا أحمد!
# مرحباً يا سارة!
# مرحباً يا محمد!

# *args مع معاملات أخرى
def وصف_حيوان(الحيوان, *الصفات, المالك="غير معروف"):
    print(f"الحيوان: {الحيوان}")
    print(f"الصفات: {', '.join(الصفات)}")
    print(f"المالك: {المالك}")

وصف_حيوان("قطة", "لطيفة", "ودودة", "نشيطة", المالك="سارة")

**kwargs - الوسيطات المُسماة المتغيرة

**kwargs يسمح للدالة بقبول أي عدد من الوسيطات المُسماة. تُجمع الوسيطات في قاموس.

Python استخدام **kwargs
# **kwargs تجمع الوسيطات المُسماة الإضافية
def طباعة_معلومات(**kwargs):
    """طباعة جميع الوسيطات المُسماة."""
    print(f"نوع kwargs: {type(kwargs)}")  # dict
    for مفتاح, قيمة in kwargs.items():
        print(f"{مفتاح}: {قيمة}")

طباعة_معلومات(الاسم="أحمد", العمر=30, المدينة="القاهرة")
# الاسم: أحمد
# العمر: 30
# المدينة: القاهرة

# إنشاء كائنات مع kwargs
def إنشاء_شخص(**kwargs):
    return {
        "الاسم": kwargs.get("الاسم", "غير معروف"),
        "العمر": kwargs.get("العمر", 0),
        "البريد": kwargs.get("البريد", "غير متاح")
    }

شخص = إنشاء_شخص(الاسم="سارة", العمر=25)
print(شخص)  # {'الاسم': 'سارة', 'العمر': 25, 'البريد': 'غير متاح'}

# دمج جميع أنواع المعاملات
def دالة_معقدة(مطلوب, *args, افتراضي="قيمة", **kwargs):
    print(f"المطلوب: {مطلوب}")
    print(f"Args: {args}")
    print(f"الافتراضي: {افتراضي}")
    print(f"Kwargs: {kwargs}")

دالة_معقدة("الأول", 1, 2, 3, افتراضي="مخصص", س=10, ص=20)

تفريغ الوسيطات

Python تفريغ القوائم والقواميس
# تفريغ قائمة/صف مع *
def جمع(أ, ب, ج):
    return أ + ب + ج

أرقام = [1, 2, 3]
print(جمع(*أرقام))  # 6 (يفرغ القائمة)

# تفريغ قاموس مع **
def تحية(الاسم, رسالة_ترحيب="مرحباً"):
    print(f"{رسالة_ترحيب} يا {الاسم}!")

شخص = {"الاسم": "أحمد", "رسالة_ترحيب": "أهلاً"}
تحية(**شخص)  # أهلاً يا أحمد!

# دمج التفريغ
def ملف_كامل(الأول, الأخير, *الهوايات, **التفاصيل):
    print(f"الاسم: {الأول} {الأخير}")
    print(f"الهوايات: {', '.join(الهوايات)}")
    for مفتاح, قيمة in التفاصيل.items():
        print(f"{مفتاح}: {قيمة}")

الأسماء = ["محمد", "علي"]
الهوايات = ["القراءة", "البرمجة", "الألعاب"]
المعلومات = {"العمر": 30, "المدينة": "الرياض"}

ملف_كامل(*الأسماء, *الهوايات, **المعلومات)

دوال Lambda

دوال Lambda هي دوال صغيرة مجهولة الاسم تُعرَّف في سطر واحد. مفيدة للعمليات القصيرة والبسيطة.

Python دوال Lambda
# صيغة lambda الأساسية: lambda الوسيطات: التعبير

# دالة عادية
def تربيع(س):
    return س ** 2

# دالة lambda مكافئة
تربيع = lambda س: س ** 2
print(تربيع(5))  # 25

# وسيطات متعددة
جمع = lambda أ, ب: أ + ب
print(جمع(3, 4))  # 7

# استخدام شائع: الترتيب
الطلاب = [
    {"الاسم": "أحمد", "الدرجة": 90},
    {"الاسم": "سارة", "الدرجة": 85},
    {"الاسم": "محمد", "الدرجة": 95}
]

# ترتيب حسب الدرجة
مرتب_بالدرجة = sorted(الطلاب, key=lambda ط: ط["الدرجة"])
print([ط["الاسم"] for ط in مرتب_بالدرجة])  # ['سارة', 'أحمد', 'محمد']

# ترتيب حسب الاسم
مرتب_بالاسم = sorted(الطلاب, key=lambda ط: ط["الاسم"])

# ترتيب تنازلي
مرتب_تنازلي = sorted(الطلاب, key=lambda ط: ط["الدرجة"], reverse=True)

# Lambda مع filter
الأرقام = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
الزوجية = list(filter(lambda س: س % 2 == 0, الأرقام))
print(الزوجية)  # [2, 4, 6, 8, 10]

# Lambda مع map
المربعات = list(map(lambda س: س ** 2, الأرقام))
print(المربعات)  # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
💡 متى تستخدم Lambda

استخدم lambda لـ:

  • دوال قصيرة من تعبير واحد
  • دوال تُمرر إلى sorted()، filter()، map()
  • دوال الاستدعاء البسيطة

استخدم الدوال العادية لـ:

  • منطق معقد أو عبارات متعددة
  • دوال تحتاج نصوص توثيقية
  • دوال قابلة لإعادة الاستخدام في أماكن متعددة

الدوال عالية المستوى

دوال تأخذ دوالاً أخرى كوسيطات أو تُرجع دوالاً.

Python map، filter، reduce
# map() - تطبيق دالة على كل عنصر
الأرقام = [1, 2, 3, 4, 5]

def مضاعفة(س):
    return س * 2

المضاعف = list(map(مضاعفة, الأرقام))
print(المضاعف)  # [2, 4, 6, 8, 10]

# filter() - الاحتفاظ بالعناصر التي تجتاز الاختبار
def هل_زوجي(س):
    return س % 2 == 0

الزوجية = list(filter(هل_زوجي, الأرقام))
print(الزوجية)  # [2, 4]

# reduce() - عملية تراكمية
from functools import reduce

def جمع(أ, ب):
    return أ + ب

المجموع = reduce(جمع, الأرقام)  # ((((1+2)+3)+4)+5)
print(المجموع)  # 15

# مع lambda
الحاصل = reduce(lambda أ, ب: أ * ب, الأرقام)
print(الحاصل)  # 120

# دالة عالية المستوى مخصصة
def تطبيق_عملية(الأرقام, العملية):
    """تطبيق العملية على كل رقم."""
    return [العملية(ن) for ن in الأرقام]

الناتج = تطبيق_عملية([1, 2, 3], lambda س: س ** 2)
print(الناتج)  # [1, 4, 9]

# دالة تُرجع دالة
def منشئ_المضاعف(ن):
    """إنشاء دالة تضرب في ن."""
    def المضاعف(س):
        return س * ن
    return المضاعف

مضاعفة = منشئ_المضاعف(2)
ثلاثة_أضعاف = منشئ_المضاعف(3)

print(مضاعفة(5))      # 10
print(ثلاثة_أضعاف(5))  # 15

الإغلاقات (Closures)

الإغلاق هو دالة تتذكر القيم من نطاقها المحيط.

Python الإغلاقات في بايثون
# مثال على الإغلاق
def منشئ_العداد():
    العدد = 0  # متغير محاط

    def العداد():
        nonlocal العدد  # الوصول للنطاق المحيط
        العدد += 1
        return العدد

    return العداد

# كل استدعاء لـ منشئ_العداد ينشئ إغلاقاً جديداً
عداد1 = منشئ_العداد()
عداد2 = منشئ_العداد()

print(عداد1())  # 1
print(عداد1())  # 2
print(عداد1())  # 3

print(عداد2())  # 1 (عداد مستقل)
print(عداد2())  # 2

# إغلاق عملي: مسجل قابل للتخصيص
def منشئ_المسجل(البادئة):
    def تسجيل(الرسالة):
        print(f"[{البادئة}] {الرسالة}")
    return تسجيل

مسجل_الأخطاء = منشئ_المسجل("خطأ")
مسجل_المعلومات = منشئ_المسجل("معلومات")

مسجل_الأخطاء("حدث خطأ ما!")   # [خطأ] حدث خطأ ما!
مسجل_المعلومات("بدأت العملية")  # [معلومات] بدأت العملية

# إغلاق مع حالة متغيرة
def منشئ_المراكم():
    المجموع = [0]  # استخدام قائمة للسماح بالتعديل

    def إضافة(القيمة):
        المجموع[0] += القيمة
        return المجموع[0]

    return إضافة

مراكم = منشئ_المراكم()
print(مراكم(10))  # 10
print(مراكم(5))   # 15
print(مراكم(3))   # 18

التكرار الذاتي (Recursion)

دالة تستدعي نفسها لحل مشاكل فرعية أصغر.

Python الدوال التكرارية
# المضروب: ن! = ن * (ن-1) * (ن-2) * ... * 1
def مضروب(ن):
    if ن <= 1:  # الحالة الأساسية
        return 1
    return ن * مضروب(ن - 1)  # الحالة التكرارية

print(مضروب(5))  # 120 (5 * 4 * 3 * 2 * 1)

# متتالية فيبوناتشي
def فيبوناتشي(ن):
    if ن <= 1:
        return ن
    return فيبوناتشي(ن - 1) + فيبوناتشي(ن - 2)

print([فيبوناتشي(i) for i in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

# جمع القائمة (تكراري)
def مجموع_القائمة(القائمة):
    if not القائمة:  # الحالة الأساسية: قائمة فارغة
        return 0
    return القائمة[0] + مجموع_القائمة(القائمة[1:])

print(مجموع_القائمة([1, 2, 3, 4, 5]))  # 15

# تسطيح القائمة المتداخلة
def تسطيح(القائمة):
    الناتج = []
    for عنصر in القائمة:
        if isinstance(عنصر, list):
            الناتج.extend(تسطيح(عنصر))  # استدعاء تكراري
        else:
            الناتج.append(عنصر)
    return الناتج

متداخلة = [1, [2, 3, [4, 5]], 6, [7, [8, 9]]]
print(تسطيح(متداخلة))  # [1, 2, 3, 4, 5, 6, 7, 8, 9]
⚠️ حدود التكرار

بايثون لديها حد افتراضي للتكرار (عادة 1000). للتكرار العميق، فكر في التكرار الحلقي أو زيادة الحد:

import sys
sys.setrecursionlimit(10000)  # زيادة الحد

# أفضل: استخدم التكرار الحلقي للتكرار العميق
def مضروب_حلقي(ن):
    الناتج = 1
    for i in range(2, ن + 1):
        الناتج *= i
    return الناتج

🎯 تحدي: معالج بيانات مرن

أنشئ دالة تعالج البيانات مع عمليات قابلة للتخصيص:

  1. تقبل بيانات وأي عدد من دوال المعالجة
  2. تطبق كل دالة بالترتيب
  3. تُرجع النتيجة النهائية
💡 عرض الحل
def معالجة_بيانات(البيانات, *المعالجات):
    """
    تطبيق دوال معالجة متعددة على البيانات بالتسلسل.

    الوسيطات:
        البيانات: البيانات المبدئية للمعالجة
        *المعالجات: الدوال المراد تطبيقها بالترتيب

    الإرجاع:
        البيانات المعالجة بعد جميع التحويلات
    """
    الناتج = البيانات
    for معالج in المعالجات:
        الناتج = معالج(الناتج)
    return الناتج

# مثال على المعالجات
def إزالة_المسافات(نص):
    return نص.replace(" ", "")

def إلى_أحرف_كبيرة(نص):
    return نص.upper()

def إضافة_بادئة(نص):
    return f"النتيجة: {نص}"

# استخدام المعالج
النص = "hello world"
الناتج = معالجة_بيانات(النص, إزالة_المسافات, إلى_أحرف_كبيرة, إضافة_بادئة)
print(الناتج)  # النتيجة: HELLOWORLD

# معالجة الأرقام
الأرقام = [1, 2, 3, 4, 5]
الناتج = معالجة_بيانات(
    الأرقام,
    lambda س: [ن * 2 for ن in س],      # مضاعفة
    lambda س: [ن for ن in س if ن > 4], # تصفية > 4
    sum                                 # جمع
)
print(الناتج)  # 24 (6 + 8 + 10)

🎯 تحدي: نظام معالجة الأحداث

أنشئ نظام أحداث بسيط يمكنه:

  • تسجيل معالجات للأحداث
  • إطلاق أحداث مع بيانات
  • استدعاء جميع المعالجات لذلك الحدث
💡 عرض الحل
def منشئ_نظام_الأحداث():
    """إنشاء نظام معالجة أحداث باستخدام الإغلاقات."""
    المعالجات = {}

    def عند(اسم_الحدث, المعالج):
        """تسجيل معالج لحدث."""
        if اسم_الحدث not in المعالجات:
            المعالجات[اسم_الحدث] = []
        المعالجات[اسم_الحدث].append(المعالج)

    def إطلاق(اسم_الحدث, *args, **kwargs):
        """إطلاق حدث، استدعاء جميع المعالجات المسجلة."""
        if اسم_الحدث in المعالجات:
            for معالج in المعالجات[اسم_الحدث]:
                معالج(*args, **kwargs)

    def إيقاف(اسم_الحدث, المعالج=None):
        """إزالة معالج(ات) لحدث."""
        if المعالج is None:
            المعالجات.pop(اسم_الحدث, None)
        elif اسم_الحدث in المعالجات:
            المعالجات[اسم_الحدث].remove(المعالج)

    return عند, إطلاق, إيقاف

# إنشاء نظام الأحداث
عند, إطلاق, إيقاف = منشئ_نظام_الأحداث()

# تسجيل المعالجات
def تسجيل_الدخول(المستخدم):
    print(f"المستخدم سجل دخوله: {المستخدم}")

def إرسال_ترحيب(المستخدم):
    print(f"تم إرسال رسالة ترحيب إلى {المستخدم}")

def تحديث_الإحصائيات(المستخدم):
    print(f"تم تحديث الإحصائيات لـ {المستخدم}")

عند("تسجيل_دخول_مستخدم", تسجيل_الدخول)
عند("تسجيل_دخول_مستخدم", إرسال_ترحيب)
عند("تسجيل_دخول_مستخدم", تحديث_الإحصائيات)

# إطلاق الحدث
print("--- المستخدم يسجل الدخول ---")
إطلاق("تسجيل_دخول_مستخدم", "ahmed@example.com")

# إزالة معالج واحد
إيقاف("تسجيل_دخول_مستخدم", إرسال_ترحيب)

print("\n--- تسجيل دخول آخر ---")
إطلاق("تسجيل_دخول_مستخدم", "sara@example.com")

📝 ملخص الدرس

  • *args يجمع الوسيطات الموضعية في صف
  • **kwargs يجمع الوسيطات المُسماة في قاموس
  • Lambda: lambda س: تعبير لدوال بسيطة
  • الدوال عالية المستوى: map()، filter()، reduce()
  • الإغلاقات تتذكر المتغيرات من النطاق المحيط
  • استخدم nonlocal لتعديل المتغيرات المحيطة
  • التكرار الذاتي يحتاج حالة أساسية لمنع الحلقات اللانهائية

في الدرس التالي، سنتعلم عن المُزخرفات (Decorators) - ميزة قوية في بايثون!