Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Hujayra — quti
- 2.2. Bytecode darajasida
- 2.3. Yashirin closure: super() va __class__
- 2.4. Kechikkan bog'lanish chuqur
- 2.5. Bo'shatilgan hujayra
- 2.6. Closure nimani tirik ushlaydi
- 2.7. Closure, klass, partial
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Hujayra anatomiyasi
- Misol 2 — Kechikkan bog'lanish va bo'sh hujayralar
- Misol 3 — Closure va obyektlarning hayot davri
- Misol 4 — Amaliy: closure asosidagi yordamchilar va ularning klass muqobili
- 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.12-dars: Closure chuqur
10-QISM — ILG'OR TIL IMKONIYATLARI · 12-dars
1. Kirish va motivatsiya
7.8-darsda closure'ni "ichki funksiya tashqi o'zgaruvchini eslab qoladi" deb ta'rifladik, __closure__ va kechikkan bog'lanishni ko'rdik. 7.9-darsda nonlocal ni o'rgandik. Oxirgi to'rt darsda esa closure'larsiz birorta dekorator yoza olmadik.
Endi real ilovadan bir parcha — GUI oynasi tugmaga obuna bo'ladi:
class Oyna:
def __init__(self, tugma):
self.malumot = katta_jadval_yukla() # 200 MB
tugma.bosilganda(lambda: self.yangila())
for _ in range(50):
oyna = Oyna(asosiy_tugma)
oyna.yopish()
del oyna50 ta oyna ochilib yopildi, del qilindi — lekin xotira 10 GB ga yetdi. Hech bir oyna o'chmadi.
Sababi — lambda: self.yangila(). Bu closure self ni hujayra (cell) orqali ushlab turadi, closure esa tugmaning ro'yxatida yashaydi. Tugma tirik ekan, har bir oyna va uning 200 MB ma'lumoti ham tirik.
Closure — "eslab qoluvchi funksiya" emas, hujayra obyektlari orqali boshqa obyektlarni tirik ushlab turadigan funksiya. Uning ichki tuzilishini bilish kechikkan bog'lanish, xotira sizishi, aylanma havolalar va NameError kabi kutilmagan xatolarni tushuntiradi.
Bu darsda:
- Hujayra (cell) — o'zgaruvchi emas, quti: bir nechta funksiya bitta qutini bo'lishadi
- Bytecode:
MAKE_CELL,LOAD_DEREF,STORE_DEREF,COPY_FREE_VARS - Yashirin closure'lar:
super()va__class__ - Kechikkan bog'lanish chuqur: comprehension, generator ifodasi,
except ... as e,del - Hayot davri: closure nimani tirik ushlaydi, aylanma havolalar, callback'lar va
weakref - Closure, klass va
partial— qachon qaysi biri
2. Nazariya — chuqur tushuntirish
2.1. Hujayra — quti
def hisoblagich():
n = 0
def ol():
return n
def qosh():
nonlocal n
n += 1
return ol, qoshKompilyator n ni ichki funksiyalar ishlatishini ko'radi va uni oddiy lokal o'zgaruvchi emas, hujayra o'zgaruvchisi qiladi:
| Joy | Ko'rinishi |
|---|---|
hisoblagich.__code__.co_cellvars |
('n',) — "men hujayra yarataman" |
ol.__code__.co_freevars |
('n',) — "men tashqi hujayrani ishlataman" |
ol.__closure__ |
(<cell>,) — hujayra obyektlarining korteji |
ol.__closure__[0].cell_contents |
Hujayraning hozirgi qiymati |
Asosiy fakt: ol va qosh ning __closure__[0] si — bitta hujayra obyekti.
hisoblagich chaqiruvi
│
▼
┌──────┐
│ cell │──► 0 → 1 → 2 ...
└──────┘
▲ ▲
│ │
ol qoshHujayra — qiymat emas, qiymatga havola saqlaydigan quti. qosh qutidagi havolani almashtiradi, ol qutidan o'qiydi. Shuning uchun:
- closure o'zgaruvchini eslaydi, qiymatni emas → kechikkan bog'lanish
- bir chaqiruvda yaratilgan barcha ichki funksiyalar holatni bo'lishadi
- tashqi funksiya qaytgandan keyin ham quti yashaydi — uni ichki funksiyalar ushlab turadi
- har
hisoblagich()chaqiruvi yangi quti yaratadi
cell_contents Python 3.7 dan yoziladigan, types.CellType(qiymat) bilan qo'lda hujayra yaratish mumkin — bu asosan disassembler, serializer va tuzatish vositalari uchun.
2.2. Bytecode darajasida
import dis
dis.dis(hisoblagich)| Operatsiya | Qayerda | Nima qiladi |
|---|---|---|
MAKE_CELL n |
Tashqi funksiya boshida | n uchun bo'sh hujayra yaratadi |
STORE_DEREF n |
Tashqi yoki nonlocal li ichki |
Hujayraga yozadi |
LOAD_DEREF n |
Ichki funksiya | Hujayradan o'qiydi |
COPY_FREE_VARS |
Ichki funksiya boshida | __closure__ dagi hujayralarni freymga ko'chiradi |
MAKE_FUNCTION + SET_FUNCTION_ATTRIBUTE |
Tashqi funksiya | Funksiya obyektini hujayralar korteji bilan yaratadi |
Oddiy lokal LOAD_FAST — freymdagi uyadan to'g'ridan-to'g'ri o'qish. LOAD_DEREF — uyadagi hujayra, keyin hujayradagi qiymat: bitta qo'shimcha qadam. Farq kichik, lekin issiq siklda sezilishi mumkin.
2.3. Yashirin closure: super() va __class__
class Hisob:
def yarat(self):
return super().__init__()
Hisob.yarat.__code__.co_freevars # ('__class__',)Argumentsiz super() qanday qilib klassni biladi? Kompilyator metod ichida super yoki __class__ nomini ko'rsa, klass tanasi uchun yashirin __class__ hujayrasi yaratadi, klass yaratilgach unga klassni yozadi. super() shu hujayradan o'qiydi.
Oqibatlari:
- metodni boshqa klassga ko'chirsangiz (
B.metod = A.metod),super()baribirAni ishlatadi —BnusxasiAturida bo'lmasa,TypeError - 10.10-darsdagi
dataclass(slots=True)yangi klass yaratganda__class__hujayralarini yangi klassga qayta yozadi — aks holdasuper()eski klassga ishora qilardi
2.4. Kechikkan bog'lanish chuqur
Closure hujayrani o'qiydi chaqirilgan paytda. Qiymat qachon o'zgarganiga qarab natija farq qiladi:
| Holat | Natija | Sabab |
|---|---|---|
[lambda: i for i in range(3)] |
[2, 2, 2] |
Comprehension bitta i hujayrasi; lambdalar sikl tugagach chaqiriladi |
(lambda: i for i in range(3)) darhol chaqirilsa |
[0, 1, 2] |
Generator dangasa: har lambda i o'zgarishidan oldin chaqiriladi |
for i in ...: funksiyalar.append(lambda: i) |
Hammasi oxirgi | Bitta hujayra |
lambda i=i: i |
[0, 1, 2] |
Sukut qiymat def paytida hisoblanadi — hujayra emas |
Uch yechim va ularning narxi:
[lambda i=i: i for i in range(3)] # 1. sukut argument
[functools.partial(ishla, i) for i in range(3)] # 2. partial
def yasa(i): return lambda: i # 3. fabrika — har chaqiruv yangi hujayra
[yasa(i) for i in range(3)]| Yechim | Kamchiligi |
|---|---|
| Sukut argument | Imzoda ko'rinadi, chaqiruvchi tasodifan f(5) bilan qayta yozishi mumkin |
partial |
Faqat pozitsion/nomli argumentlar uchun; repr o'qilishi qiyinroq |
| Fabrika | Qo'shimcha funksiya, lekin eng toza — 10.9-darsdagi dekorator fabrikasi aynan shu |
Kechikkan bog'lanish foydali ham bo'lishi mumkin: sozlama qayta yuklanganda closure yangi qiymatni avtomatik ko'radi.
2.5. Bo'shatilgan hujayra
Hujayra bo'sh bo'lishi mumkin:
def f():
try:
1 / 0
except ZeroDivisionError as e:
g = lambda: e
return g
f()() # NameError: cannot access free variable 'e' where it is not associated with a value in enclosing scopeexcept ... as e bloki tugaganda Python e ni o'chiradi (del e) — aylanma havolaning oldini olish uchun (traceback → freym → e). Hujayra bo'shaydi. Xuddi shunday del x ham tashqi funksiyada hujayrani bo'shatadi.
Kerakli qiymatni blok ichida oddiy nomga ko'chiring: xato = e.
2.6. Closure nimani tirik ushlaydi
Closure hujayralar orqali butun obyektlarni ushlaydi — ulardan faqat bir qismi kerak bo'lsa ham:
def hisobot_yasa(jadval): # jadval — 1 mln qator
soni = len(jadval)
def sarlavha():
return f"{len(jadval)} qator" # ❌ jadvalni ushlaydi
return sarlavha
def hisobot_yasa(jadval):
soni = len(jadval)
def sarlavha():
return f"{soni} qator" # ✅ faqat butun son
return sarlavha self ni ushlaydigan callback (kirishdagi misol):
tugma.bosilganda(lambda: self.yangila()) # tugma → lambda → cell → selfTugma tirik ekan, self ham tirik. Yechimlar:
- Obunani bekor qilish — yopilganda ro'yxatdan olib tashlash
weakref.WeakMethod(self.yangila)— metodga zaif havola; obyekt o'lsa, havolaNoneqaytaradiweakref.ref(self)ni closure'da ushlash va chaqiruvda tekshirish
Aylanma havola: o'zini chaqiradigan ichki funksiya.
def yasa(malumot):
def yur(n):
return yur(n - 1) if n else malumot # yur → cell → yur
return yuryur o'z hujayrasiga, hujayra esa yur ga ishora qiladi. Havola hisoblagichi bunday siklni tozalay olmaydi — faqat davriy axlat yig'uvchi (gc), noaniq vaqtda. gc.disable() qilingan ilovada bu sizish.
2.7. Closure, klass, partial
| Mezon | Closure | Klass (__call__) |
functools.partial |
|---|---|---|---|
| Holat | Hujayralarda, yashirin | Atributlarda, ochiq | Faqat qotirilgan argumentlar |
| Bir nechta amal | Bir nechta ichki funksiya (qaytarish noqulay) | Metodlar | Yo'q |
| Chaqiruv tezligi | Tez (oddiy funksiya) | Sekinroq (__call__ qidirish) |
Tez, agar C funksiyani o'rasa |
pickle |
lokal funksiya | (o'ralgan funksiya pickle bo'lsa) | |
| Tekshirish/tuzatish | __closure__ orqali |
vars(obj) |
.func, .args, .keywords |
Meros, isinstance |
Yo'q | Ha | Yo'q |
Qoida:
- Bitta amal + kichik holat — closure (dekoratorlar, callback'lar, fabrikalar)
- Argumentlarni qotirish, holatsiz —
partial - Bir nechta amal, ochiq holat, pickle, meros — klass
3. Tez ma'lumotnoma
Ichki tuzilish
tashqi.__code__.co_cellvars # tashqi yaratadigan hujayralar nomlari
ichki.__code__.co_freevars # ichki ishlatadigan tashqi hujayralar nomlari
ichki.__closure__ # hujayra obyektlari korteji (yoki None)
ichki.__closure__[0].cell_contents # hozirgi qiymat (yoziladi)
types.CellType(qiymat) # qo'lda hujayraBytecode
MAKE_CELL STORE_DEREF LOAD_DEREF COPY_FREE_VARSKechikkan bog'lanish
[lambda: i for i in r] → hammasi oxirgi
lambda i=i: i → sukut — def paytida
functools.partial(f, i) → qotirilgan argument
def yasa(i): return lambda: i → har chaqiruv yangi hujayraHayot davri
closure → cell → obyekt (butun obyekt tirik)
except E as e: → blok oxirida del e → bo'sh hujayra → NameError
o'zini chaqiruvchi ichki funksiya → sikl → faqat gc
callback'da self → WeakMethod / weakref.ref / obunani bekor qilish4. Batafsil misollar
Misol 1 — Hujayra anatomiyasi
"""Bitta hujayrani bo'lishadigan funksiyalar, co_cellvars/co_freevars, bytecode, cell_contents, __class__."""
import dis
import types
def hisoblagich(boshlangich=0):
n = boshlangich
def ol():
return n
def qosh(qadam=1):
nonlocal n
n += qadam
return n
def nolla():
nonlocal n
n = 0
return ol, qosh, nolla
print("=== 1. Bir chaqiruv — bitta quti ===")
ol, qosh, nolla = hisoblagich()
print(f" hisoblagich.co_cellvars: {hisoblagich.__code__.co_cellvars}")
print(f" ol.co_freevars: {ol.__code__.co_freevars}, qosh.co_freevars: {qosh.__code__.co_freevars}")
print(f" bitta hujayra: {ol.__closure__[0] is qosh.__closure__[0] is nolla.__closure__[0]}")
qosh(); qosh(5)
print(f" qosh ikki marta → ol() = {ol()}, cell_contents = {ol.__closure__[0].cell_contents}")
nolla()
print(f" nolla() → ol() = {ol()}")
print("\n=== 2. Har chaqiruv — yangi quti ===")
a_ol, a_qosh, _ = hisoblagich(100)
b_ol, b_qosh, _ = hisoblagich(100)
a_qosh(); a_qosh(); b_qosh()
print(f" a: {a_ol()}, b: {b_ol()}, hujayralar bir xilmi: {a_ol.__closure__[0] is b_ol.__closure__[0]}")
print("\n=== 3. Closure o'zgaruvchini eslaydi, qiymatni emas ===")
def yarat():
xabar = "birinchi"
f = lambda: xabar
oldin = f()
xabar = "ikkinchi"
return oldin, f
oldin, f = yarat()
print(f" lambda yaratilgan paytda o'qilsa: {oldin!r}, keyin chaqirilsa: {f()!r}")
print("\n=== 4. Bytecode ===")
def opkodlar(funksiya):
return sorted({i.opname for i in dis.get_instructions(funksiya)} &
{"MAKE_CELL", "STORE_DEREF", "LOAD_DEREF", "COPY_FREE_VARS", "LOAD_FAST",
"STORE_FAST", "LOAD_FAST_BORROW"})
for nom, funksiya in [("hisoblagich", hisoblagich), ("ol", ol), ("qosh", qosh)]:
print(f" {nom:12} {opkodlar(funksiya)}")
def oddiy(n):
return n + 1
print(f" {'oddiy':12} {opkodlar(oddiy)} closure: {oddiy.__closure__}")
print("\n=== 5. cell_contents yoziladi; CellType ===")
ol, qosh, _ = hisoblagich()
ol.__closure__[0].cell_contents = 41
print(f" hujayraga 41 yozildi → qosh() = {qosh()}")
hujayra = types.CellType("qo'lda")
yangi = types.FunctionType(ol.__code__, globals(), "ol_nusxa", None, (hujayra,))
print(f" boshqa hujayra bilan yangi funksiya: {yangi()!r}, asl ol(): {ol()}")
print("\n=== 6. Yashirin closure: super() va __class__ ===")
class Hisob:
def __init__(self, balans):
self.balans = balans
def tavsif(self):
return f"{type(self).__name__}: {self.balans}"
class Omonat(Hisob):
def tavsif(self):
return super().tavsif() + " (omonat)"
def oddiy(self):
return self.balans
print(f" Omonat.tavsif.co_freevars: {Omonat.tavsif.__code__.co_freevars}")
print(f" Omonat.oddiy.co_freevars: {Omonat.oddiy.__code__.co_freevars}")
print(f" __class__ hujayrasida: {Omonat.tavsif.__closure__[0].cell_contents.__name__}")
class Boshqa(Hisob):
tavsif = Omonat.tavsif
try:
Boshqa(5).tavsif()
except TypeError as e:
print(f" ⚠️ metodni boshqa klassga ko'chirish: TypeError: {e}")Natijaning muhim qismi:
=== 1. Bir chaqiruv — bitta quti ===
hisoblagich.co_cellvars: ('n',)
ol.co_freevars: ('n',), qosh.co_freevars: ('n',)
bitta hujayra: True
qosh ikki marta → ol() = 6, cell_contents = 6
nolla() → ol() = 0
=== 2. Har chaqiruv — yangi quti ===
a: 102, b: 101, hujayralar bir xilmi: False
=== 3. Closure o'zgaruvchini eslaydi, qiymatni emas ===
lambda yaratilgan paytda o'qilsa: 'birinchi', keyin chaqirilsa: 'ikkinchi'
=== 4. Bytecode ===
hisoblagich ['LOAD_FAST_BORROW', 'MAKE_CELL', 'STORE_DEREF', 'STORE_FAST']
ol ['COPY_FREE_VARS', 'LOAD_DEREF']
qosh ['COPY_FREE_VARS', 'LOAD_DEREF', 'LOAD_FAST_BORROW', 'STORE_DEREF']
oddiy ['LOAD_FAST_BORROW'] closure: None
=== 5. cell_contents yoziladi; CellType ===
hujayraga 41 yozildi → qosh() = 42
boshqa hujayra bilan yangi funksiya: "qo'lda", asl ol(): 42
=== 6. Yashirin closure: super() va __class__ ===
Omonat.tavsif.co_freevars: ('__class__',)
Omonat.oddiy.co_freevars: ()
__class__ hujayrasida: Omonat
⚠️ metodni boshqa klassga ko'chirish: TypeError: super(type, obj): obj (instance of Boshqa) is not an instance or subtype of type (Omonat).Nima ko'rsatdi: 2.1, 2.2, 2.3-bo'limlar.
Misol 2 — Kechikkan bog'lanish va bo'sh hujayralar
"""Sikl, comprehension, generator ifodasi; uch yechim; except as e va del; foydali kechikkan bog'lanish."""
import functools
print("=== 1. Uch xil yaratish — uch xil natija ===")
royxat = [lambda: i for i in range(3)]
print(f" list comprehension: {[f() for f in royxat]}")
print(f" generator, darhol chaqirilsa: {[f() for f in (lambda: i for i in range(3))]}")
generator = (lambda: i for i in range(3))
saqlangan = list(generator)
print(f" generator, avval list(): {[f() for f in saqlangan]}")
funksiyalar = []
for j in range(3):
funksiyalar.append(lambda: j)
j = 99
print(f" for sikl, keyin j = 99: {[f() for f in funksiyalar]} ⚠️ modul darajasida — global o'zgaruvchi")
print("\n=== 2. Tugmalar: har tugma o'z raqamini chiqarishi kerak ===")
class Tugma:
def __init__(self, yorliq, bosilganda):
self.yorliq, self.bosilganda = yorliq, bosilganda
def ishla(raqam, belgi="#"):
return f"{belgi}{raqam}"
def yasa(raqam):
return lambda: ishla(raqam)
variantlar = {
"lambda: ishla(r)": [Tugma(r, lambda: ishla(r)) for r in range(1, 4)],
"lambda r=r: ...": [Tugma(r, lambda r=r: ishla(r)) for r in range(1, 4)],
"partial(ishla, r)": [Tugma(r, functools.partial(ishla, r)) for r in range(1, 4)],
"yasa(r)": [Tugma(r, yasa(r)) for r in range(1, 4)],
}
for nom, tugmalar in variantlar.items():
print(f" {nom:18} → {[t.bosilganda() for t in tugmalar]}")
print("\n=== 3. ⚠️ Sukut argument yechimining narxi ===")
f = variantlar["lambda r=r: ..."][0].bosilganda
print(f" imzo ko'rinadi: {f.__defaults__}, tasodifan f(7) → {f(7)!r}")
p = variantlar["partial(ishla, r)"][0].bosilganda
print(f" partial ichini ko'rish mumkin: func={p.func.__name__}, args={p.args}; p(belgi='№') → {p(belgi='№')!r}")
print("\n=== 4. ⚠️ Bo'sh hujayra: except ... as e va del ===")
def xato_ushla():
try:
{}["kalit"]
except KeyError as e:
kechikkan = lambda: f"oxirgi xato: {e!r}"
xato = e
togri = lambda: f"oxirgi xato: {xato!r}"
return kechikkan, togri
kechikkan, togri = xato_ushla()
for nom, f in [("e ni ushlagan", kechikkan), ("xato = e", togri)]:
try:
print(f" {nom}: {f()}")
except NameError as ex:
print(f" {nom}: NameError: {ex}")
print(f" e hujayrasi bo'shmi: {kechikkan.__closure__[0].__repr__().endswith('empty>')}")
def ochirilgan():
vaqtinchalik = [1, 2, 3]
f = lambda: sum(vaqtinchalik)
del vaqtinchalik
return f
try:
ochirilgan()()
except NameError as ex:
print(f" del dan keyin: NameError: {ex}")
print("\n=== 5. Kechikkan bog'lanish foydali bo'lganda ===")
def sozlamali_ilova():
sozlama = {"til": "uz", "valyuta": "UZS"}
def narx(summa):
return f"{summa:,} {sozlama['valyuta']}"
def qayta_yukla(yangi):
nonlocal sozlama
sozlama = yangi
return narx, qayta_yukla
narx, qayta_yukla = sozlamali_ilova()
print(f" {narx(150_000)}")
qayta_yukla({"til": "en", "valyuta": "USD"})
print(f" qayta yuklangandan keyin o'sha narx funksiyasi: {narx(12)}")Natijaning muhim qismi:
=== 1. Uch xil yaratish — uch xil natija ===
list comprehension: [2, 2, 2]
generator, darhol chaqirilsa: [0, 1, 2]
generator, avval list(): [2, 2, 2]
for sikl, keyin j = 99: [99, 99, 99] ⚠️ modul darajasida — global o'zgaruvchi
=== 2. Tugmalar: har tugma o'z raqamini chiqarishi kerak ===
lambda: ishla(r) → ['#3', '#3', '#3']
lambda r=r: ... → ['#1', '#2', '#3']
partial(ishla, r) → ['#1', '#2', '#3']
yasa(r) → ['#1', '#2', '#3']
=== 3. ⚠️ Sukut argument yechimining narxi ===
imzo ko'rinadi: (1,), tasodifan f(7) → '#7'
partial ichini ko'rish mumkin: func=ishla, args=(1,); p(belgi='№') → '№1'
=== 4. ⚠️ Bo'sh hujayra: except ... as e va del ===
e ni ushlagan: NameError: cannot access free variable 'e' where it is not associated with a value in enclosing scope
xato = e: oxirgi xato: KeyError('kalit')
e hujayrasi bo'shmi: True
del dan keyin: NameError: cannot access free variable 'vaqtinchalik' where it is not associated with a value in enclosing scope
=== 5. Kechikkan bog'lanish foydali bo'lganda ===
150,000 UZS
qayta yuklangandan keyin o'sha narx funksiyasi: 12 USDNima ko'rsatdi: 2.4, 2.5-bo'limlar. 1-bo'limdagi for sikl modul darajasida bo'lgani uchun j — hujayra emas, global o'zgaruvchi; natija baribir kechikkan, chunki lambda globalni ham chaqiruv paytida o'qiydi.
Misol 3 — Closure va obyektlarning hayot davri
"""Closure butun obyektni ushlaydi; self li callback; WeakMethod; o'zini chaqiruvchi ichki funksiya va gc."""
import gc
import weakref
class KattaJadval:
def __init__(self, qatorlar):
self.qatorlar = list(range(qatorlar))
def __len__(self):
return len(self.qatorlar)
print("=== 1. Closure butun obyektni tirik ushlaydi ===")
def sarlavha_yomon(jadval):
return lambda: f"{len(jadval)} qator"
def sarlavha_yaxshi(jadval):
soni = len(jadval)
return lambda: f"{soni} qator"
for nom, yasovchi in [("jadvalni ushlaydi", sarlavha_yomon), ("faqat sonni", sarlavha_yaxshi)]:
jadval = KattaJadval(100_000)
havola = weakref.ref(jadval)
sarlavha = yasovchi(jadval)
del jadval
gc.collect()
ushlangan = [type(c.cell_contents).__name__ for c in sarlavha.__closure__]
print(f" {nom:18} {sarlavha()!r:15} jadval tirikmi: {havola() is not None}, hujayralarda: {ushlangan}")
print("\n=== 2. ⚠️ self ni ushlaydigan callback ===")
class Tugma:
def __init__(self):
self.ishlovchilar = []
def bosilganda(self, f):
self.ishlovchilar.append(f)
def bos(self):
natijalar = []
for f in list(self.ishlovchilar):
if isinstance(f, weakref.WeakMethod):
metod = f()
if metod is None:
self.ishlovchilar.remove(f)
continue
f = metod
natijalar.append(f())
return natijalar
class Oyna:
soni = 0
def __init__(self, tugma, usul):
Oyna.soni += 1
self.raqam = Oyna.soni
self.jadval = KattaJadval(10_000)
if usul == "lambda":
tugma.bosilganda(lambda: self.yangila())
elif usul == "WeakMethod":
tugma.bosilganda(weakref.WeakMethod(self.yangila))
def yangila(self):
return f"oyna {self.raqam} yangilandi"
for usul in ("lambda", "WeakMethod"):
tugma = Tugma()
havolalar = []
for _ in range(3):
oyna = Oyna(tugma, usul)
havolalar.append(weakref.ref(oyna))
del oyna
gc.collect()
tirik = sum(h() is not None for h in havolalar)
print(f" {usul:10}: 3 oyna yaratilib o'chirildi → tirik: {tirik}, tugma bosildi: {tugma.bos()}, "
f"ro'yxatda qoldi: {len(tugma.ishlovchilar)}")
print("\n=== 3. ⚠️ O'zini chaqiruvchi ichki funksiya — aylanma havola ===")
def yurgizuvchi(jadval):
def yur(n):
return yur(n - 1) if n else len(jadval)
return yur
gc.disable()
try:
jadval = KattaJadval(1_000)
havola = weakref.ref(jadval)
yur = yurgizuvchi(jadval)
print(f" yur(5) = {yur(5)}, yur hujayralari: {yur.__code__.co_freevars}")
print(f" yur o'z hujayrasida o'zi: {yur.__closure__[1].cell_contents is yur}")
del jadval, yur
print(f" del dan keyin (gc o'chiq) jadval tirikmi: {havola() is not None}")
yigildi = gc.collect()
print(f" gc.collect() → {yigildi} obyekt yig'ildi, jadval tirikmi: {havola() is not None}")
finally:
gc.enable()
print("\n=== 4. Obunani bekor qiluvchi closure ===")
class Hodisalar:
def __init__(self):
self.obunachilar = {}
def obuna(self, nom, f):
self.obunachilar.setdefault(nom, []).append(f)
bekor_qilindi = False
def bekor_qil():
nonlocal bekor_qilindi
if bekor_qilindi:
return False
self.obunachilar[nom].remove(f)
bekor_qilindi = True
return True
return bekor_qil
def chiqar(self, nom, *args):
return [f(*args) for f in self.obunachilar.get(nom, [])]
TOLOV = "to'lov"
h = Hodisalar()
bekor1 = h.obuna(TOLOV, lambda s: f"SMS: {s:,}")
bekor2 = h.obuna(TOLOV, lambda s: f"email: {s:,}")
print(f" {h.chiqar(TOLOV, 50_000)}")
print(f" bekor1() → {bekor1()}, yana bekor1() → {bekor1()}")
print(f" {h.chiqar(TOLOV, 70_000)}")Natijaning muhim qismi:
=== 1. Closure butun obyektni tirik ushlaydi ===
jadvalni ushlaydi '100000 qator' jadval tirikmi: True, hujayralarda: ['KattaJadval']
faqat sonni '100000 qator' jadval tirikmi: False, hujayralarda: ['int']
=== 2. ⚠️ self ni ushlaydigan callback ===
lambda : 3 oyna yaratilib o'chirildi → tirik: 3, tugma bosildi: ['oyna 1 yangilandi', 'oyna 2 yangilandi', 'oyna 3 yangilandi'], ro'yxatda qoldi: 3
WeakMethod: 3 oyna yaratilib o'chirildi → tirik: 0, tugma bosildi: [], ro'yxatda qoldi: 0
=== 3. ⚠️ O'zini chaqiruvchi ichki funksiya — aylanma havola ===
yur(5) = 1000, yur hujayralari: ('jadval', 'yur')
yur o'z hujayrasida o'zi: True
del dan keyin (gc o'chiq) jadval tirikmi: True
gc.collect() → 6 obyekt yig'ildi, jadval tirikmi: False
=== 4. Obunani bekor qiluvchi closure ===
['SMS: 50,000', 'email: 50,000']
bekor1() → True, yana bekor1() → False
['email: 70,000']Nima ko'rsatdi: 2.6-bo'lim. Callback ro'yxatiga lambda: self... qo'yish obyektni ro'yxat yashagan paytgacha ushlaydi; WeakMethod esa obyekt o'lganda o'zini ro'yxatdan chiqarib yuborish imkonini beradi.
Misol 4 — Amaliy: closure asosidagi yordamchilar va ularning klass muqobili
Real ilovada tez-tez uchraydigan uch yordamchi — debounce (tez-tez chaqiruvlardan faqat oxirgisini bajarish), throttle (davr ichida bir martadan ko'p emas) va memo — avval closure bilan, keyin klass bilan yoziladi va solishtiriladi. Vaqt deterministik bo'lishi uchun soxta soat ishlatiladi.
"""debounce, throttle, memo: closure va klass variantlari; holatni ko'rish, pickle, bir nechta amal."""
import pickle
class Soat:
def __init__(self):
self.t = 0.0
def __call__(self):
return self.t
soat = Soat()
def debounce(kutish, *, soat=soat):
"""Oxirgi chaqiruvdan keyin `kutish` soniya jimlik bo'lsa, `tekshir()` funksiyani bajaradi."""
def dekorator(funksiya):
kutilayotgan = None # (vaqt, args)
def chaqir(*args):
nonlocal kutilayotgan
kutilayotgan = (soat(), args)
def tekshir():
nonlocal kutilayotgan
if kutilayotgan and soat() - kutilayotgan[0] >= kutish:
_, args = kutilayotgan
kutilayotgan = None
return funksiya(*args)
return None
chaqir.tekshir = tekshir
return chaqir
return dekorator
def throttle(davr, *, soat=soat):
def dekorator(funksiya):
oxirgi = float("-inf")
def orovchi(*args):
nonlocal oxirgi
if soat() - oxirgi >= davr:
oxirgi = soat()
return funksiya(*args)
return "o'tkazib yuborildi"
return orovchi
return dekorator
def memo(funksiya):
kesh = {}
def orovchi(*args):
if args not in kesh:
kesh[args] = funksiya(*args)
return kesh[args]
orovchi.tozala = kesh.clear
orovchi.hajm = lambda: len(kesh)
return orovchi
class Memo:
def __init__(self, funksiya):
self.funksiya = funksiya
self.kesh = {}
def __call__(self, *args):
if args not in self.kesh:
self.kesh[args] = self.funksiya(*args)
return self.kesh[args]
print("=== 1. debounce: qidiruv maydoniga yozish ===")
sorovlar = []
@debounce(0.3)
def qidir(matn):
sorovlar.append(matn)
return f"'{matn}' bo'yicha qidirildi"
for vaqt, harf in [(0.0, "p"), (0.1, "py"), (0.2, "pyt"), (0.25, "pyth"), (0.4, "pytho"), (0.5, "python")]:
soat.t = vaqt
qidir(harf)
natija = qidir.tekshir()
if natija:
print(f" t={vaqt}: {natija}")
soat.t = 0.85
print(f" t=0.85: {qidir.tekshir()}")
print(f" serverga so'rovlar: {sorovlar} (6 ta harf, 1 ta so'rov)")
print("\n=== 2. throttle: skroll hodisalari ===")
@throttle(1.0)
def yukla(sahifa):
return f"{sahifa}-sahifa yuklandi"
for vaqt, sahifa in [(10.0, 2), (10.3, 3), (10.9, 4), (11.0, 5), (11.5, 6), (12.2, 7)]:
soat.t = vaqt
print(f" t={vaqt}: {yukla(sahifa)}")
print("\n=== 3. Holatni ko'rish: closure vs klass ===")
def fib(n):
return n if n < 2 else fib(n - 1) + fib(n - 2)
m1, m2 = memo(fib), Memo(fib)
for n in (10, 20, 10):
m1(n), m2(n)
print(f" closure: hujayralar {m1.__code__.co_freevars}, kesh hajmi (yordamchi orqali): {m1.hajm()}")
print(f" klass: vars → {sorted(vars(m2))}, kesh kalitlari: {sorted(m2.kesh)}")
print("\n=== 4. pickle ===")
for nom, obyekt in [("closure memo", m1), ("klass Memo", m2)]:
try:
tiklangan = pickle.loads(pickle.dumps(obyekt))
print(f" {nom}: ✅ tiklandi, kesh: {sorted(tiklangan.kesh)}")
except (pickle.PicklingError, AttributeError) as e:
print(f" {nom}: ❌ {type(e).__name__}: {str(e).split(' <')[0]}")
print("\n=== 5. Bitta chaqiruvning ichki holati — yashirinlik afzallik ham ===")
m2.kesh[(10,)] = -1
print(f" klass: tashqaridan buzish oson → m2(10) = {m2(10)}")
try:
m1.kesh
except AttributeError:
print(f" closure: m1.kesh yo'q, faqat tozala/hajm orqali → m1(10) = {m1(10)}")Natijaning muhim qismi:
=== 1. debounce: qidiruv maydoniga yozish ===
t=0.85: 'python' bo'yicha qidirildi
serverga so'rovlar: ['python'] (6 ta harf, 1 ta so'rov)
=== 2. throttle: skroll hodisalari ===
t=10.0: 2-sahifa yuklandi
t=10.3: o'tkazib yuborildi
t=10.9: o'tkazib yuborildi
t=11.0: 5-sahifa yuklandi
t=11.5: o'tkazib yuborildi
t=12.2: 7-sahifa yuklandi
=== 3. Holatni ko'rish: closure vs klass ===
closure: hujayralar ('funksiya', 'kesh'), kesh hajmi (yordamchi orqali): 2
klass: vars → ['funksiya', 'kesh'], kesh kalitlari: [(10,), (20,)]
=== 4. pickle ===
closure memo: ❌ PicklingError: Can't pickle local object
klass Memo: ✅ tiklandi, kesh: [(10,), (20,)]
=== 5. Bitta chaqiruvning ichki holati — yashirinlik afzallik ham ===
klass: tashqaridan buzish oson → m2(10) = -1
closure: m1.kesh yo'q, faqat tozala/hajm orqali → m1(10) = 55Nima ko'rsatdi: 2.7-bo'lim — closure holatni yashiradi va bitta amal uchun ixcham, klass holatni ochadi, pickle qilinadi va bir nechta amalni tabiiy ifodalaydi.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Closure o'zgaruvchi qiymatining nusxasini saqlaydi" | Hujayraga havola saqlaydi — qiymat chaqiruv paytida o'qiladi |
| "Har ichki funksiyaning o'z nusxasi bor" | Bir chaqiruvdagi barcha ichki funksiyalar bitta hujayrani bo'lishadi |
| "Tashqi funksiya qaytgach, uning o'zgaruvchilari yo'qoladi" | Hujayralar ichki funksiyalar yashagan paytgacha yashaydi |
| "Generator ifodasida ham lambda'lar oxirgi qiymatni oladi" | Darhol chaqirilsa — yo'q, generator dangasa |
"except ... as e dagi e blokdan keyin ham bor" |
Blok oxirida o'chiriladi — closure'da NameError |
| "Closure faqat kerakli qismni ushlaydi" | Butun obyektni ushlaydi |
"super() hech qanday closure ishlatmaydi" |
Yashirin __class__ hujayrasidan o'qiydi |
| "Closure har doim klassdan yaxshiroq" | Bitta amal uchun — ha; ochiq holat, pickle, bir nechta amal uchun — klass |
6. Keng tarqalgan xatolar va yechimlari
1. Siklda closure yaratish
for b in tugmalar:
b.bosilganda(lambda: ochir(b.id)) # ❌ hammasi oxirgi tugmani o'chiradi
for b in tugmalar:
b.bosilganda(functools.partial(ochir, b.id)) # ✅2. nonlocal ni unutish
def hisoblagich():
n = 0
def qosh():
n += 1 # ❌ UnboundLocalError — n lokal bo'lib qoldi
return qosh
def qosh():
nonlocal n # ✅
n += 13. Closure'da katta obyektni keraksiz ushlash
def hisobot(df):
return lambda: f"{len(df)} qator" # ❌ butun DataFrame xotirada qoladi
def hisobot(df):
n = len(df)
return lambda: f"{n} qator" # ✅4. Callback'da self
tugma.bosilganda(lambda: self.yangila()) # ❌ oyna hech qachon o'chmaydi
tugma.bosilganda(weakref.WeakMethod(self.yangila)) # ✅ yoki yopilganda obunani bekor qiling5. except blokidagi e ni closure'da ishlatish
except ValueError as e:
qayta_urinish = lambda: jurnal(e) # ❌ NameError
except ValueError as e:
xato = e
qayta_urinish = lambda: jurnal(xato) # ✅6. Sukut argument yechimi imzoni "ifloslaydi"
ishlovchilar = [lambda x, i=i: x * i for i in range(3)]
ishlovchilar[0](5, 10) # ❌ tasodifan i qayta yozildi — xato yo'q
ishlovchilar = [functools.partial(operator.mul, i) for i in range(3)] # ✅7. Closure asosidagi obyektni pickle/multiprocessing ga berish
pool.map(yasa_ishlovchi(sozlama), malumot) # ❌ PicklingError: Can't pickle local object
pool.map(functools.partial(ishla, sozlama), malumot) # ✅ modul darajasidagi funksiya + partial8. Bir nechta amal uchun closure'larni lug'atga yig'ish
def hisob(balans):
def yech(s): ...
def qosh(s): ...
def qoldiq(): ...
return {"yech": yech, "qosh": qosh, "qoldiq": qoldiq} # ❌ klassni qo'lda yasash
class Hisob: ... # ✅ metodlar, repr, isinstance, pickle7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 7.8–7.9-darslar (o'tilgan): LEGB,
__closure__,nonlocal - 8.7-dars (o'tilgan):
super()— endi__class__hujayrasi orqali tushunamiz - 10.8–10.11-darslar (o'tilgan): dekoratorlarning barcha qavatlari — closure
- 10.13-dars:
functools.partialvacache— closure muqobillari - 10.17-dars: deskriptorlar va
WeakMethodga o'xshash bog'langan metodlar - 14-qism:
asynciocallback'lari va closure'dagi holat; iplar orasida hujayra bo'lishish - 11-qism: havolalar, axlat yig'uvchi va
weakref— closure hayot davrining asosi - Tkinter/Qt: signal va callback'lar — 2.6-bo'limdagi tuzoq
8. Eng yaxshi amaliyotlar
Closure'da faqat kerakli qiymatni ushlang — butun obyektni emas.
Siklda callback yaratishda
partialyoki fabrika funksiya — sukut argument emas.Uzoq yashaydigan ro'yxatga
selfni ushlagan closure qo'ymang.WeakMethodyoki bekor qilish funksiyasini qaytaring.except ... as edagi xatoni saqlash kerak bo'lsa — boshqa nomga ko'chiring.O'zini chaqiruvchi ichki funksiyadan ehtiyot bo'ling —
gco'chiq muhitda sizadi.Bitta amal + kichik holat — closure; bir nechta amal yoki ochiq holat — klass.
picklevamultiprocessingkerak bo'lsa — closure emas, modul darajasidagi funksiya +partialyoki klass.Tuzatishda
__closure__vaco_freevarsga qarang — closure nimani ushlab turganini aniq ko'rsatadi.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
def yasash():
n = 0
def ol():
return n
def qosh():
nonlocal n
n += 1
return n
return ol, qosh
1. ol, qosh = yasash()
qosh()
qosh()
print(ol())
2. a = yasash()
b = yasash()
a[1]()
print(a[0](), b[0]())
3. print([f() for f in [lambda: i for i in range(3)]])
4. print([f() for f in (lambda: i for i in range(3))])
5. print([f() for f in [lambda i=i: i for i in range(3)]])
6. def f():
x = 1
g = lambda: x
x = 2
return g
print(f()())
7. ol, qosh = yasash()
print(ol.__closure__[0] is qosh.__closure__[0])
8. ol, qosh = yasash()
ol.__closure__[0].cell_contents = 10
print(qosh())
9. print(yasash.__code__.co_cellvars, yasash()[0].__code__.co_freevars)
10. def f():
try:
1 / 0
except ZeroDivisionError as e:
g = lambda: type(e).__name__
return g
try:
f()()
except NameError:
print("NameError")
11. class K:
def m(self):
return super().__str__()
print(K.m.__code__.co_freevars)
12. def tashqi():
x = "tashqi"
def orta():
x = "orta"
def ichki():
nonlocal x
x = "ichki"
ichki()
return x
return orta(), x
print(tashqi())Javoblar
2—olvaqoshbitta hujayrani bo'lishadi1 0— haryasash()chaqiruvi yangi hujayra[2, 2, 2]— bittai, sikl tugagach o'qildi[0, 1, 2]— generator dangasa, har lambda darhol chaqirildi[0, 1, 2]— sukut qiymatdefpaytida2— lambda o'zgaruvchini eslaydiTrue11— hujayraga yozilgan qiymatqoshga ham ko'rinadi('n',) ('n',)NameError—eblok oxirida o'chirildi('__class__',)—super()uchun yashirin hujayra('ichki', 'tashqi')—nonlocaleng yaqin tashqi funksiyadagixni (ortaniki) oldi
Vazifa 2: Xatolarni tuzating
1. def vazifalar_yarat(nomlar):
vazifalar = []
for nom in nomlar:
vazifalar.append(lambda: print(f"{nom} bajarildi"))
return vazifalar
2. def sanagich():
soni = 0
def keyingi():
soni += 1
return soni
return keyingi
3. class Grafik:
def __init__(self, manba):
self.nuqtalar = manba.yukla() # juda katta
manba.yangilanganda(lambda: self.chiz())
4. def ishlov(fayllar):
xatolar = []
for f in fayllar:
try:
oqi(f)
except OSError as e:
xatolar.append(lambda: f"{f}: {e}")
return xatolar
5. def daraja_filtri(jurnal):
return lambda yozuv: yozuv.daraja >= jurnal.sozlama.daraja and len(jurnal.yozuvlar)Javoblar
1. def vazifalar_yarat(nomlar):
return [functools.partial(print, f"{nom} bajarildi") for nom in nomlar]
2. def sanagich():
soni = 0
def keyingi():
nonlocal soni # aks holda UnboundLocalError
soni += 1
return soni
return keyingi
3. class Grafik:
def __init__(self, manba):
self.nuqtalar = manba.yukla()
manba.yangilanganda(weakref.WeakMethod(self.chiz)) # Grafik o'lsa, manba uni ushlab turmaydi
# yoki: self._bekor_qil = manba.yangilanganda(...) va yopishda self._bekor_qil()
4. def ishlov(fayllar):
xatolar = []
for f in fayllar:
try:
oqi(f)
except OSError as e:
xabar = f"{f}: {e}" # f ham kechikkan, e esa o'chiriladi — hozirning o'zida satr
xatolar.append(lambda xabar=xabar: xabar)
return xatolar
# yoki oddiyroq: xatolar.append(f"{f}: {e}") — closure umuman kerak emas
5. def daraja_filtri(jurnal):
sozlama = jurnal.sozlama # butun jurnal (yozuvlari bilan) emas, faqat sozlama
return lambda yozuv: yozuv.daraja >= sozlama.daraja
# len(jurnal.yozuvlar) filtr mantiqiga aloqasi yo'q edi va jurnalni tirik ushlardiVazifa 3: Closure inspektori
closure_tahlil(f) funksiyasini yozing:
co_freevarsva__closure__dan{nom: qiymat}lug'atini qaytarsin, bo'sh hujayralarni<bo'sh>deb belgilasin- Har qiymatning taxminiy xotira hajmini
tracemallocyoki rekursivsys.getsizeofbilan baholasin (ehtiyot bo'ling:getsizeofichki obyektlarni hisoblamaydi) __wrapped__zanjiri bo'ylab barcha qatlamlarning closure'larini ko'rsatsin- 10.8–10.11-darslardagi dekoratorlarga qo'llab ko'ring
Vazifa 4: WeakCallback
Istalgan chaqiriladigan obyekt uchun zaif callback yozing:
- Bog'langan metod bo'lsa —
WeakMethod, oddiy funksiya bo'lsa — kuchli havola (funksiyalar odatda modul darajasida yashaydi) - Closure bo'lsa — hujayralarida
selfga o'xshash obyektlar bor-yo'qligini tekshirib, ogohlantirish chiqarsin - Obyekt o'lganda
o_lganda=callback'i chaqirilsin (weakref.finalize) - Misol 3 dagi
Tugmabilan testlar yozing
Vazifa 5: Closure'siz dekorator
10.9-darsdagi qayta_urin(urinishlar, istisnolar) dekoratorini closure ishlatmasdan yozing:
- Sinf asosida (
__init__,__call__,__get__) functools.partialasosida — ichki funksiyalarsiz- Uch variantni (closure, klass, partial) tezlik (
timeit),picklevainspect.signaturebo'yicha solishtiring - Xulosangizni jadvalda yozing
Vazifa 6: Hujayralarni qayta bog'lash
qayta_bogla(f, **yangi) funksiyasini yozing:
fning kodi va boshqa atributlarini saqlagan holda, ko'rsatilgan erkin o'zgaruvchilari uchun yangi hujayralar bilan yangi funksiya yaratsin (types.FunctionType,types.CellType)- Asl funksiyaning hujayralariga tegmasin
- Noma'lum nom —
ValueError - Testlarda qanday foydali bo'lishi va nega ishlab chiqarish kodida ishlatmaslik kerakligini izohlang
Vazifa 7: O'ylash
"Obyektlar — kambag'alning closure'lari" va "closure'lar — kambag'alning obyektlari" degan mashhur koan bor. Bu nimani anglatadi? Python'da closure va klass qanchalik teng kuchli, va nega Python dasturchilari holatli narsalar uchun ko'pincha klassni, JavaScript dasturchilari esa tarixan closure'ni tanlagan?
Javob
Qisqa javob: closure va obyekt bir xil g'oyaning ikki ko'rinishi — holat + shu holat ustidagi xatti-harakat. Closure'da holat hujayralarda, xatti-harakat — funksiya; obyektda holat atributlarda, xatti-harakat — metodlar. Nazariy jihatdan biri bilan ikkinchisini yasash mumkin. Amalda esa tanlovni til imkoniyatlari belgilaydi: Python klasslarni juda qulay qilgan va closure'larni yozishga cheklov qo'ygan (nonlocal, lambda — bitta ifoda), JavaScript esa uzoq vaqt klass sintaksisiga ega bo'lmagan va funksiyalarni asosiy qurilish bloki qilgan.
1. Koan
Anton van Straaten ga nisbat beriladigan qissada shogird ustoziga "obyektlar — kambag'alning closure'lari" deydi, keyinroq esa "closure'lar — kambag'alning obyektlari" degan xulosaga keladi. Ma'nosi: ikkalasini ham asosiy tushuncha deb olib, ikkinchisini undan qurish mumkin — va ikkala yo'nalishda ham nimadir yo'qotiladi.
2. Closure'dan obyekt
def hisob(balans):
def yech(summa):
nonlocal balans
if summa > balans:
raise ValueError("mablag' yetarli emas")
balans -= summa
def qoldiq():
return balans
return {"yech": yech, "qoldiq": qoldiq}
h = hisob(1000)
h["yech"](300)
h["qoldiq"]() # 700Bu — to'liq inkapsulyatsiyalangan obyekt: balans ga tashqaridan hech qanday yo'l bilan (hujayra ichiga kirmasdan) tegib bo'lmaydi. Lekin yo'qotilgan narsalar: meros, isinstance, repr, pickle, maxsus metodlar (h + boshqa), atribut sintaksisi.
3. Obyektdan closure
class Kopaytiruvchi:
def __init__(self, n):
self.n = n
def __call__(self, x):
return x * self.n
uch_barobar = Kopaytiruvchi(3)
uch_barobar(5) # 15Bu — closure'ning xatti-harakati: chaqiriladi va "eslab qolgan" holatni ishlatadi. Yo'qotilgan narsalar: qisqalik, tezlik (Python 3.14 da bu kompyuterda closure chaqiruvi ~70 ns, __call__ li nusxa ~140 ns atrofida o'lchandi — taxminan ikki barobar), va holatning yashirinligi (uch_barobar.n = 0 bilan buzish mumkin).
4. Python klassni nega afzal ko'radi
- Sintaksis:
classva metodlar — birinchi darajali, qulay;lambdaesa faqat bitta ifoda, ko'p qatorli anonim funksiya yo'q nonlocaltalabi: closure holatini o'zgartirish uchun har safar e'lon kerak — bu ataylab qiyinlashtirish- Ma'lumotlar modeli:
__repr__,__eq__,__len__va boshqa protokollar (10.15-dars) faqat klasslar orqali - Introspeksiya va vositalar:
vars(),dataclass,pickle, tur annotatsiyalari, IDE yordami - Madaniyat: "explicit is better than implicit" — ochiq atributlar va
_konvensiyasi, qat'iy yashirinlik o'rniga
Shu bilan birga Python dekoratorlar, callback'lar va fabrikalar uchun closure'ni tabiiy vosita sifatida ishlatadi — ular bitta amal uchun.
5. JavaScript closure'ni nega afzal ko'rgan
- ES2015 gacha
classsintaksisi yo'q edi; prototiplar bilan ishlash noqulay edi - Modul naqshi (IIFE — darhol chaqiriladigan funksiya) va closure — xususiy holatning yagona ishonchli usuli edi;
#xususiymaydonlar faqat ES2022 da keldi - Brauzer hodisalari va asinxron callback'lar — funksiyani uzatish kundalik ish
- Ko'p qatorli anonim funksiyalar (
function () { ... },() => { ... }) — qulay
Zamonaviy JavaScript'da class keng tarqalgan, React esa aksincha — klass komponentlaridan closure asosidagi hook'larga (useState) o'tdi. Ya'ni tanlov tilning imkoniyatlari va ekotizimi bilan birga o'zgaradi.
6. Qachon qaysi biri (Python)
| Vaziyat | Tanlov |
|---|---|
| Dekorator, callback, fabrika — bitta amal | Closure |
| Argumentlarni qotirish | functools.partial |
Bir nechta amal, ochiq holat, repr, taqqoslash |
Klass |
pickle, multiprocessing, konfiguratsiya sifatida saqlash |
Klass yoki modul funksiyasi + partial |
| Holatni tashqaridan buzib bo'lmasligi juda muhim | Closure (Python'da yagona "haqiqiy" yashirinlik) |
| Issiq siklda millionlab chaqiruv | Closure yoki partial + C funksiya |
7. Xulosa
- Closure va obyekt — holat va xatti-harakatning ikki ifodasi; biri bilan ikkinchisini yasash mumkin
- Closure'dan yasalgan obyekt meros, protokollar va introspeksiyani yo'qotadi; obyektdan yasalgan closure qisqalik va tezlikni
- Python sintaksisi va ma'lumotlar modeli klasslarni holatli narsalar uchun tabiiy qiladi, closure'larni esa bitta amal uchun
- JavaScript tarixan klass va xususiy maydonlarsiz yashagani uchun closure'ga tayangan — va bu tanlov til rivojlanishi bilan o'zgarib bormoqda
- Tanlov mezoni: amallar soni, holat ochiqligi,
pickle/protokollar kerakmi va tezlik
Nimani mustahkamlaydi: 2.1–2.7-bo'limlar.
Xulosa
Bu darsda closure'larning ichki tuzilishi va hayot davrini o'rgandik.
Eng muhim uch fikr:
Closure hujayra orqali ishlaydi — hujayra esa qiymat emas, quti. Tashqi funksiya
MAKE_CELLbilan quti yaratadi, ichki funksiyalar uni__closure__da ushlaydi vaLOAD_DEREFbilan chaqiruv paytida o'qiydi. Bir chaqiruvda yaratilgan barcha ichki funksiyalar bitta qutini bo'lishadi, har yangi chaqiruv yangi quti beradi.super()ham yashirin__class__hujayrasidan foydalanadi.Kechikkan bog'lanish va bo'sh hujayralar — shu tuzilishning oqibati. Siklda yaratilgan lambda'lar oxirgi qiymatni ko'radi, lekin darhol chaqirilgan generator ifodasida — yo'q. Yechim —
partialyoki fabrika funksiya; sukut argument esa imzoni ochib qo'yadi.except ... as eblok oxiridaeni o'chiradi va uni ushlagan closureNameErrorberadi.Closure butun obyektlarni tirik ushlaydi. Faqat kerakli qiymatni hujayraga oling. Uzoq yashaydigan callback ro'yxatiga
lambda: self...qo'yish obyektni o'chirmaydi —WeakMethodyoki bekor qilish funksiyasini ishlating. O'zini chaqiruvchi ichki funksiya aylanma havola yaratadi va faqatgcbilan tozalanadi. Bitta amal uchun closure, bir nechta amal va ochiq holat uchun klass tanlang.
Keyingi darsda functools modulining uch muhim vositasini chuqur o'rganamiz: cache va lru_cache qanday ishlaydi va qachon xotirani sizdiradi, partial closure o'rnini qanday bosadi va reduce qachon o'qilishi mumkin bo'lgan tanlov.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!