Generator va yield — lazy evaluation, xotira tejash
Muammodan boshlaylik
Server log fayli — 8 GB. Undan faqat ERROR qatorlarini topish kerak:
def xatolarni_ol(yol):
natija = []
with open(yol, encoding="utf-8") as f:
for qator in f:
if "ERROR" in qator:
natija.append(qator)
return natija # millionlab qator — hammasi xotirada
Funksiya avval hamma natijani ro'yxatga yig'adi, keyin qaytaradi. Kompyuterda 4 GB RAM — dastur yiqiladi. Va siz faqat birinchi 10 ta xatoni ko'rmoqchi edingiz.
Generator nima?
Generator — qiymatlarni birdaniga emas, birma-bir, so'ralganda ishlab chiqaradigan funksiya. return o'rniga — yield:
def xatolarni_ol(yol):
with open(yol, encoding="utf-8") as f:
for qator in f:
if "ERROR" in qator:
yield qator # bittasini berib, shu yerda "pauza"
for xato in xatolarni_ol("server.log"):
print(xato)
Xotirada bir vaqtda faqat bitta qator. Fayl 8 GB yoki 800 GB — farqi yo'q.
Hayotiy o'xshatma
Bufet va oshpaz. Ro'yxat — bufet: barcha taomlar oldindan tayyorlangan va stolga qo'yilgan (xotira band). Generator — buyurtma bo'yicha oshpaz: siz "keyingisi" deganingizda bitta taom tayyorlaydi. Agar 3 taomdan keyin to'ysangiz — qolganlari umuman pishirilmaydi.
yield qanday ishlaydi
def sanash():
print("Boshlandi")
yield 1
print("Davom")
yield 2
print("Oxiri")
yield 3
gen = sanash() # hech narsa chiqmaydi! funksiya hali ishga tushmagan
print(gen) # <generator object sanash at 0x...>
print(next(gen)) # Boshlandi → 1
print(next(gen)) # Davom → 2
print(next(gen)) # Oxiri → 3
print(next(gen)) # StopIteration — qiymatlar tugadi
- Generator funksiyasini chaqirish — generator ob'ekti qaytaradi, kod bajarilmaydi
next()— keyingiyieldgacha bajaradi, qiymatni qaytaradi va to'xtaydi- Keyingi
next()— aynan to'xtagan joydan davom etadi; barcha local o'zgaruvchilar saqlangan - Funksiya tugasa —
StopIteration
for sikl next() ni o'zi chaqiradi va StopIteration da jim to'xtaydi:
for son in sanash():
print(son)
Lazy evaluation — kerak bo'lganda hisoblash
Cheksiz ketma-ketlik
def fibonachchi():
a, b = 0, 1
while True: # cheksiz!
yield a
a, b = b, a + b
for i, son in enumerate(fibonachchi()):
if i == 10:
break
print(son, end=" ") # 0 1 1 2 3 5 8 13 21 34
Ro'yxat bilan cheksiz ketma-ketlik — imkonsiz. Generator bilan — faqat so'ralgancha hisoblanadi.
Xotira farqi
import sys
def kvadratlar_royxat(n):
return [i * i for i in range(n)]
def kvadratlar_gen(n):
for i in range(n):
yield i * i
print(sys.getsizeof(kvadratlar_royxat(1_000_000))) # ~8 000 000 bayt
print(sys.getsizeof(kvadratlar_gen(1_000_000))) # ~200 bayt
print(sum(kvadratlar_gen(1_000_000))) # hisoblaydi, xotira tejab
Generator expression (16-dars) — xuddi shu, bir qatorda: (i * i for i in range(n)).
Generator zanjiri — pipeline
Har bir bosqich — kichik generator; ma'lumot ular orqali birma-bir oqib o'tadi:
def qatorlar(yol):
with open(yol, encoding="utf-8") as f:
yield from f # har bir qatorni beradi
def faqat_xatolar(qatorlar):
for q in qatorlar:
if "ERROR" in q:
yield q
def vaqt_va_xabar(qatorlar):
for q in qatorlar:
vaqt, _, xabar = q.partition(" ERROR ")
yield vaqt.strip(), xabar.strip()
pipeline = vaqt_va_xabar(faqat_xatolar(qatorlar("server.log")))
for vaqt, xabar in pipeline:
print(f"[{vaqt}] {xabar}")
8 GB fayl, uch bosqich — xotirada baribir bitta qator. Har bir funksiya kichik, alohida test qilinadi.
yield from
def ichma_ich(royxat):
for el in royxat:
if isinstance(el, list):
yield from ichma_ich(el) # ichki generatorning barcha qiymatlarini uzatish
else:
yield el
print(list(ichma_ich([1, [2, [3, 4]], 5]))) # [1, 2, 3, 4, 5]
yield from boshqa — for x in boshqa: yield x ning qisqa shakli.
Bo'laklab qayta ishlash
def bolaklar(iterable, hajm):
bolak = []
for el in iterable:
bolak.append(el)
if len(bolak) == hajm:
yield bolak
bolak = []
if bolak:
yield bolak
for partiya in bolaklar(range(10), 4):
print(partiya)
# [0, 1, 2, 3]
# [4, 5, 6, 7]
# [8, 9]
Millionlab yozuvni bazaga 1000 tadan yozish, API ga partiyalab yuborish uchun. Python 3.12+ da tayyor: itertools.batched(iterable, 4) (46-dars).
Generator bir marta ishlatiladi
gen = (x * 2 for x in range(3))
print(list(gen)) # [0, 2, 4]
print(list(gen)) # [] — tugagan
[!WARNING] Generatorni ikki marta aylanib chiqib bo'lmaydi va unda
len()yoki indeks (gen[0]) yo'q. Ma'lumot bir necha marta kerak bo'lsa —list(gen)bilan ro'yxatga aylantiring (agar xotiraga sig'sa) yoki generator funksiyasini qayta chaqiring.
Qachon generator
| Vaziyat | Tanlov |
|---|---|
| Katta fayl, ko'p yozuvlar | Generator |
| Cheksiz yoki uzunligi noma'lum ketma-ketlik | Generator |
Bir marta aylanib chiqish, sum/max/any |
Generator |
| Bosqichli qayta ishlash (pipeline) | Generator |
Bir necha marta aylanish, len, indeks, saralash |
List |
| Kichik ma'lumot | Ikkalasi ham; list soddaroq |
[!INFO] Siz generatorlarni allaqachon ko'p ishlatgansiz: fayl ob'ekti (
for qator in f),range(),dict.items(),zip(),enumerate(),map()— hammasi qiymatlarni "lazy" tarzda, birma-bir beradi.
Tez-tez uchraydigan xatolar
Generator funksiyasini chaqirib, natija kutish:
natija = sanash()
print(natija) # ❌ <generator object> — ro'yxat emas
print(list(natija)) # ✅
yield va return qiymatini aralashtirish:
Generator ichidagi return qiymat — for siklga berilmaydi; u faqat generatorni tugatadi.
Generatorni ikki marta ishlatish — ikkinchi safar bo'sh.
Fayl yopilgandan keyin generator:
def ol():
with open("a.txt") as f:
return (q for q in f) # ❌ with tugadi — fayl yopiq
yield bilan yozing — with generator tugaguncha ochiq qoladi.
Xulosa
Generator — qiymatlarni so'ralganda birma-bir beruvchi funksiya; xotirani tejaydi.
Eslab qoling:
yield— qiymat berib, shu joyda pauza; keyinginext()— davom- Chaqirish — generator ob'ekti; kod
next()/forda bajariladi - Lazy: cheksiz ketma-ketliklar, ulkan fayllar
- Pipeline — generatorlar zanjiri;
yield from - Bir marta ishlatiladi;
lenva indeks yo'q (x for x in ...)— generator expression
Keyingi dars
46-dars: itertools — chain, combinations, permutations, product, islice