Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. @ nimaga ochiladi
- 2.2. Dekorator def paytida bajariladi
- 2.3. Dekorator nima qaytarishi mumkin
- 2.4. PEP 614 — istalgan ifoda
- 2.5. O'rovchi funksiya anatomiyasi
- 2.6. Bir nechta dekorator — "piyoz" modeli
- 2.7. Tuzoqlar
- 2.8. Dekorator qachon to'g'ri vosita
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — @ sintaksisini kuzatish
- Misol 2 — O'rovchi anatomiyasi va tuzoqlar
- Misol 3 — Piyoz: qatlamlar tartibi ma'noni o'zgartiradi
- Misol 4 — Amaliy: buyruqlar tizimi va ro'yxatga olish tuzog'i
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
10.8-dars: Dekorator — tushuncha
10-QISM — ILG'OR TIL IMKONIYATLARI · 8-dars
1. Kirish va motivatsiya
7.11-darsda dekoratorni "funksiya oladi, funksiya qaytaradi" deb ta'rifladik va bir nechta misol yozdik. Endi real loyihadan bir parcha:
ilova = Ilova()
@faqat_admin
@ilova.buyruq
def foydalanuvchini_ochir(ism):
...Kod ishlaydi, testlar o'tadi. Faqat bitta muammo bor: istalgan foydalanuvchi bu buyruqni chaqira oladi. Admin tekshiruvi umuman ishlamaydi.
Sababi — ikki qator tartibi. @ilova.buyruq pastda turgani uchun u asl funksiyani ro'yxatga oldi. @faqat_admin esa undan keyin qo'llandi va o'ralgan funksiyani faqat foydalanuvchini_ochir nomiga bog'ladi. Ilova buyruqlarni o'z ro'yxatidan chaqiradi — o'ralmagan funksiyadan.
Dekoratorni ishlatish oson, lekin to'g'ri ishlatish uchun @ aynan nima qilishini, qachon bajarilishini va qaysi tartibda qo'llanishini aniq bilish kerak.
Bu darsda:
-
@sintaksisi nimaga ochiladi va qachon bajariladi - Bir nechta dekorator: qo'llanish tartibi va bajarilish tartibi ("piyoz" modeli)
- Dekorator nima qaytarishi mumkin
- O'rovchi funksiya anatomiyasi
- Tuzoqlar: rekursiya, generator funksiyalar, metodlar,
staticmethod/classmethod - Dekorator qachon to'g'ri vosita, qachon emas
Argumentli dekoratorlar — 10.9-darsda, klass dekoratorlari — 10.10-darsda, functools.wraps — 10.11-darsda, closure'lar chuqur — 10.12-darsda.
2. Nazariya — chuqur tushuntirish
2.1. @ nimaga ochiladi
@dekorator
def f():
...Bu taxminan quyidagiga teng:
def f():
...
f = dekorator(f)"Taxminan" — chunki ikkita nozik farq bor:
@shaklida o'ralmagan funksiyafnomiga hech qachon bog'lanmaydi. Dekorator ishlayotgan paytdafnomi hali yo'q (yoki eski qiymatga ega)- Dekorator ifodasi
defdan oldin baholanadi
Aniq tartib:
@A
@B
def f(): ...Aifodasi baholanadiBifodasi baholanadi- Funksiya obyekti yaratiladi (nom hali bog'lanmagan)
B(funksiya)chaqiriladiA(natija)chaqiriladi- Oxirgi natija
fnomiga bog'lanadi
Ya'ni ifodalar yuqoridan pastga baholanadi, qo'llanish pastdan yuqoriga: f = A(B(f)).
2.2. Dekorator def paytida bajariladi
Dekorator funksiya chaqirilganda emas, aniqlanganda ishlaydi. Modul darajasidagi funksiya uchun bu — modul import qilinganda:
royxat = []
def royxatga_ol(f):
royxat.append(f.__name__) # ⭐ import paytida bajariladi
return f
@royxatga_ol
def hisobot(): ...
print(royxat) # ['hisobot'] — hisobot() hali chaqirilmaganBundan ikki xulosa:
- Ro'yxatga olish (marshrutlar, buyruqlar, plaginlar) — dekoratorning tabiiy vazifasi
- Dekoratorda og'ir ish (tarmoq, fayl, ma'lumotlar bazasi) qilish — importni sekinlashtiradi va import paytidagi xatolarga olib keladi
Klass ichidagi metodlar uchun — klass tanasi bajarilayotganda; funksiya ichidagi funksiya uchun — tashqi funksiya har chaqirilganda.
2.3. Dekorator nima qaytarishi mumkin
Dekorator — shunchaki chaqiriladigan obyekt, natijasi esa istalgan narsa bo'lishi mumkin:
| Qaytaradi | Misol | Natija |
|---|---|---|
| O'sha funksiyani | Ro'yxatga olish, atribut qo'shish | Funksiya o'zgarmaydi |
| Yangi o'rovchi funksiya | Jurnal, vaqt o'lchash, kesh | Xatti-harakat kengayadi |
| Boshqa turdagi obyekt | property, staticmethod, functools.cache |
Nom boshqa narsaga bog'lanadi |
None |
return unutilgan |
Nom None ga bog'lanadi |
Oxirgisi eng keng tarqalgan xato: dekorator return ni unutsa, funksiya import paytida jim yo'qoladi, xato esa birinchi chaqiruvda — 'NoneType' object is not callable bo'lib chiqadi.
2.4. PEP 614 — istalgan ifoda
Python 3.9 dan @ dan keyin istalgan ifoda yozish mumkin (avval faqat nuqtali nom va bitta chaqiruv mumkin edi):
@tugmalar[0].bosilganda
def saqlash(): ...
@(jurnal if DEBUG else lambda f: f)
def hisobla(): ...2.5. O'rovchi funksiya anatomiyasi
def jurnal(funksiya): # 1. def paytida bir marta
def orovchi(*args, **kwargs): # 2. har chaqiruvda
print(f"→ {funksiya.__name__}{args}")
try:
natija = funksiya(*args, **kwargs) # 3. asl funksiya
except Exception as e:
print(f"✗ {type(e).__name__}")
raise # 4. ⭐ xatoni yutmaymiz
print(f"← {natija!r}")
return natija # 5. ⭐ natijani qaytaramiz
return orovchi # 6. ⭐ o'rovchini qaytaramizTo'g'ri o'rovchining besh talabi:
*args, **kwargs— istalgan argumentlarni o'zgartirmay uzatadireturn— asl natijani qaytaradiraise— istisnolarni yutmaydi (ataylab qilinmasa)- Ichki funksiyada
funksiya— closure orqali eslab qolinadi (10.12-dars) - Metama'lumotni ko'chiradi —
functools.wraps(10.11-dars)
Oxirgisisiz nima yo'qoladi:
jurnal_bilan.__name__ # 'orovchi'
jurnal_bilan.__doc__ # None
inspect.signature(...) # (*args, **kwargs)2.6. Bir nechta dekorator — "piyoz" modeli
@A
@B
@C
def f(): ...f = A(B(C(f))) — A eng tashqi qatlam. Chaqiruv qatlamlardan tashqaridan ichkariga o'tadi va natija ichkaridan tashqariga qaytadi:
chaqiruv → A oldin → B oldin → C oldin → f → C keyin → B keyin → A keyin → natijaTartib ma'noni o'zgartiradi:
| Tartib | Ma'nosi |
|---|---|
@qayta_urin ustida @jurnal |
Har bir urinish jurnalga yoziladi |
@jurnal ustida @qayta_urin |
Jurnalda bitta yozuv — hamma urinishlar ichkarida |
@kesh ustida @hisobla |
Faqat kesh o'tkazib yuborgan chaqiruvlar sanaladi |
@hisobla ustida @kesh |
Hamma chaqiruvlar sanaladi |
@faqat_admin ustida @ilova.buyruq |
ro'yxatda tekshiruvsiz funksiya |
@ilova.buyruq ustida @faqat_admin |
ro'yxatda tekshiruvli funksiya |
Qoida: ro'yxatga oluvchi dekorator — eng tashqarida (eng yuqorida), chunki u qolgan hamma qatlamlarni o'z ichiga olgan funksiyani saqlashi kerak.
2.7. Tuzoqlar
A) Rekursiya o'rovchidan o'tadi.
@jurnal
def fib(n):
return n if n < 2 else fib(n - 1) + fib(n - 2)fib nomi ichkarida ham o'rovchiga ishora qiladi (nom global qidiriladi, LEGB). Shuning uchun har bir rekursiv chaqiruv jurnalga yoziladi. Kesh uchun bu foydali (har qadam keshlanadi), jurnal va vaqt o'lchash uchun — shovqin.
B) Generator funksiya.
@vaqt_olch
def satrlar(yol):
with open(yol) as f:
yield from fGenerator funksiya chaqirilganda hech narsa bajarilmaydi — faqat generator obyekti qaytadi (10.3-dars). O'rovchi shu lahzani o'lchaydi — ~0 soniya. Haqiqiy ish keyin, iteratsiyada bo'ladi. Xuddi shu gap try/except li o'rovchiga ham tegishli: generator ichidagi xato o'rovchidan tashqarida ko'tariladi.
C) Metodlar.
self oddiy birinchi argument — *args orqali o'tadi, o'rovchi metodlarda ham ishlaydi. Lekin staticmethod va classmethod bilan tartib muhim:
class K:
@classmethod
@jurnal # ✅ jurnal oddiy funksiyani o'raydi
def yarat(cls): ...
@jurnal
@classmethod # ❌ K.yarat() → TypeError: 'classmethod' object is not callable
def yarat2(cls): ...classmethod, staticmethod, property — deskriptorlar (10.17-dars), ular klassda eng tashqi qatlam bo'lishi kerak. Aks holda ular funksiya ichiga yashirinib qoladi va o'z vazifasini bajarmaydi.
D) Narxi.
*args, **kwargs li o'rovchi har chaqiruvga o'nlab nanosekund qo'shadi (Python 3.14 da ~50 ns). Kichik funksiya siklda millionlab chaqirilsa — seziladi.
2.8. Dekorator qachon to'g'ri vosita
Ko'ndalang vazifalar (cross-cutting concerns) — biznes mantiqqa aloqasi yo'q, lekin ko'p funksiyada takrorlanadigan ishlar:
- jurnal, vaqt o'lchash, metrika
- kesh
- qayta urinish
- huquqlarni tekshirish
- tranzaksiya
- ro'yxatga olish (marshrut, buyruq, plagin, test)
Noto'g'ri qo'llanish:
- funksiyaning asosiy mantiqini dekoratorga yashirish — o'quvchi funksiyaga qarab nima qilishini bilolmaydi
- faqat bitta joyda ishlatiladigan dekorator — oddiy funksiya chaqiruvi aniqroq
- argumentlarni sezilmas tarzda o'zgartiradigan dekorator — imzo va haqiqat mos kelmaydi
3. Tez ma'lumotnoma
Sintaksis
@A
@B
def f(): ... # f = A(B(f))Ifodalar baholanishi: A, B (yuqoridan pastga)
Qo'llanish: B, keyin A (pastdan yuqoriga)
Chaqiruvda: A → B → f → B → A
Qachon: def bajarilganda (modul uchun — importda)O'rovchi shabloni
def dekorator(funksiya):
def orovchi(*args, **kwargs):
# oldin
natija = funksiya(*args, **kwargs)
# keyin
return natija
return orovchiTartib qoidalari
ro'yxatga oluvchi → eng yuqorida
classmethod / staticmethod / property → eng yuqorida (klassda)
kesh ↔ hisoblagich, jurnal ↔ qayta urinish → ma'no o'zgaradiTuzoqlar
return unutildi → nom None ga bog'lanadi
rekursiya → har ichki chaqiruv o'rovchidan o'tadi
generator → o'rovchi faqat yaratilishni ko'radi
metama'lumot → __name__ = 'orovchi' (10.11 da wraps)4. Batafsil misollar
Misol 1 — @ sintaksisini kuzatish
"""Dekorator ifodasi qachon baholanadi, qachon qo'llanadi, nom qachon bog'lanadi."""
def kuzat(yorliq):
"""Kuzatuv uchun dekorator yaratuvchi (argumentli dekoratorlar — 10.9-darsda)."""
print(f" ifoda baholandi: {yorliq}")
def dekorator(funksiya):
print(f" qo'llandi: {yorliq}({funksiya.__name__})")
return funksiya
return dekorator
print("=== 1. Tartib: ifodalar yuqoridan pastga, qo'llanish pastdan yuqoriga ===")
print(" def dan oldin")
@kuzat("A")
@kuzat("B")
@kuzat("C")
def vazifa():
return "bajarildi"
print(f" def dan keyin — vazifa() hali chaqirilmagan, lekin hammasi ishladi")
print("\n=== 2. Dekorator ishlayotganda nom hali bog'lanmagan ===")
def nomni_tekshir(funksiya):
nom = funksiya.__name__
print(f" globals() da {nom!r} bormi: {nom in globals()}")
return funksiya
@nomni_tekshir
def yangi_funksiya():
pass
print(f" def dan keyin: {'yangi_funksiya' in globals()}")
def yangi_funksiya_2():
pass
print(" qo'lda yozilgan shaklda esa:")
yangi_funksiya_2 = nomni_tekshir(yangi_funksiya_2)
print("\n=== 3. Dekorator istalgan narsa qaytarishi mumkin ===")
royxat = []
def royxatga_ol(funksiya):
royxat.append(funksiya.__name__)
return funksiya # o'sha funksiya
def ikki_marta(funksiya):
def orovchi(*args, **kwargs):
funksiya(*args, **kwargs)
return funksiya(*args, **kwargs)
return orovchi # yangi funksiya
@royxatga_ol
def salomlash(ism):
print(f" salom, {ism}")
return ism.upper()
@ikki_marta
def chiq(matn):
print(f" {matn}")
@(lambda funksiya: funksiya()) # darhol chaqirib, natijasini saqlaydi
def SOZLAMA():
return {"til": "uz", "sahifa": 20}
print(f" royxat: {royxat}, salomlash turi: {type(salomlash).__name__}")
chiq("ikki marta chiqadi")
print(f" SOZLAMA endi funksiya emas: {type(SOZLAMA).__name__} = {SOZLAMA}")
print("\n=== 4. ⚠️ return unutilgan dekorator ===")
def yarim_dekorator(funksiya):
funksiya.tekshirildi = True # return yo'q
@yarim_dekorator
def muhim_hisob():
return 42
print(f" import paytida xato yo'q, muhim_hisob = {muhim_hisob}")
try:
muhim_hisob()
except TypeError as e:
print(f" birinchi chaqiruvda: TypeError: {e}")
print("\n=== 5. PEP 614: istalgan ifoda ===")
class Tugma:
def __init__(self, nom):
self.nom = nom
self.ishlovchilar = []
def bosilganda(self, funksiya):
self.ishlovchilar.append(funksiya)
return funksiya
def bos(self):
for f in self.ishlovchilar:
f(self.nom)
tugmalar = [Tugma("Saqlash"), Tugma("Chiqish")]
DEBUG = False
@tugmalar[0].bosilganda
def saqla(nom):
print(f" '{nom}' bosildi — saqlanmoqda")
@(ikki_marta if DEBUG else lambda f: f)
def hisobla():
print(" hisoblandi")
tugmalar[0].bos()
hisobla()Natijaning muhim qismi:
=== 1. Tartib: ifodalar yuqoridan pastga, qo'llanish pastdan yuqoriga ===
def dan oldin
ifoda baholandi: A
ifoda baholandi: B
ifoda baholandi: C
qo'llandi: C(vazifa)
qo'llandi: B(vazifa)
qo'llandi: A(vazifa)
def dan keyin — vazifa() hali chaqirilmagan, lekin hammasi ishladi
=== 2. Dekorator ishlayotganda nom hali bog'lanmagan ===
globals() da 'yangi_funksiya' bormi: False
def dan keyin: True
qo'lda yozilgan shaklda esa:
globals() da 'yangi_funksiya_2' bormi: True
=== 3. Dekorator istalgan narsa qaytarishi mumkin ===
royxat: ['salomlash'], salomlash turi: function
ikki marta chiqadi
ikki marta chiqadi
SOZLAMA endi funksiya emas: dict = {'til': 'uz', 'sahifa': 20}
=== 4. ⚠️ return unutilgan dekorator ===
import paytida xato yo'q, muhim_hisob = None
birinchi chaqiruvda: TypeError: 'NoneType' object is not callable
=== 5. PEP 614: istalgan ifoda ===
'Saqlash' bosildi — saqlanmoqda
hisoblandiNima ko'rsatdi: 2.1, 2.2, 2.3, 2.4-bo'limlar.
Misol 2 — O'rovchi anatomiyasi va tuzoqlar
"""To'g'ri o'rovchi, nima yo'qoladi, rekursiya, generator va metodlar."""
import inspect
chuqurlik = 0
def jurnal(funksiya):
def orovchi(*args, **kwargs):
global chuqurlik
belgi = " " * chuqurlik
qism = [repr(a) for a in args] + [f"{k}={v!r}" for k, v in kwargs.items()]
print(f" {belgi}→ {funksiya.__name__}({', '.join(qism)})")
chuqurlik += 1
try:
natija = funksiya(*args, **kwargs)
except Exception as e:
print(f" {belgi}✗ {type(e).__name__}: {e}")
raise
finally:
chuqurlik -= 1
print(f" {belgi}← {natija!r}")
return natija
return orovchi
print("=== 1. Argumentlar, natija va istisno o'zgarmay o'tadi ===")
@jurnal
def bol(a, b, *, butun=False):
"""a ni b ga bo'ladi."""
return a // b if butun else a / b
bol(7, 2)
bol(7, 2, butun=True)
try:
bol(1, 0)
except ZeroDivisionError:
print(" istisno tashqarida ushlandi ✅")
print("\n=== 2. Nima yo'qoldi (10.11-darsda tuzatamiz) ===")
print(f" __name__: {bol.__name__!r}")
print(f" __doc__: {bol.__doc__!r}")
print(f" imzo: {inspect.signature(bol)}")
print("\n=== 3. ⚠️ Rekursiya o'rovchidan o'tadi ===")
@jurnal
def fib(n):
return n if n < 2 else fib(n - 1) + fib(n - 2)
fib(3)
sanoq = {"jami": 0}
def hisobla(funksiya):
def orovchi(*args):
sanoq["jami"] += 1
return funksiya(*args)
return orovchi
@hisobla
def fib2(n):
return n if n < 2 else fib2(n - 1) + fib2(n - 2)
fib2(20)
print(f" fib2(20): o'rovchi {sanoq['jami']:,} marta chaqirildi — har rekursiv qadam")
print("\n=== 4. ⚠️ Generator funksiya: o'rovchi faqat yaratilishni ko'radi ===")
def bosqich(funksiya):
def orovchi(*args, **kwargs):
print(" [o'rovchi] boshlandi")
try:
return funksiya(*args, **kwargs)
except ValueError:
print(" [o'rovchi] ValueError ushlandi")
finally:
print(" [o'rovchi] tugadi")
return orovchi
@bosqich
def sonlar(qatorlar):
for q in qatorlar:
print(f" [generator] {q!r} o'qilmoqda")
yield int(q)
gen = sonlar(["1", "2", "uch"])
print(f" qaytgan: {type(gen).__name__}")
try:
for x in gen:
print(f" olindi: {x}")
except ValueError as e:
print(f" ⚠️ ValueError o'rovchidan tashqarida: {e}")
print("\n=== 5. Metodlar, staticmethod va classmethod ===")
def oddiy(funksiya):
def orovchi(*args, **kwargs):
return funksiya(*args, **kwargs)
return orovchi
class Hisob:
def __init__(self, balans):
self.balans = balans
@oddiy
def qoldiq(self):
return self.balans
@classmethod
@oddiy
def bosh(cls):
return cls(0)
@oddiy
@classmethod
def xato_tartib(cls):
return cls(0)
@staticmethod
@oddiy
def valyuta():
return "UZS"
@oddiy
@staticmethod
def xato_statik():
return "UZS"
h = Hisob(500)
print(f" oddiy metod, self *args orqali: {h.qoldiq()}")
print(f" @classmethod @oddiy: {type(Hisob.bosh()).__name__}")
print(f" @staticmethod @oddiy: {Hisob.valyuta()}, nusxadan: {h.valyuta()}")
for nom, chaqir in [("@oddiy @classmethod", lambda: Hisob.xato_tartib()),
("@oddiy @staticmethod (nusxadan)", lambda: h.xato_statik())]:
try:
chaqir()
except TypeError as e:
print(f" ❌ {nom}: TypeError: {e}")
print(f" @oddiy @staticmethod (klassdan): {Hisob.xato_statik()} ⚠️ faqat klassdan ishlaydi")Natijaning muhim qismi:
=== 1. Argumentlar, natija va istisno o'zgarmay o'tadi ===
→ bol(7, 2)
← 3.5
→ bol(7, 2, butun=True)
← 3
→ bol(1, 0)
✗ ZeroDivisionError: division by zero
istisno tashqarida ushlandi ✅
=== 2. Nima yo'qoldi (10.11-darsda tuzatamiz) ===
__name__: 'orovchi'
__doc__: None
imzo: (*args, **kwargs)
=== 3. ⚠️ Rekursiya o'rovchidan o'tadi ===
→ fib(3)
→ fib(2)
→ fib(1)
← 1
→ fib(0)
← 0
← 1
→ fib(1)
← 1
← 2
fib2(20): o'rovchi 21,891 marta chaqirildi — har rekursiv qadam
=== 4. ⚠️ Generator funksiya: o'rovchi faqat yaratilishni ko'radi ===
[o'rovchi] boshlandi
[o'rovchi] tugadi
qaytgan: generator
[generator] '1' o'qilmoqda
olindi: 1
[generator] '2' o'qilmoqda
olindi: 2
[generator] 'uch' o'qilmoqda
⚠️ ValueError o'rovchidan tashqarida: invalid literal for int() with base 10: 'uch'
=== 5. Metodlar, staticmethod va classmethod ===
oddiy metod, self *args orqali: 500
@classmethod @oddiy: Hisob
@staticmethod @oddiy: UZS, nusxadan: UZS
❌ @oddiy @classmethod: TypeError: 'classmethod' object is not callable
❌ @oddiy @staticmethod (nusxadan): TypeError: Hisob.xato_statik() takes 0 positional arguments but 1 was given
@oddiy @staticmethod (klassdan): UZS ⚠️ faqat klassdan ishlaydiNima ko'rsatdi: 2.5, 2.7-bo'limlar.
Misol 3 — Piyoz: qatlamlar tartibi ma'noni o'zgartiradi
"""Bir xil dekoratorlar, boshqa tartib — boshqa natija."""
import random
def qatlam(nom):
"""Kuzatuv uchun: oldin va keyin chop etadigan dekorator yaratadi."""
def dekorator(funksiya):
def orovchi(*args, **kwargs):
print(f" {nom} oldin")
natija = funksiya(*args, **kwargs)
print(f" {nom} keyin")
return natija
return orovchi
return dekorator
print("=== 1. Chaqiruv qatlamlardan qanday o'tadi ===")
@qatlam("A")
@qatlam("B")
@qatlam("C")
def yadro():
print(" >>> yadro <<<")
yadro()
jurnal_yozuvlari = []
def jurnal(funksiya):
def orovchi(*args, **kwargs):
try:
natija = funksiya(*args, **kwargs)
jurnal_yozuvlari.append(f"{funksiya.__name__}: ok")
return natija
except Exception as e:
jurnal_yozuvlari.append(f"{funksiya.__name__}: {type(e).__name__}")
raise
return orovchi
def qayta_urin(funksiya):
def orovchi(*args, **kwargs):
for urinish in range(1, 4):
try:
return funksiya(*args, **kwargs)
except ConnectionError:
if urinish == 3:
raise
return orovchi
def beqaror_tarmoq():
rng = random.Random(7) # har chaqiruvda bir xil ketma-ketlik
def sorov():
if rng.random() < 0.6:
raise ConnectionError("uzildi")
return "javob"
sorov.__name__ = "sorov"
return sorov
print("\n=== 2. jurnal va qayta_urin ===")
a = jurnal(qayta_urin(beqaror_tarmoq())) # @jurnal @qayta_urin
b = qayta_urin(jurnal(beqaror_tarmoq())) # @qayta_urin @jurnal
for nom, f in [("@jurnal ustida @qayta_urin", a), ("@qayta_urin ustida @jurnal", b)]:
jurnal_yozuvlari.clear()
try:
natija = f()
except ConnectionError as e:
natija = f"ConnectionError: {e}"
print(f" {nom}: {natija}")
print(f" jurnal: {jurnal_yozuvlari}")
print("\n=== 3. kesh va hisoblagich ===")
def kesh(funksiya):
saqlangan = {}
def orovchi(n):
if n not in saqlangan:
saqlangan[n] = funksiya(n)
return saqlangan[n]
return orovchi
def hisoblagich(funksiya):
def orovchi(*args):
orovchi.soni += 1
return funksiya(*args)
orovchi.soni = 0
return orovchi
def kvadrat(n):
return n * n
tashqi_hisob = hisoblagich(kesh(kvadrat)) # @hisoblagich @kesh
ichki_hisob_orovchi = hisoblagich(kvadrat)
ichki_hisob = kesh(ichki_hisob_orovchi) # @kesh @hisoblagich
for n in [3, 3, 4, 3, 4, 5]:
tashqi_hisob(n)
ichki_hisob(n)
print(f" @hisoblagich ustida @kesh: {tashqi_hisob.soni} — barcha so'rovlar")
print(f" @kesh ustida @hisoblagich: {ichki_hisob_orovchi.soni} — faqat haqiqiy hisoblashlar")
print("\n=== 4. Tekshiruv va o'zgartirish tartibi ===")
def musbat_bolsin(funksiya):
def orovchi(x):
if x <= 0:
raise ValueError(f"musbat bo'lishi kerak: {x}")
return funksiya(x)
return orovchi
def modul_ol(funksiya):
def orovchi(x):
return funksiya(abs(x))
return orovchi
def ildiz(x):
return x ** 0.5
tartib1 = musbat_bolsin(modul_ol(ildiz)) # avval tekshiradi, keyin abs
tartib2 = modul_ol(musbat_bolsin(ildiz)) # avval abs, keyin tekshiradi
for nom, f in [("@musbat_bolsin @modul_ol", tartib1), ("@modul_ol @musbat_bolsin", tartib2)]:
try:
print(f" {nom}: f(-9) = {f(-9)}")
except ValueError as e:
print(f" {nom}: ValueError: {e}")Natijaning muhim qismi:
=== 1. Chaqiruv qatlamlardan qanday o'tadi ===
A oldin
B oldin
C oldin
>>> yadro <<<
C keyin
B keyin
A keyin
=== 2. jurnal va qayta_urin ===
@jurnal ustida @qayta_urin: javob
jurnal: ['orovchi: ok']
@qayta_urin ustida @jurnal: javob
jurnal: ['sorov: ConnectionError', 'sorov: ConnectionError', 'sorov: ok']
=== 3. kesh va hisoblagich ===
@hisoblagich ustida @kesh: 6 — barcha so'rovlar
@kesh ustida @hisoblagich: 3 — faqat haqiqiy hisoblashlar
=== 4. Tekshiruv va o'zgartirish tartibi ===
@musbat_bolsin @modul_ol: ValueError: musbat bo'lishi kerak: -9
@modul_ol @musbat_bolsin: f(-9) = 3.0Nima ko'rsatdi: 2.6-bo'lim — f = A(B(C(f))), chaqiruv tashqaridan ichkariga. 2-bo'limdagi 'orovchi: ok' yozuviga e'tibor bering: tashqi jurnal ichki dekoratorning o'rovchisini ko'rdi va asl nom yo'qoldi — 10.11-darsdagi functools.wraps aynan shu uchun.
Misol 4 — Amaliy: buyruqlar tizimi va ro'yxatga olish tuzog'i
Kichik bank konsol ilovasi. Buyruqlar dekorator bilan ro'yxatga olinadi, huquq tekshiruvi, tranzaksiya va audit — alohida dekoratorlar.
"""Ro'yxatga olish, huquq, tranzaksiya va audit dekoratorlari; noto'g'ri tartib xavfsizlik teshigini ochadi."""
import copy
class Ilova:
def __init__(self):
self.buyruqlar = {}
self.foydalanuvchi = None
self.hisoblar = {"Aziz": 1000, "Bobur": 200}
self.audit = []
def buyruq(self, funksiya):
"""Ro'yxatga oluvchi dekorator: nom — funksiya nomi."""
self.buyruqlar[funksiya.__name__.replace("_", "-")] = funksiya
return funksiya
def bajar(self, foydalanuvchi, qator):
self.foydalanuvchi = foydalanuvchi
nom, *argumentlar = qator.split()
try:
natija = self.buyruqlar[nom](self, *argumentlar)
return f"✅ {natija}"
except KeyError:
return f"❓ noma'lum buyruq: {nom}"
except (PermissionError, ValueError) as e:
return f"❌ {type(e).__name__}: {e}"
ADMINLAR = {"admin"}
def faqat_admin(funksiya):
def orovchi(ilova, *args):
if ilova.foydalanuvchi not in ADMINLAR:
raise PermissionError(f"{ilova.foydalanuvchi} admin emas")
return funksiya(ilova, *args)
orovchi.__name__ = funksiya.__name__ # nomni qo'lda saqlaymiz (10.11 da wraps)
return orovchi
def tranzaksiyali(funksiya):
def orovchi(ilova, *args):
nusxa = copy.deepcopy(ilova.hisoblar)
try:
return funksiya(ilova, *args)
except Exception:
ilova.hisoblar = nusxa
raise
orovchi.__name__ = funksiya.__name__
return orovchi
def audit(funksiya):
def orovchi(ilova, *args):
try:
natija = funksiya(ilova, *args)
ilova.audit.append((ilova.foydalanuvchi, funksiya.__name__, "ok"))
return natija
except Exception as e:
ilova.audit.append((ilova.foydalanuvchi, funksiya.__name__, type(e).__name__))
raise
orovchi.__name__ = funksiya.__name__
return orovchi
ilova = Ilova()
@ilova.buyruq
def balans(ilova, ism):
return f"{ism}: {ilova.hisoblar[ism]:,}"
@ilova.buyruq
@audit
@tranzaksiyali
def otkaz(ilova, kimdan, kimga, summa):
summa = int(summa)
ilova.hisoblar[kimdan] -= summa
ilova.hisoblar[kimga] = ilova.hisoblar.get(kimga, 0) + summa
if ilova.hisoblar[kimdan] < 0:
raise ValueError(f"{kimdan} hisobida mablag' yetarli emas")
return f"{kimdan} → {kimga}: {summa:,}"
@ilova.buyruq # ✅ eng yuqorida
@audit
@faqat_admin
def hisob_ochir(ilova, ism):
del ilova.hisoblar[ism]
return f"{ism} hisobi o'chirildi"
@faqat_admin # ❌ ro'yxatga oluvchidan yuqorida
@ilova.buyruq
def hammasini_nolla(ilova):
for ism in ilova.hisoblar:
ilova.hisoblar[ism] = 0
return "barcha hisoblar nollandi"
print("=== 1. Ro'yxatga olingan buyruqlar (import paytida) ===")
print(f" {list(ilova.buyruqlar)}")
print("\n=== 2. Oddiy foydalanuvchi ===")
for qator in ["balans Aziz", "otkaz Aziz Bobur 300", "otkaz Bobur Aziz 9000",
"hisob-ochir Bobur", "kredit Aziz"]:
print(f" aziz> {qator:24} {ilova.bajar('aziz', qator)}")
print(f" hisoblar: {ilova.hisoblar} ⭐ muvaffaqiyatsiz o'tkazma orqaga qaytarildi")
print("\n=== 3. ❌ Tartib xatosi: tekshiruv chetlab o'tildi ===")
print(f" aziz> hammasini-nolla {ilova.bajar('aziz', 'hammasini-nolla')}")
print(f" hisoblar: {ilova.hisoblar}")
try:
ilova.foydalanuvchi = "aziz"
hammasini_nolla(ilova)
except PermissionError as e:
print(f" to'g'ridan-to'g'ri chaqiruvda esa tekshiruv bor: PermissionError: {e}")
print(f" ro'yxatdagi funksiya o'ralganmi: {ilova.buyruqlar['hammasini-nolla'] is not hammasini_nolla}")
print("\n=== 4. Admin ===")
ilova.hisoblar = {"Aziz": 700, "Bobur": 500}
print(f" admin> hisob-ochir Bobur {ilova.bajar('admin', 'hisob-ochir Bobur')}")
print(f" hisoblar: {ilova.hisoblar}")
print("\n=== 5. Audit jurnali ===")
for yozuv in ilova.audit:
print(f" {yozuv}")Natijaning muhim qismi:
=== 1. Ro'yxatga olingan buyruqlar (import paytida) ===
['balans', 'otkaz', 'hisob-ochir', 'hammasini-nolla']
=== 2. Oddiy foydalanuvchi ===
aziz> balans Aziz ✅ Aziz: 1,000
aziz> otkaz Aziz Bobur 300 ✅ Aziz → Bobur: 300
aziz> otkaz Bobur Aziz 9000 ❌ ValueError: Bobur hisobida mablag' yetarli emas
aziz> hisob-ochir Bobur ❌ PermissionError: aziz admin emas
aziz> kredit Aziz ❓ noma'lum buyruq: kredit
hisoblar: {'Aziz': 700, 'Bobur': 500} ⭐ muvaffaqiyatsiz o'tkazma orqaga qaytarildi
=== 3. ❌ Tartib xatosi: tekshiruv chetlab o'tildi ===
aziz> hammasini-nolla ✅ barcha hisoblar nollandi
hisoblar: {'Aziz': 0, 'Bobur': 0}
to'g'ridan-to'g'ri chaqiruvda esa tekshiruv bor: PermissionError: aziz admin emas
ro'yxatdagi funksiya o'ralganmi: True
=== 4. Admin ===
admin> hisob-ochir Bobur ✅ Bobur hisobi o'chirildi
hisoblar: {'Aziz': 700}
=== 5. Audit jurnali ===
('aziz', 'otkaz', 'ok')
('aziz', 'otkaz', 'ValueError')
('aziz', 'hisob_ochir', 'PermissionError')
('admin', 'hisob_ochir', 'ok')Nima ko'rsatdi: 2.2, 2.6-bo'limlar — ro'yxatga olish import paytida bo'ladi va u o'zidan pastdagi hamma narsani saqlaydi; tartib xatosi testda sezilmasligi mumkin, chunki funksiyani to'g'ridan-to'g'ri chaqirganda tekshiruv ishlaydi.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Dekorator funksiya chaqirilganda ishlaydi" | def bajarilganda — modul uchun import paytida |
"@A @B def f — avval A qo'llanadi" |
f = A(B(f)): avval B, keyin A |
| "Dekorator ichida funksiyani nomi bilan chaqirish mumkin" | Nom hali bog'lanmagan |
| "Dekorator albatta funksiya qaytaradi" | Istalgan obyekt — hatto None (xato) |
"@ dan keyin faqat nom yoziladi" |
3.9 dan istalgan ifoda |
| "Rekursiv funksiyaning ichki chaqiruvlari o'rovchini chetlab o'tadi" | Nom orqali chaqirilgani uchun o'rovchidan o'tadi |
| "Generator funksiyaning vaqtini o'rovchi o'lchaydi" | Faqat generator yaratilishini |
| "Dekoratorlar tartibi faqat uslub masalasi" | Ma'noni, hatto xavfsizlikni o'zgartiradi |
6. Keng tarqalgan xatolar va yechimlari
1. return orovchi ni unutish
def jurnal(f):
def orovchi(*args, **kwargs):
return f(*args, **kwargs)
# ❌ f endi None
def jurnal(f):
def orovchi(*args, **kwargs):
return f(*args, **kwargs)
return orovchi # ✅2. O'rovchida return ni unutish
def orovchi(*args, **kwargs):
print("chaqirildi")
f(*args, **kwargs) # ❌ natija yo'qoldi — har doim None
def orovchi(*args, **kwargs):
print("chaqirildi")
return f(*args, **kwargs) # ✅3. Dekoratorni chaqirib qo'yish
@jurnal() # ❌ TypeError: jurnal() missing 1 required positional argument
def f(): ...
@jurnal # ✅ argumentsiz dekorator qavssiz
def f(): ...4. Ro'yxatga oluvchi dekorator pastda
@faqat_admin
@app.route("/ochir") # ❌ marshrut tekshiruvsiz funksiyani saqladi
def ochir(): ...
@app.route("/ochir") # ✅ eng yuqorida
@faqat_admin
def ochir(): ...5. classmethod/staticmethod/property ni ichkariga qo'yish
@jurnal
@classmethod # ❌ 'classmethod' object is not callable
def yarat(cls): ...
@classmethod # ✅
@jurnal
def yarat(cls): ...6. Generator funksiyaga oddiy o'rovchi
@vaqt_olch
def oqi(yol):
yield from open(yol) # ❌ o'lchov ~0 s — faqat yaratilish
@vaqt_olch
def oqi_hammasi(yol):
return list(oqi(yol)) # ✅ haqiqiy ishni bajaradigan funksiyani o'lchangGeneratorlar uchun alohida dekorator yozish ham mumkin — o'rovchining o'zi generator bo'lib, yield from qiladi.
7. Dekoratorda import paytida og'ir ish
def ulanishli(f):
db = connect("postgres://...") # ❌ har importda ulanish, test muhitida yiqiladi
def orovchi(*args):
return f(db, *args)
return orovchiResursni o'rovchi ichida, birinchi chaqiruvda oling.
8. Rekursiv funksiyaga shovqinli dekorator
@jurnal
def fib(n): ... # ❌ fib(30) — 2.7 mln jurnal yozuvi
def fib(n): ...
@jurnal
def fib_hisobla(n): # ✅ faqat tashqi chaqiruv
return fib(n)7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 7.11-dars (o'tilgan): funksiya — obyekt, yuqori tartibli funksiyalar
- 8.15–8.17-darslar (o'tilgan):
@property,@classmethod,@dataclass— dekoratorlar - 10.3-dars (o'tilgan): generator chaqirilganda tana bajarilmaydi
- 10.9–10.13-darslar: argumentli va klass dekoratorlari,
wraps, closure,functools.cache - 10.17-dars: deskriptorlar —
classmethodvapropertynega tashqarida bo'lishi kerak - 17-qism:
pytest.mark,pytest.fixture,mock.patch— dekoratorlar - 20, 22-qismlar: FastAPI va Flask marshrutlari,
Depends— ro'yxatga olish dekoratorlari
8. Eng yaxshi amaliyotlar
O'rovchi argumentlarni
*args, **kwargsbilan o'zgartirmay uzatsin, natijani qaytarsin, istisnoni yutmasin.Ro'yxatga oluvchi dekoratorlar va deskriptorlar (
classmethod,property) — eng yuqorida.Tartibni ongli tanlang va izohlang — kesh/hisoblagich, jurnal/qayta urinish, tekshiruv/o'zgartirish.
Dekorator import paytida yengil bo'lsin. Og'ir ishni o'rovchi ichida qiling.
Generator va rekursiv funksiyalarni o'rashdan oldin o'ylang.
Dekorator — ko'ndalang vazifa uchun. Asosiy mantiqni yashirmang.
Metama'lumotni saqlang —
functools.wraps(10.11-dars).Muhim dekoratorlarni ro'yxatdan chaqirib test qiling, faqat to'g'ridan-to'g'ri emas — tartib xatolari shunda ko'rinadi.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
def d(nom):
def dek(f):
def w(*a):
print(nom, end=" ")
return f(*a)
return w
return dek
def ikki(f):
def w(*a):
return f(*a) * 2
return w
1. @d("A")
@d("B")
def f():
print("f", end=" ")
f()
print()
2. @ikki
@ikki
def g():
return 3
print(g())
3. def yoz(f):
print("yoz", end=" ")
return f
print("oldin", end=" ")
@yoz
def h(): pass
print("keyin")
4. def unut(f):
f.x = 1
@unut
def k(): pass
print(k)
5. @ikki
def r(n):
return 1 if n == 0 else r(n - 1)
print(r(2))
6. @ikki
def s():
return "ab"
print(s(), s.__name__)
7. @(lambda f: f())
def t():
return [1, 2]
print(t)
8. @ikki
def gen():
yield 1
try:
gen()
except TypeError as e:
print("TypeError")
9. class K:
@classmethod
@ikki
def m(cls):
return 5
print(K.m())
10. class K:
@ikki
@staticmethod
def m():
return 5
print(K.m())
11. royxat = []
def ol(f):
royxat.append(f)
return f
@ikki
@ol
def u():
return 1
print(u(), royxat[0]())
12. @d("X")
def v(n):
return n
print(v(4))Javoblar
A B f—Aeng tashqi qatlam, birinchi ishlaydi12—3 * 2 * 2oldin yoz keyin— dekoratordefpaytida ishladiNone— dekoratorreturnqilmadi8— har rekursiv chaqiruv ham o'rovchidan o'tadi:1 → 2 → 4 → 8abab w— satr ikkiga ko'paytirildi, nom esaw[1, 2]—tfunksiya emas, natijaTypeError— generator obyektini* 2qilib bo'lmaydi10— to'g'ri tartib10—staticmethod3.10 dan chaqiriladigan, klassdan chaqirganda ishlaydi2 1— ro'yxatda o'ralmagan funksiyaX 4—printichida avvalv(4)hisoblanadi vaXchiqadi
Vazifa 2: Xatolarni tuzating
1. def vaqt(f):
def orovchi(*args, **kwargs):
bosh = time.perf_counter()
f(*args, **kwargs)
print(time.perf_counter() - bosh)
return orovchi
2. def jim(f):
def orovchi(*args):
try:
return f(*args)
except Exception:
print("xato")
return orovchi
3. class Foydalanuvchi:
@jurnal
@property
def ism(self):
return self._ism
4. @login_kerak
@app.route("/profil")
def profil(): ...
5. @kesh
def satrlar(yol):
with open(yol) as f:
for q in f:
yield q.strip()Javoblar
1. def vaqt(f):
def orovchi(*args, **kwargs):
bosh = time.perf_counter()
try:
return f(*args, **kwargs) # natija qaytariladi
finally:
print(time.perf_counter() - bosh) # xato bo'lsa ham o'lchaydi
return orovchi
2. def jim(f):
def orovchi(*args):
try:
return f(*args)
except Exception:
print("xato")
raise # yutish ataylab bo'lmasa — qayta ko'tarish
return orovchi
3. class Foydalanuvchi:
@property # deskriptor eng tashqarida
@jurnal
def ism(self):
return self._ism
4. @app.route("/profil") # ro'yxatga oluvchi eng tashqarida
@login_kerak
def profil(): ...
5. # Generator obyektini keshlash ma'nosiz — birinchi iteratsiyadan keyin u bo'sh.
@kesh
def satrlar(yol):
with open(yol) as f:
return tuple(q.strip() for q in f) # o'zgarmas natijani keshlaymizVazifa 3: Uch dekorator
Argumentsiz dekoratorlar yozing:
@hisobla— chaqiruvlar soninif.soniatributida saqlaydi@vaqt_olch— davomiyliknif.oxirgi_vaqtga yozadi, istisno bo'lsa ham@bir_marta— funksiyani faqat birinchi chaqiruvda bajaradi, keyin o'sha natijani qaytaradi; birinchi chaqiruv xato bersa, keyingi chaqiruv yana urinib ko'rsin
Har biri uchun: oddiy funksiya, metod va rekursiv funksiyada test yozing. Rekursiyada nima bo'lishini izohlang.
Vazifa 4: Generatorga mos dekorator
@generator_vaqti yozing:
- O'ralgan funksiya generator funksiya bo'lsa (
inspect.isgeneratorfunction), o'rovchi ham generator bo'lsin va butun iteratsiya vaqtini o'lchasin - Iteratsiya oxirigacha bormasa (
break) ham o'lchov yozilsin - Oddiy funksiyalar uchun oddiy o'lchov
- Ichki generatorga
send()vathrow()shaffof o'tsin (10.5-dars)
Vazifa 5: Marshrutizator
Marshrutizator sinfini yozing:
@r.getva@r.post— funksiya nomidan yo'l yasaydi (profil_korish→/profil/korish)r.chaqir("GET", "/profil/korish", **params)- Ro'yxatga olishda bir xil yo'l ikki marta kelsa — import paytida
ValueError @login_kerakdekoratori va uni noto'g'ri tartibda qo'llashni aniqlaydigan tekshiruv:r.tekshir()— ro'yxatdagi funksiya bilan moduldagi nom bir xil obyekt bo'lmasa, ogohlantirish chiqarsin
Vazifa 6: Dekoratorlar tartibi testi
@jurnal, @kesh, @qayta_urin, @huquq dekoratorlarining 24 xil tartibini avtomatik ishga tushiradigan dastur yozing. Har tartib uchun: jurnal yozuvlari soni, haqiqiy chaqiruvlar soni, huquqsiz foydalanuvchi keshdan natija ola oladimi. Natijani jadval qilib chiqaring va qaysi tartiblar xavfli ekanini aniqlang.
Vazifa 7: O'ylash
Python dekoratorlari, Java annotatsiyalari (@Override, Spring @Transactional), C# atributlari va TypeScript dekoratorlari — hammasi @ belgisi yoki shunga o'xshash sintaksisdan foydalanadi. Ular bir xil narsami? Spring'dagi "this.metod() chaqirilganda @Transactional ishlamaydi" muammosi Python'da bormi?
Javob
Qisqa javob: yo'q. Python dekoratori — def paytida bajariladigan kod, u nomni boshqa obyektga qayta bog'laydi. Java annotatsiyasi va C# atributi — o'zicha hech narsa qilmaydigan metama'lumot; xatti-harakatni ularni o'qiydigan kompilyator yoki framework qo'shadi. TypeScript dekoratorlari Python'ga yaqin — ular ham funksiya — lekin faqat klasslar va ularning a'zolariga qo'llanadi. Spring muammosining sababi proksi, Python'da esa aksincha: nom orqali qilingan ichki chaqiruv ham o'rovchidan o'tadi.
1. Python — bajariladigan qayta bog'lash
@jurnal
def f(): ...
# f = jurnal(f)- dekorator — oddiy chaqiriladigan obyekt,
defpaytida chaqiriladi - natija nomga bog'lanadi — asl funksiya o'rovchi ichida yashirinadi
- modul funksiyalari, metodlar va klasslarga qo'llanadi
- hech qanday framework kerak emas
2. Java annotatsiyalari — metama'lumot
@Override
public String toString() { ... }
@Transactional
public void otkaz(...) { ... }Annotatsiya klass fayliga yoziladigan belgi. @Override ni kompilyator tekshiradi. @Transactional ni esa Spring ishga tushirish paytida refleksiya bilan o'qiydi va obyekt atrofida proksi yaratadi: tashqaridan chaqirilganda proksi tranzaksiyani ochadi, keyin haqiqiy metodni chaqiradi. Spring bo'lmasa, @Transactional hech narsa qilmaydi.
3. C# atributlari
Java annotatsiyalariga o'xshaydi: [Obsolete], [HttpGet("/profil")]. Ular metama'lumot; ASP.NET kabi framework'lar refleksiya bilan o'qib, marshrutlar yoki filtrlar quradi.
4. TypeScript dekoratorlari
TypeScript 5.0 dan boshlab standart (TC39) dekoratorlar qo'llab-quvvatlanadi. Ular Python'ga yaqin: dekorator — klass aniqlanayotganda chaqiriladigan funksiya, metodni almashtirishi mumkin. Farqi: ular faqat klass va klass a'zolariga qo'llanadi — oddiy funksiyaga emas.
5. Spring o'z-o'zini chaqirish muammosi
@Service
class HisobXizmati {
public void ommaviyOtkaz(List<Otkaz> otkazlar) {
for (var o : otkazlar) this.otkaz(o); // ⚠️ proksini chetlab o'tadi
}
@Transactional
public void otkaz(Otkaz o) { ... }
}Tashqi kod xizmat.otkaz(o) ni chaqirsa, u proksi orqali o'tadi — tranzaksiya bor. this.otkaz(o) esa proksini emas, haqiqiy obyektni chaqiradi — tranzaksiya yo'q. Chunki xatti-harakat obyekt atrofiga qo'shilgan, metodning o'ziga emas.
6. Python'da bu muammo yo'q — lekin teskarisi bor
class HisobXizmati:
def ommaviy_otkaz(self, otkazlar):
for o in otkazlar:
self.otkaz(o) # ✅ o'rovchidan o'tadi
@tranzaksiyali
def otkaz(self, o): ...Python'da dekorator klass atributining o'zini almashtiradi — HisobXizmati.otkaz endi o'rovchi. self.otkaz ham, tashqi chaqiruv ham shu o'rovchiga boradi.
Teskari tomoni — Misol 2 dagi rekursiya: ichki chaqiruvni chetlab o'tish kerak bo'lsa (masalan, jurnal shovqinini oldini olish), buni ataylab qilish kerak — ichki yordamchi funksiya yoki __wrapped__ (10.11-dars) orqali.
7. Taqqoslash
| Python | Java/C# | TypeScript | |
|---|---|---|---|
| Tabiati | Bajariladigan kod | Metama'lumot | Bajariladigan kod |
| Kim ishga tushiradi | Til, def paytida |
Kompilyator yoki framework | Til, klass aniqlanganda |
| Nimaga qo'llanadi | Funksiya, metod, klass | Klass, metod, maydon, parametr... | Klass va a'zolari |
| O'z-o'zini chaqirish | O'rovchidan o'tadi | Proksi bo'lsa — chetlab o'tadi | Metod almashtirilsa — o'tadi |
| Framework'siz foyda | To'liq | Faqat kompilyator tekshiruvlari | To'liq |
8. Amaliy xulosa
Python dasturchisi @ ni ko'rganda "bu yerda nom qayta bog'landi" deb o'ylashi kerak — bu kod va uni o'qib, qadamma-qadam kuzatish mumkin. Java/Spring dan kelgan dasturchi uchun muhim farq: Python'da "sehrli" framework yo'q, proksi ham yo'q — lekin tartib, rekursiya va import vaqti kabi masalalar bevosita dasturchi zimmasida.
9. Xulosa
- Python dekoratori —
defpaytida bajariladigan chaqiruv va nomni qayta bog'lash - Java/C# annotatsiyalari — metama'lumot; xatti-harakatni framework proksi yoki kod generatsiyasi bilan qo'shadi
- TypeScript dekoratorlari tabiatan Python'ga yaqin, lekin faqat klasslar uchun
- Spring'ning o'z-o'zini chaqirish muammosi proksidan kelib chiqadi; Python'da nom orqali chaqiruv doim o'rovchidan o'tadi
- Shuning uchun Python'da ichki chaqiruvlarni chetlab o'tish — ataylab qilinadigan ish
Nimani mustahkamlaydi: 2.1–2.8-bo'limlar.
Xulosa
Bu darsda dekoratorlarning aniq ishlash mexanizmini o'rgandik.
Eng muhim uch fikr:
@A @B def f— buf = A(B(f)), va udefpaytida bajariladi. Dekorator ifodalari yuqoridan pastga baholanadi, qo'llanish esa pastdan yuqoriga. Modul funksiyalari uchun bu import paytida bo'ladi — shuning uchun ro'yxatga olish tabiiy, og'ir ish esa zararli. Dekorator istalgan narsa qaytarishi mumkin;returnunutilsa, funksiya jimNonega aylanadi.To'g'ri o'rovchi shaffof bo'ladi. Argumentlarni
*args, **kwargsbilan o'zgartirmay uzatadi, natijani qaytaradi va istisnoni yutmaydi. Lekin u rekursiv funksiyaning har ichki chaqiruvidan ham o'tadi, generator funksiyada esa faqat generator yaratilishini ko'radi. Metodlardaselfoddiy argument sifatida o'tadi,classmethod/staticmethod/propertyesa eng tashqarida turishi kerak.Qatlamlar tartibi — ma'no. Chaqiruv tashqi qatlamdan ichkariga o'tadi. Kesh va hisoblagich, jurnal va qayta urinish, tekshiruv va o'zgartirish tartibi almashsa, natija o'zgaradi. Ro'yxatga oluvchi dekorator pastda qolsa, ro'yxatda tekshiruvsiz funksiya saqlanadi — to'g'ridan-to'g'ri chaqiruvda sezilmaydigan xavfsizlik teshigi.
Keyingi darsda argumentli dekoratorlarni o'rganamiz: @qayta_urin(3) qanday qilib uch qavatli funksiyaga aylanadi va dekoratorni ham qavsli, ham qavssiz ishlatiladigan qilib yozish.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!