דילוג לתוכן המרכזי
מודול 9 · שיעור 4 מתוך 436/36 בקורס~14 min
שיעורי מודול (4/4)

דקורטורים: פונקציות שמשנות פונקציות

דקורטור היא פונקציה שלוקחת פונקציה אחרת ומחזירה א גרסה "מעוטרת" שלו. זוהי דרך רבת עוצמה להוסיף התנהגות (רישום, תזמון, שמירה במטמון, ניסיון חוזר, אימות) מבלי לגעת בגוף הפונקציה המקורית.

מתפקד כערכים

ב-Python, פונקציות הן אובייקטים: אתה יכול להעביר אותם, להחזיר אותם, להקצות אותם.

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()

דקורטורים מהסטדליב

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טוען...

כתוב דקורטור `תוצא_כפול` שקורא לפונקציה המעוטרת ומחזיר את התוצאה שלה * 2. החל אותו על פונקציה `f(x)` שמחזירה x + 1. הערך `f(10)`.

טוען עורך...
הצג רמז

העטיפה קוראת ל-func ומחזירה את התוצאה כפול 2.

הפתרון זמין לאחר 3 ניסיונות

סקירת התרגיל

פעילות גופנית#python.m9.l4.e2
ניסיונות: 0טוען...

השתמש ב-functools.lru_cache כדי להאיץ פונקציית fib(n) רקורסיבית. חשב את fib(30) והקצה ל-`r`. הערך '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 ניסיונות