Kodokon kodokon.com

المُزخرِفات والدوال عالية الرتبة

استثمر الدوال ككائنات من الدرجة الأولى لكتابة مُزخرِفات متينة وقابلة للتخصيص بمعاملات وشفافة.

10 دقيقة · 3 أسئلة

افتح هذا الدرس في Kodokon

في Python، الدالة كائن كأي كائن آخر: يمكنك إسنادها إلى متغير، وتمريرها كمُعطى، وإرجاعها. أما الدالة عالية الرتبة فهي التي تأخذ دالة أو تُرجِع دالة - وsorted(data, key=...) واحدة منها. أضف إلى ذلك الإغلاقات (closures)، حيث تلتقط دالة داخلية متغيرات الدالة المحيطة وتبقيها حية بعد عودتها، فتحصل على كل مكوّنات المُزخرِفات.

PYTHON
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
إغلاق: تلتقط multiply المتغير factor

إن المُزخرِف (decorator) دالة تتلقّى دالة وتُرجِع أخرى - عادةً غلافًا wrapper يضيف سلوكًا حول الاستدعاء. والصياغة @timed فوق def slow_sum ليست إلا اختصارًا نحويًا صرفًا لـ slow_sum = timed(slow_sum). ويقبل الغلاف *args, kwargs ليبقى محايدًا تجاه البصمة، ويُرجِع** نتيجة الدالة المُزخرَفة - ونسيان ذلك خطأ كلاسيكي يحوّل كل قيمة مُرجَعة إلى None.

PYTHON
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__ أوضح للقراءة. واحصر المُزخرِفات في الاهتمامات المستعرِضة: التسجيل، والتخزين المؤقت، وإعادة المحاولة، والتحكم في الوصول. فالمُزخرِف الذي يغيّر منطق الأعمال يُخفي الجوهري عن القارئ.

PYTHON
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")
مُزخرِف بمعاملات: مصنع، ثم مُزخرِف، ثم غلاف

اختبار المعرفة

تأكّد من أنك تذكّرت النقاط الأساسية في هذا الدرس.

  1. ما الذي تكافئه الصياغة @timed الموضوعة فوق def slow_sum؟
    • timed(slow_sum())
    • slow_sum = timed(slow_sum)
    • slow_sum.timed = True
  2. أيّ مشكلة يحلّها @functools.wraps؟
    • يُسرّع استدعاء الدالة المُزخرَفة
    • يحافظ على اسم الدالة المُزخرَفة ونص توثيقها
    • يجعل المُزخرِف متوافقًا مع توابع الأصناف
  3. في @repeat(times=3)، ماذا يُرجِع الاستدعاء repeat(times=3)؟
    • نتيجة الدالة المُزخرَفة
    • مُزخرِفًا، جاهزًا لتلقّي الدالة
    • الغلاف النهائي مباشرة
    • قائمة من ثلاث نتائج