Pelajaran modul (4/4)
Dekorator: fungsi yang mengubah fungsi
dekorator adalah fungsi yang mengambil fungsi lain dan mengembalikan a versi "dihiasi" itu. Ini adalah cara ampuh untuk menambahkan perilaku (logging, timing, caching, coba lagi, validasi) tanpa menyentuh isi fungsi aslinya.
Berfungsi sebagai nilai
Dalam Python, fungsi adalah objek: Anda dapat meneruskannya, mengembalikannya, menugaskannya.
def saluta():
print("ciao")
f = saluta # NIENTE parentesi: assegno la funzione
f() # 'ciao'Dekorator minimal
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
# 5Sintaks @decorator
Tugas somma = log(somma) ditulis lebih elegan dengan @:
@log
def somma(a, b):
return a + bIni hal yang persis sama. @log definisi di atas setara dengan:
"segera setelah fungsi ini ditentukan, gantilah dengan log(function)".
*args, **kwargs: menerima tanda tangan apa pun
def wrapper(*args, **kwargs):
return func(*args, **kwargs)*args mengumpulkan argumen posisi, **kwargs argumen kata kunci. Mereka membiarkan
Anda menulis pembungkus yang berfungsi dengan fungsi apa pun.
functools.wraps: melestarikan metadata
Tanpa wraps, fungsi yang didekorasi kehilangan __name__, __doc__, petunjuk:
from functools import wraps
def log(func):
@wraps(func)
def wrapper(*args, **kwargs):
...
return func(*args, **kwargs)
return wrapperContoh: @timeit
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()Dekorator dari stdlib
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— mengingat hasil fungsi murni.@property— menampilkan metode sebagai atribut (terlihat di modul OOP).@staticmethod,@classmethod— mengubah caraselfditeruskan.
Dekorator dengan argumen
Dekorator juga dapat menerima argumen. Untuk mengimplementasikannya, Anda harus menulis tiga tingkat fungsi bertingkat: fungsi terluar menerima argumen dekorator, fungsi tengah menerima fungsi yang dihias, dan fungsi terdalam (wrapper) menerima argumen aktual yang diteruskan ke fungsi yang dihias selama panggilan.
Cobalah sendiri
Tulis dekorator `double_result` yang memanggil fungsi yang dihias dan mengembalikan hasilnya * 2. Terapkan ke fungsi `f(x)` yang mengembalikan x + 1. Evaluasi `f(10)`.
Tunjukkan petunjuk
Pembungkus memanggil func dan mengembalikan hasil dikalikan 2.
Solusi tersedia setelah 3 upaya
Tinjau latihan
Gunakan functools.lru_cache untuk mempercepat fungsi fib(n) rekursif. Hitung fib(30) dan tetapkan ke `r`. Evaluasi `r`.
Tunjukkan petunjuk
@lru_cache(maxsize=None) di atas def.
Solusi tersedia setelah 3 upaya
Tantangan tambahan
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)`.
Tunjukkan petunjuk
Pembungkus memanggil func(*args, **kwargs) dan mengalikan hasilnya dengan 2.
Solusi tersedia setelah 3 upaya