Python dekoratorlari: qanday ishlaydi va o‘zingiznikini yozish
Amaliyot3 daqInnovialabs tahririyati
Qisqacha
Dekorator — funksiyani qabul qilib, chaqiruvdan oldin yoki keyin qo‘shimcha xatti-harakat qo‘shilgan yangi funksiyani qaytaruvchi funksiya; @decorator yozuvi shunchaki func = decorator(func) ning qisqa shakli.
Dekorator nima
Dekorator — boshqa funksiyani qabul qilib, yangi funksiya qaytaradigan funksiya. Funksiya ustiga @timer yozish — shunchaki func = timer(func) ning qisqa shakli. Dekoratorlar takrorlanadigan mantiqni — loglash, vaqtni o‘lchash, qayta urinish, ruxsatni tekshirish — biznes-koddan bitta joyga chiqarishga imkon beradi.
Ular Python’ning ikki xususiyati tufayli ishlaydi: funksiyalar oddiy obyektlar, ichki funksiya esa tashqi funksiyaning o‘zgaruvchilarini «eslab qoladi». Bu closure (yopilma) deyiladi.
1-qadam: closure
def make_greeter(greeting):
def greet(name):
return f"{greeting}, {name}!"
return greet
hello = make_greeter("Salom")
print(hello("Anna")) # Salom, Anna!
make_greeter tugaganidan keyin ham ichki greet funksiyasi greeting ni ko‘radi. Dekorator ham xuddi shunday ishlaydi, faqat tashqi funksiyaga satr emas, funksiya uzatiladi.
2-qadam: birinchi dekorator va functools.wraps
import functools
import time
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
print(f"{func.__name__}: {time.perf_counter() - start:.3f} s")
return result
return wrapper
@timer
def build_report(n):
return sum(range(n))
Muhim jihatlar:
*args, **kwargs— o‘ram (wrapper) har qanday argumentni qabul qilib, ularni uzatadi.return result— busiz dekoratsiyalangan funksiyaNoneqaytaradi.functools.wrapsasl funksiyaning__name__,__doc__va boshqa atributlarini ko‘chiradi. Usiz barcha funksiyalarwrapperdeb nomlanadi, bu esa loglar, debugging va hujjatlarni buzadi.
3-qadam: argumentli dekorator
Dekoratorga parametrlar kerak bo‘lsa, yana bir daraja qo‘shiladi: tashqi funksiya sozlamalarni qabul qilib, dekoratorning o‘zini qaytaradi.
def retry(times=3, delay=1.0, exceptions=(Exception,)):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(1, times + 1):
try:
return func(*args, **kwargs)
except exceptions:
if attempt == times:
raise
time.sleep(delay)
return wrapper
return decorator
@retry(times=5, delay=0.5, exceptions=(ConnectionError,))
def fetch_rates():
...
E’tibor bering: qavsli @retry() va qavssiz @retry — turli narsalar. Ikkinchi holatda funksiya times parametriga tushadi va kod buziladi.
Amaliy misollar
Keshlash. Sof funksiyalar uchun o‘z keshingizni yozmang — standart kutubxonada functools.lru_cache va functools.cache bor:
@functools.lru_cache(maxsize=256)
def get_tariff(region_id):
...
Argumentlar hashlanadigan bo‘lishi kerak, kesh esa jarayon xotirasida saqlanadi.
Ruxsatni tekshirish. Birinchi argument sifatida foydalanuvchi keladigan handlerlarga dekorator qo‘yish qulay:
def require_role(role):
def decorator(func):
@functools.wraps(func)
def wrapper(user, *args, **kwargs):
if role not in user.roles:
raise PermissionError(f"{role} roli kerak")
return func(user, *args, **kwargs)
return wrapper
return decorator
@require_role("admin")
def delete_order(user, order_id):
...
Klass-dekoratorlar va klasslar uchun dekoratorlar
Bular ikki xil tushuncha:
| Nima | Ko‘rinishi | Qachon foydali |
|---|---|---|
| Dekorator sifatidagi klass | __init__(self, func) va __call__ li klass | holat saqlash kerak, masalan chaqiruvlar hisoblagichi |
| Klass uchun dekorator | funksiya klassni qabul qilib, klass qaytaradi | ro‘yxatdan o‘tkazish, metod qo‘shish; @dataclass shunday ishlaydi |
class CountCalls:
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
return self.func(*args, **kwargs)
Ko‘p uchraydigan xatolar
functools.wrapsni unutish — nom va docstring yo‘qoladi.- O‘ramda
returnni unutish — funksiya jimginaNoneqaytaradi. - Parametrli dekoratorlarda
@decova@deco()ni adashtirish. - Tartibni hisobga olmaslik: dekoratorlar pastdan yuqoriga qo‘llanadi,
defga eng yaqini birinchi. async defni oddiy o‘ram bilan o‘rash: korutinlar uchun o‘ram hamasyncbo‘lishi vaawait func(...)qilishi kerak.- Dekoratorga juda ko‘p mantiq yashirish — xatti-harakat nomidan tushunarli bo‘lmasa, kodni o‘qish qiyinlashadi.