الانتقال إلى المحتوى الرئيسي
الوحدة 9 · الدرس 4 من 436/36 في الدورة~14 min
دروس الوحدة (4/4)

المزخرفون: الوظائف التي تعدل الوظائف

decorator هي دالة تأخذ دالة أخرى وترجع a نسخة "مزخرفة" منه. إنها طريقة قوية لإضافة السلوك (التسجيل، التوقيت، التخزين المؤقت، إعادة المحاولة، التحقق من الصحة) دون لمس نص الملف الوظيفة الأصلية.

وظائف كقيم

في بايثون، الوظائف هي كائنات: يمكنك تمريرها وإعادتها وتعيينها.

Python
def saluta():
    print("ciao")

f = saluta   # NIENTE parentesi: assegno la funzione
f()          # 'ciao'

ديكور الحد الأدنى

Python
def log(func):
    def wrapper(*args, **kwargs):
        print(f"chiamo {func.__name__}")
        risultato = func(*args, **kwargs)
        print(f"ho finito {func.__name__}")
        return risultato
    return wrapper

def somma(a, b):
    return a + b

somma = log(somma)   # ora somma è "wrapper"
somma(2, 3)
# chiamo somma
# ho finito somma
# 5

@decorator بناء الجملة

تمت كتابة المهمة somma = log(somma) بشكل أكثر أناقة باستخدام @:

Python
@log
def somma(a, b):
    return a + b

إنه ** بالضبط نفس الشيء **. @log أعلاه التعريف يعادل: "بمجرد تعريف هذه الوظيفة، استبدلها بـ log(function)".

*args, **kwargs: قبول أي توقيع

Python
def wrapper(*args, **kwargs):
    return func(*args, **kwargs)

*args يجمع الوسائط الموضعية، **kwargs وسيطات الكلمات الرئيسية. سمحوا تكتب أغلفة تعمل مع أي وظيفة.

functools.wraps: الحفاظ على البيانات الوصفية

بدون wraps، تفقد الوظيفة المزخرفة __name__، __doc__، تلميحات:

Python
from functools import wraps

def log(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        ...
        return func(*args, **kwargs)
    return wrapper

مثال: @timeit

Python
from functools import wraps
import time

def timeit(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        t0 = time.perf_counter()
        risultato = func(*args, **kwargs)
        dt = time.perf_counter() - t0
        print(f"{func.__name__} ha impiegato {dt*1000:.2f} ms")
        return risultato
    return wrapper

@timeit
def somma_grande():
    return sum(range(10**6))

somma_grande()

مصممو الديكور من stdlib

Python
from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    if n < 2: return n
    return fib(n - 1) + fib(n - 2)

fib(100)  # istantaneo grazie alla cache
  • @lru_cache — يحفظ نتائج دالة خالصة.
  • @property — يعرض الطريقة كسمة (تظهر في وحدة OOP).
  • @staticmethod، @classmethod — تغيير كيفية تمرير self.

الديكور بالحجج

يمكن لمصممي الديكور أيضًا قبول الحجج. لتنفيذ ذلك، يجب عليك كتابة ثلاثة مستويات من الوظائف المتداخلة: الوظيفة الخارجية تستقبل وسائط المزخرف، وتستقبل الوظيفة الوسطى الوظيفة المزخرفة، وتستقبل الوظيفة الأعمق (wrapper) الوسائط الفعلية التي تم تمريرها إلى الوظيفة المزخرفة أثناء الاستدعاءات.

جربه بنفسك

تمرين#python.m9.l4.e1
المحاولات: 0جارٍ التحميل…

اكتب ديكورًا `double_result` يستدعي الدالة المزخرفة ويعيد نتيجتها * 2. قم بتطبيقه على دالة `f(x)` التي ترجع x + 1. قم بتقييم `f(10)`.

جارٍ تحميل المحرر…
إظهار التلميح

يستدعي المجمّع func ويعيد النتيجة مضروبة في 2.

الحل متاح بعد 3 من المحاولات

تمرين المراجعة

تمرين#python.m9.l4.e2
المحاولات: 0جارٍ التحميل…

استخدم functools.lru_cache لتسريع وظيفة fib(n) العودية. حساب fib(30) وتعيين إلى `r`. تقييم "ر".

جارٍ تحميل المحرر…
إظهار التلميح

@lru_cache(maxsize=None) أعلى من القيمة المحددة.

الحل متاح بعد 3 من المحاولات

التحدي الإضافي

تمرين#python.m9.l4.e3
المحاولات: 0جارٍ التحميل…

Define a decorator `double_result` that doubles (multiplies by 2) the return value of the decorated function. Apply the decorator to the function `def sum_nums(a, b): return a + b`. Finally, evaluate `sum_nums(3, 4)`.

جارٍ تحميل المحرر…
إظهار التلميح

يستدعي المجمع func(*args, **kwargs) ويضرب النتيجة في 2.

الحل متاح بعد 3 من المحاولات