الدوال كقيم: Lambda والدوال العليا والإغلاقات — Python المتقدم
الدوال كقيم: Lambda والدوال العليا والإغلاقات
في Python الدالة كائن مثل أي قيمة: يمكن وضعها في متغير، وتمريرها لدالة أخرى، وإرجاعها من دالة. هذه الفكرة أساس كل ما في هذه الدورة تقريباً (decorators، مفاتيح الترتيب، callbacks).
def shout(text):
return text.upper() + "!"
speak = shout # بدون أقواس: لا نستدعي الدالة، بل نشير إليها
print(speak("مرحبا"))الدوال العليا (Higher-Order Functions)
دالة تأخذ دالة كمعامل أو ترجع دالة:
def apply_twice(func, value):
return func(func(value))
print(apply_twice(lambda x: x * 3, 2)) # 18lambda
دالة صغيرة بلا اسم من تعبير واحد: lambda المعاملات: التعبير. مناسبة للاستخدام لمرة واحدة كمعامل (key=lambda p: p.price). إذا احتجت أكثر من تعبير أو اسماً واضحاً، اكتب def.
map و filter و reduce
nums = [1, 2, 3, 4, 5]
list(map(lambda n: n * 2, nums)) # [2, 4, 6, 8, 10]
list(filter(lambda n: n % 2, nums)) # [1, 3, 5]
from functools import reduce
reduce(lambda acc, n: acc + n, nums) # 15في Python الحديثة غالباً تكون comprehensions أوضح من map و filter: [n * 2 for n in nums]. أما reduce فاستخدم بدلها دوالاً جاهزة مثل sum و max و math.prod كلما أمكن.
الإغلاق (Closure)
دالة داخلية تتذكر المتغيرات من الدالة التي أنشأتها حتى بعد انتهائها:
def make_multiplier(factor):
def multiply(x):
return x * factor # factor من النطاق الخارجي
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5), triple(5)) # 10 15كل استدعاء لـ make_multiplier ينشئ دالة جديدة تحمل قيمة factor الخاصة بها. هذا بالضبط ما تقوم عليه الـ decorators في الدرس التالي.
nonlocal
لتعديل (وليس فقط قراءة) متغير من النطاق الخارجي داخل الإغلاق:
def counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
c = counter()
print(c(), c(), c()) # 1 2 3functools.partial
تثبيت بعض معاملات دالة لإنشاء دالة أبسط: partial(pow, exp=2).
فخ شائع: الإغلاق داخل حلقة
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs]) # [2, 2, 2] وليس [0, 1, 2]
funcs = [lambda i=i: i for i in range(3)]
print([f() for f in funcs]) # [0, 1, 2]الإغلاق يتذكر المتغير لا قيمته لحظة الإنشاء؛ القيمة الافتراضية i=i تثبّت القيمة.
تمرين
اكتب make_validator(min_len) ترجع دالة تتحقق من طول كلمة مرور، و make_counter() ترجع دالتين: زيادة وإعادة تعيين، تتشاركان نفس العدّاد.
# Lambda بسيطة
square = lambda x: x ** 2
print(square(5)) # 25
add = lambda x, y: x + y
print(add(3, 4)) # 7
# map - تطبيق دالة على كل عنصر
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared) # [1, 4, 9, 16, 25]
# مع دالة عادية
def double(x):
return x * 2
doubled = list(map(double, numbers))
print(doubled) # [2, 4, 6, 8, 10]
# filter - تصفية العناصر
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4]
# تصفية الأسماء الطويلة
names = ["أحمد", "محمد علي", "سارة", "عبدالرحمن"]
long_names = list(filter(lambda n: len(n) > 4, names))
print(long_names)
# reduce - تجميع
from functools import reduce
total = reduce(lambda x, y: x + y, numbers)
print(total) # 15
# إيجاد الأكبر
maximum = reduce(lambda x, y: x if x > y else y, numbers)
print(maximum) # 5
# sorted مع key
students = [
{"name": "أحمد", "score": 85},
{"name": "محمد", "score": 92},
{"name": "علي", "score": 78}
]
# ترتيب حسب الدرجة
by_score = sorted(students, key=lambda s: s["score"], reverse=True)
for s in by_score:
print(f"{s['name']}: {s['score']}")
# ترتيب نصوص حسب الطول
words = ["programming", "is", "fun", "and", "challenging"]
by_length = sorted(words, key=len)
print(by_length)
# دمج map و filter
result = list(map(
lambda x: x ** 2,
filter(lambda x: x % 2 == 0, range(10))
))
print(result) # [0, 4, 16, 36, 64]
# دالة ترجع دالة
def multiplier(n):
return lambda x: x * n
double = multiplier(2)
triple = multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15اختبر فهمك