دوال الرتبة العالية و Lambda
في بايثون، الدوال هي أيضاً "قيم" — يمكن إسنادها إلى متغيرات، تمريرها إلى دوال أخرى، تماماً مثل الأرقام والسلاسل النصية. هذا يتيح كوداً موجزاً وأنيقاً جداً. دوال الرتبة العالية هي دوال تأخذ دوالاً أخرى كوسائط أو تُرجع دوالاً. هذا الدرس سيفتح لك عالماً جديداً.
1. الدوال كمواطنين من الدرجة الأولى
في بايثون، يمكن التعامل مع الدوال مثل أي قيمة أخرى:
مثال: الدوال كقيم
def say_hello(name):
return f"Hello, {name}"
# أسند دالة إلى متغير
my_func = say_hello
print(my_func("Alice")) # Hello, Alice
# مرر دالة كوسيطة
def greet_with(func, name):
return func(name)
print(greet_with(say_hello, "Bob")) # Hello, Bob
# أرجع دالة من دالة
def create_greeter(greeting):
def greeter(name):
return f"{greeting}, {name}!"
return greeter
say_hi = create_greeter("Hi")
say_hello = create_greeter("Hello")
print(say_hi("Alice")) # Hi, Alice!
print(say_hello("Bob")) # Hello, Bob!
2. map(): التحويل الجماعي
map() تطبق دالة على كل عنصر في تسلسل وتُرجع مكرراً جديداً:
مثال: map تحويل جماعي
# ربّع كل رقم في قائمة
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(squared) # [1, 4, 9, 16, 25]
# حوّل قائمة سلاسل نصية إلى أحرف كبيرة
fruits = ["apple", "banana", "cherry"]
upper_fruits = list(map(str.upper, fruits))
print(upper_fruits) # ['APPLE', 'BANANA', 'CHERRY']
# مئوي إلى فهرنهايت
temps_c = [0, 10, 20, 30, 40]
temps_f = list(map(lambda c: c * 9 / 5 + 32, temps_c))
print(temps_f) # [32.0, 50.0, 68.0, 86.0, 104.0]
map()تعادل شاملة قائمة:map(f, seq)≈[f(x) for x in seq]. أي استخدام هو مسألة تفضيل — الشاملات أكثر "بايثونية."
3. filter(): التصفية الجماعية
filter() تختار العناصر التي تُرجع الدالة True لها:
مثال: filter بيانات
# صفّ الأرقام الزوجية
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4, 6, 8, 10]
# صفّ الكلمات بطول >= 5
words = ["cat", "elephant", "dog", "giraffe", "ant"]
long_words = list(filter(lambda w: len(w) >= 5, words))
print(long_words) # ['elephant', 'giraffe']
# صفّ السلاسل غير الفارغة
data = ["hello", "", "world", "", "python"]
non_empty = list(filter(None, data)) # None تعني "أزل القيم falsy"
print(non_empty) # ['hello', 'world', 'python']
filter()أيضاً تعادل شاملة:filter(f, seq)≈[x for x in seq if f(x)]. استخدامNoneكدالة يعادل إزالة جميع القيم falsy ("",0,None,False، إلخ.).
4. استخدام sorted() المتقدم
الدرس 11 غطى معامل key في sorted(). دعنا نتعمق أكثر:
مثال: ترتيب مخصص باستخدام sorted
# ترتيب حسب سمة الكائن
students = [
{"name": "Alice", "score": 85},
{"name": "Bob", "score": 92},
{"name": "Charlie", "score": 78},
]
# ترتيب حسب الدرجة تنازلياً
ranked = sorted(students, key=lambda s: s["score"], reverse=True)
for s in ranked:
print(f"{s['name']}: {s['score']}")
# ترتيب حسب طول الاسم
sorted_by_name = sorted(students, key=lambda s: len(s["name"]))
print([s["name"] for s in sorted_by_name]) # ['Alice', 'Bob', 'Charlie']
مثال: ترتيب متعدد المستويات (الصعوبة ⭐⭐)
# ترتيب حسب الدرجة تنازلياً، ثم حسب الاسم أبجدياً
data = [
("Alice", 85),
("Bob", 92),
("Charlie", 85),
("Diana", 92),
]
def sort_key(item):
return (-item[1], item[0])
sorted_data = sorted(data, key=sort_key)
for name, score in sorted_data:
print(f"{name}: {score}")
المخرجات:
Bob: 92
Diana: 92
Alice: 85
Charlie: 85
5. reduce(): الحساب التراكمي
reduce() يُجمّع العناصر في تسلسل، مطبقاً دالة على عنصرين في كل مرة. إنها ليست مدمجة؛ استوردها من functools:
مثال: reduce حساب تراكمي
from functools import reduce
# حاصل ضرب جميع الأرقام
numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product) # 120 (1×2×3×4×5)
# ابحث عن القيمة القصوى
max_val = reduce(lambda a, b: a if a > b else b, [3, 1, 4, 1, 5])
print(max_val) # 5
# دمج السلاسل النصية
words = ["Hello", " ", "World", " ", "Python"]
sentence = reduce(lambda a, b: a + b, words)
print(sentence) # Hello World Python
عملية تنفيذ reduce()
# reduce(lambda x, y: x + y, [1, 2, 3, 4])
# الخطوة 1: x=1, y=2 → 3
# الخطوة 2: x=3, y=3 → 6
# الخطوة 3: x=6, y=4 → 10
# النتيجة: 10
reduce() مقابل الحلقات: أي شيء يمكن لـ reduce() فعله، يمكن لحلقة for فعله أيضاً. reduce() أكثر إيجازاً وتصريحاً — أنت تقول "ماذا تفعل" بدلاً من "كيف تفعله." الجانب السلبي هو أنه قد يكون أصعب في الفهم للوافدين الجدد.
6. تعبيرات Lambda المتعمقة
Lambda، المسماة على اسم الحرف اليوناني λ، هي أساساً دالة مجهولة — دالة صغيرة بدون اسم، تُستخدم ثم تُتجاهل.
Lambda مقابل الدوال العادية
مثال: Lambda مقابل عادية
# دالة عادية
def double(x):
return x * 2
# تعبير lambda
double = lambda x: x * 2
print(double(5)) # 10
صيغة Lambda الكاملة
lambda parameters: expression
# ↑ تعادل
def anonymous_function(parameters):
return expression
معاملات متعددة
مثال: Lambda مع معاملات متعددة
add = lambda a, b: a + b
print(add(3, 5)) # 8
# تعبير ثلاثي في lambda
max_val = lambda a, b: a if a > b else b
print(max_val(10, 20)) # 20
متى تستخدم Lambda
مثال: حالات استخدام Lambda
# ✅ جيد: تحويل بسيط من سطر واحد كوسيطة لدالة رتبة عالية
numbers = [1, 2, 3, 4]
result = list(map(lambda x: x ** 2, numbers))
# ❌ سيء: منطق معقد يتطلب أسطراً متعددة — استخدم دالة def عادية بدلاً من ذلك
حالات الاستخدام الشائعة
- خطوط أنابيب البيانات: سلسلة
map()→filter()→sorted()لمعالجة البيانات. - الترتيب المخصص: استخدم
lambdaكمعاملkeyلقواعد ترتيب مختلفة. - استدعاءات الأحداث: مرر الدوال كوسائط لأطر GUI أو الويب.
- مصانع الإغلاق: الدالة الخارجية تُرجع دالة داخلية لوظائف "مُعلمة."
- التقييم الكسول: دوال الرتبة العالية تؤجل الحساب حتى الحاجة إليه.
❓ أسئلة شائعة
map() أم شاملات القوائم؟map() لها مزايا: ① أقصر عندما يكون لديك اسم دالة موجودة (map(str.upper, seq) مقابل [s.upper() for s in seq])؛ ② تُرجع مكرراً كسولاً، يوفر الذاكرة للبيانات الكبيرة. في معظم الحالات، شاملات القوائم هي الخيار الأفضل.
⚠️ س:هل يمكن لـ lambda أن تمتد عبر أسطر متعددة؟ ج:لا. صيغة lambda مقيدة بتعبيرات مفردة. للمنطق متعدد الأسطر، عرّف دالة عادية. Lambda في بايثون أضعف عمداً منها في لغات أخرى (مثل JavaScript) — غيدو (منشئ بايثون) يعتقد أن "الصريح أفضل من الضمني."
س:هل reduce() لا تزال مستخدمة في بايثون الحديثة؟ ج:ليس كثيراً. غيدو نفسه لم يكن معجباً بـ reduce()، معتبراً إياها صعبة الفهم. معظم سيناريوهات reduce() يمكن استبدالها بحلقة أو دالة أكثر مباشرة (مثل sum()، max()). ومع ذلك، تبقى reduce() مفهوماً مهماً في البرمجة الوظيفية.📖 ملخص
- الدوال هي مواطنون من الدرجة الأولى في بايثون — يمكن إسنادها وتمريرها وإرجاعها
map(f, seq)تطبق دالة على كل عنصر؛ استخدمlist()للتحويل إلى قائمةfilter(f, seq)تختار العناصر حيث تُرجع الدالة Truesorted(seq, key=f)يقبل دالة للترتيب المخصصreduce(f, seq)يُجمّع القيم؛ استورد منfunctools- Lambda
lambda params: expressionهي دالة مجهولة من سطر واحد للسيناريوهات البسيطة
📝 تمارين
-
أساسي (الصعوبة ⭐): بالنظر إلى
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]، استخدمmap()وfilter()لـ: تصفية الأرقام الفردية وحساب مربعاتها. تلميح: أولاًfilterللفردية، ثمmapللتربيع. -
متوسط (الصعوبة ⭐⭐): بالنظر إلى قائمة من القواميس
products = [{"name": "A", "price": 100}, {"name": "B", "price": 80}, {"name": "C", "price": 120}]، استخدمsorted()للترتيب حسب السعر تنازلياً وأخرج الأسماء والأسعار. -
تحدي (الصعوبة ⭐⭐⭐): استخدم
reduce()لتنفيذ دالةflatten(nested_list)التي تسطّح قائمة متداخلة بمستوى واحد. مثال:flatten([[1, 2], [3, 4], [5]])تُرجع[1, 2, 3, 4, 5]. تلميح: فيreduce، استخدم+لدمج قائمتين في كل مرة. ثم نفذ نفس الشيء بشاملة قائمة وقارن بين النهجين.



