استثمر الدوال ككائنات من الدرجة الأولى لكتابة مُزخرِفات متينة وقابلة للتخصيص بمعاملات وشفافة.
افتح هذا الدرس في Kodokonفي Python، الدالة كائن كأي كائن آخر: يمكنك إسنادها إلى متغير، وتمريرها كمُعطى، وإرجاعها. أما الدالة عالية الرتبة فهي التي تأخذ دالة أو تُرجِع دالة - وsorted(data, key=...) واحدة منها. أضف إلى ذلك الإغلاقات (closures)، حيث تلتقط دالة داخلية متغيرات الدالة المحيطة وتبقيها حية بعد عودتها، فتحصل على كل مكوّنات المُزخرِفات.
def make_multiplier(factor):
def multiply(value):
return value * factor
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(10), triple(10)) # 20 30إن المُزخرِف (decorator) دالة تتلقّى دالة وتُرجِع أخرى - عادةً غلافًا wrapper يضيف سلوكًا حول الاستدعاء. والصياغة @timed فوق def slow_sum ليست إلا اختصارًا نحويًا صرفًا لـ slow_sum = timed(slow_sum). ويقبل الغلاف *args, kwargs ليبقى محايدًا تجاه البصمة، ويُرجِع** نتيجة الدالة المُزخرَفة - ونسيان ذلك خطأ كلاسيكي يحوّل كل قيمة مُرجَعة إلى None.
import functools
import time
def timed(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
delay = time.perf_counter() - start
print(f"{func.__name__}: {delay:.4f}s")
return result
return wrapper
@timed
def slow_sum(n):
return sum(range(n))
print(slow_sum(1_000_000))لتخصيص مُزخرِف بمعاملات (@repeat(times=3))، تحتاج إلى مستوى إضافي: مصنع يتلقّى المعاملات ويُرجِع المُزخرِف، الذي يُرجِع بدوره الغلاف. ثلاث دوال متداخلة - وذلك ثمن المرونة. وإن تعقّدت الحالة، فغالبًا ما يكون صنف ذو تابع __call__ أوضح للقراءة. واحصر المُزخرِفات في الاهتمامات المستعرِضة: التسجيل، والتخزين المؤقت، وإعادة المحاولة، والتحكم في الوصول. فالمُزخرِف الذي يغيّر منطق الأعمال يُخفي الجوهري عن القارئ.
import functools
def repeat(times):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
result = None
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Hello {name}")
greet("Ada")