2-qism · Ma'lumot tuzilmalari va funksiyalar·07-bob
Comprehension'lar, iteratorlar va generatorlar
List/dict/set comprehension, iteratsiya protokoli qanday ishlaydi, generatorlar va yield, dangasa (lazy) hisoblash, katta ma'lumotlarni xotiraga sig'dirish va itertools.
Bu bob ikki narsani o'rgatadi. Birinchisi — comprehension: ro'yxat,
lug'at va to'plamlarni bitta o'qiladigan ifoda bilan yaratish. Ikkinchisi —
iteratorlar va generatorlar: ma'lumotni bir vaqtda emas, bittalab
ishlab chiqarish. Ikkinchisi ayniqsa AI/ML uchun muhim: 50 GB dataset
xotiraga sig'maydi, lekin generator bilan uni qatorma-qator qayta ishlash
mumkin. PyTorch'ning DataLoaderi, Hugging Face'ning streaming dataset'lari,
LLM'ning token-token javob qaytarishi (streaming) — hammasi shu g'oyaga
asoslangan.
Generatorlar va yield: xotirani tejash va dangasa hisoblash amalda. Bobdagi iteratsiya protokoli bilan birga ko'ring.
1. List comprehension#
# Odatiy sikl
kvadratlar = []
for x in range(10):
kvadratlar.append(x ** 2)
# Comprehension — bir xil natija
kvadratlar = [x ** 2 for x in range(10)]Umumiy shakl:
[ IFODA for ELEMENT in ITERABLE if SHART ]
↑ ↑ ↑
nima nimadan qaysilarini
qo'yish olish (ixtiyoriy)juftlar = [x for x in range(20) if x % 2 == 0]
ismlar = [ism.strip().title() for ism in xom_ismlar if ism.strip()]
uzunliklar = [len(s) for s in so_zlar]Shartli ifoda va filtr farqi#
[x for x in sonlar if x > 0] # filtr: musbatlarni OLADI (kam element)
[x if x > 0 else 0 for x in sonlar] # ifoda: manfiylarni 0 ga ALMASHTIRADI (element soni o'zgarmaydi)if oxirida — filtr; if/else boshida — har bir element uchun qiymat
tanlash. Bu ko'pincha chalkashtiriladi.
Ichma-ich comprehension#
matritsa = [[1, 2, 3], [4, 5, 6]]
tekis = [x for qator in matritsa for x in qator] # [1, 2, 3, 4, 5, 6]forlar oddiy ichma-ich sikl tartibida yoziladi:
tekis = []
for qator in matritsa: # birinchi for
for x in qator: # ikkinchi for
tekis.append(x)2. Dict va set comprehension#
so_zlar = ["olma", "nok", "uzum"]
{s: len(s) for s in so_zlar} # {'olma': 4, 'nok': 3, 'uzum': 4}
{v: k for k, v in lug_at.items()} # lug'atni teskari aylantirish
{k: v for k, v in config.items() if v is not None} # None qiymatlarni olib tashlash
{x % 3 for x in range(10)} # {0, 1, 2} — setAmaliy misol — API'ga faqat berilgan parametrlarni yuborish:
def build_params(model: str, max_tokens: int, temperature: float | None = None,
top_p: float | None = None) -> dict:
params = {"model": model, "max_tokens": max_tokens,
"temperature": temperature, "top_p": top_p}
return {k: v for k, v in params.items() if v is not None}
build_params("claude-opus-5-5", 1024) # {'model': 'claude-opus-5-5', 'max_tokens': 1024}Comprehension'ning o'z doirasi bor#
x = "tashqi"
kv = [x for x in range(3)]
print(x) # 'tashqi' — comprehension ichidagi x tashqariga "oqib chiqmaydi"3. Iteratsiya qanday ishlaydi#
for x in narsa: yozganingizda Python aslida nima qiladi? Bu savolning
javobi — iteratsiya protokoli:
iter() — iterator olish
Python
iter(narsa)ni chaqiradi. Bunarsa.__iter__()metodini ishlatadi va iterator obyektini qaytaradi.next() — keyingi elementni so'rash
Har bir qadamda
next(iterator)chaqiriladi (iterator.__next__()) va keyingi element qaytariladi.StopIteration — tugadi
Elementlar tugaganda
__next__StopIterationxatosini ko'taradi.forsikli uni ushlab, jimgina to'xtaydi.
Buni qo'lda qilib ko'ramiz:
mevalar = ["olma", "nok"]
it = iter(mevalar)
next(it) # 'olma'
next(it) # 'nok'
next(it) # StopIteration
next(it, "tugadi") # 'tugadi' — standart qiymat bilan xato chiqmaydiDemak for sikli aslida shu:
it = iter(mevalar)
while True:
try:
meva = next(it)
except StopIteration:
break
print(meva)Iterable va iterator — farqi#
| Iterable | Iterator | |
|---|---|---|
| Ta'rif | iter() ga berish mumkin bo'lgan narsa | next() ga berish mumkin bo'lgan narsa |
| Misollar | list, str, dict, range, fayl | iter(lst), generator, fayl, zip(...), map(...) |
| Qayta aylanish | Ha — har safar yangi iterator | Yo'q — bir marta ishlatiladi |
| Holat saqlaydimi | Yo'q | Ha (qayerda turganini biladi) |
Oxirgi qator — eng ko'p xatoga sabab bo'ladigan fakt:
juftlar = zip([1, 2, 3], "abc") # zip — iterator
print(list(juftlar)) # [(1, 'a'), (2, 'b'), (3, 'c')]
print(list(juftlar)) # [] — tugab bo'lgan!kv = (x * x for x in range(3)) # generator ifodasi — iterator
print(sum(kv)) # 5
print(sum(kv)) # 0 — "bo'sh" generator, xato yo'q!4. Generatorlar — yield#
Generator funksiyasi — return o'rniga yield ishlatadigan funksiya.
U chaqirilganda kod bajarilmaydi — generator obyekti qaytadi. Kod har
next() chaqiruvida keyingi yieldgacha bajariladi va to'xtab turadi:
def hisobla():
print("boshlandi")
yield 1
print("o'rtada")
yield 2
print("tugadi")
g = hisobla() # hech narsa chiqmaydi — kod hali ishlamadi
for v in g:
print("olindi", v)boshlandi
olindi 1
o'rtada
olindi 2
tugadiGenerator o'zining lokal o'zgaruvchilari va qayerda to'xtaganini eslab qoladi. Bu — oddiy funksiyaning "muzlatilgan" holati.
Nega generator kerak: xotira#
import sys
ro_yxat = [x * x for x in range(1_000_000)]
gen = (x * x for x in range(1_000_000))
sys.getsizeof(ro_yxat) # ~8 400 000 bayt (8 MB, va bu faqat ko'rsatkichlar!)
sys.getsizeof(gen) # ~200 bayt — hajmdan qat'i nazarGenerator hech qachon hamma qiymatlarni xotirada saqlamaydi — har birini kerak bo'lganda hisoblaydi.
Katta faylni qayta ishlash#
import json
from collections.abc import Iterator
def read_jsonl(path: str) -> Iterator[dict]:
"""JSONL faylni qatorma-qator o'qiydi — fayl hajmi xotiraga ta'sir qilmaydi."""
with open(path, encoding="utf-8") as f:
for line_no, line in enumerate(f, 1):
line = line.strip()
if not line:
continue
try:
yield json.loads(line)
except json.JSONDecodeError:
print(f"⚠️ {line_no}-qator buzilgan, o'tkazib yuborildi")
def filter_long(records: Iterator[dict], min_len: int) -> Iterator[dict]:
for r in records:
if len(r.get("text", "")) >= min_len:
yield r
def batched(items: Iterator[dict], size: int) -> Iterator[list[dict]]:
batch = []
for item in items:
batch.append(item)
if len(batch) == size:
yield batch
batch = []
if batch:
yield batch
# Pipeline: hech qaysi qadam butun faylni xotiraga yuklamaydi
records = read_jsonl("dataset.jsonl")
long_records = filter_long(records, min_len=200)
for batch in batched(long_records, size=32):
process(batch) # masalan, modelga yuborishBu dangasa pipeline (lazy pipeline): har bir bosqich keyingisidan
element "so'raydi", hech kim butun ma'lumotni ushlab turmaydi. 100 GB
fayl bo'lsa ham, xotirada bir vaqtning o'zida faqat bitta batch bo'ladi.
Hugging Face datasets kutubxonasining streaming=True rejimi aynan shu
tamoyilda ishlaydi.
Cheksiz generatorlar#
def fibonacci():
a, b = 0, 1
while True: # cheksiz — lekin muammo emas, chunki dangasa
yield a
a, b = b, a + b
from itertools import islice
list(islice(fibonacci(), 10)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]yield from — boshqa generatorga uzatish#
def flatten(nested):
for item in nested:
if isinstance(item, list):
yield from flatten(item) # ichki generatorning hamma qiymatlarini uzatadi
else:
yield item
list(flatten([1, [2, [3, 4]], 5])) # [1, 2, 3, 4, 5]5. Generator ifodalari#
Comprehension'ni [] o'rniga () bilan yozsangiz — generator ifodasi:
sum(x * x for x in range(10)) # funksiyaning yagona argumenti bo'lsa, qo'shimcha qavs shart emas
any(line.startswith("ERROR") for line in log) # birinchi topilganda to'xtaydi!
max(len(s) for s in so_zlar)
", ".join(str(x) for x in sonlar)any() va all() bilan generator ifodasi ayniqsa samarali: any birinchi
True topilganda to'xtaydi va qolgan elementlarni umuman hisoblamaydi.
Qachon ro'yxat, qachon generator?
Ro'yxat [...] | Generator (...) |
|---|---|
| Natijani bir necha marta ishlatasiz | Bir marta aylanasiz |
len(), indeks, slicing kerak | Faqat ketma-ket o'qish |
| Ma'lumot kichik | Ma'lumot katta yoki cheksiz |
| Debug qilish (ko'rish) oson bo'lishi kerak | Xotira muhim |
6. map, filter — va nega comprehension afzal#
list(map(str.upper, ["a", "b"])) # ['A', 'B']
list(filter(lambda x: x > 0, [-1, 2, 3])) # [2, 3]
list(filter(None, [0, 1, "", 2])) # [1, 2] — falsy'larni olib tashlaydiPython 3'da map va filter iterator qaytaradi. Ular ishlaydi, lekin
Python jamiyatida comprehension ko'proq o'qiladigan deb hisoblanadi:
[s.upper() for s in lst]. map tayyor funksiya bilan (masalan,
map(int, satrlar)) qisqa va chiroyli — lambda kerak bo'lsa,
comprehension yozing.
7. itertools — iteratorlar uchun asboblar qutisi#
import itertools as it
it.count(10, 5) # 10, 15, 20, ... (cheksiz)
it.cycle("ab") # a, b, a, b, ... (cheksiz)
it.repeat("x", 3) # x, x, x
it.islice(gen, 5) # iteratordan "slice" — birinchi 5 tasi
it.chain([1, 2], [3]) # 1, 2, 3 — ketma-ket ulash
it.chain.from_iterable(ro_yxatlar) # ro'yxatlar ro'yxatini tekislash
it.batched(range(7), 3) # (0, 1, 2), (3, 4, 5), (6,) — Python 3.12+
it.pairwise([1, 2, 3, 4]) # (1, 2), (2, 3), (3, 4) — Python 3.10+
it.accumulate([1, 2, 3, 4]) # 1, 3, 6, 10 — yig'ma yig'indi
it.zip_longest([1, 2], [1], fillvalue=0) # (1, 1), (2, 0)
it.takewhile(lambda x: x < 3, [1, 2, 5, 1]) # 1, 2 — shart buzilgunchaKombinatorika — hyperparameter qidirish#
it.product("ab", [0, 1]) # ('a', 0), ('a', 1), ('b', 0), ('b', 1)
it.combinations([1, 2, 3], 2) # (1, 2), (1, 3), (2, 3)
it.permutations([1, 2, 3], 2) # (1, 2), (1, 3), (2, 1), ... — tartib muhimproduct — grid search'ning asosi:
grid = {
"lr": [1e-4, 3e-4, 1e-3],
"batch_size": [16, 32],
"dropout": [0.1, 0.3],
}
keys = list(grid)
for values in it.product(*grid.values()):
params = dict(zip(keys, values))
print(params) # 3 * 2 * 2 = 12 ta kombinatsiyagroupby — ketma-ket guruhlash#
[(k, list(g)) for k, g in it.groupby("aaabbc")]
# [('a', ['a', 'a', 'a']), ('b', ['b', 'b']), ('c', ['c'])]8. Performans haqida halol gap#
"Comprehension sikldan tezroq" degan gap to'g'ri, lekin farq odatda kichik (zamonaviy Python'da 10–30% atrofida): ikkalasi ham Python bytecode'da ishlaydi. Comprehension'ni tezlik uchun emas, o'qilishi uchun tanlang.
Haqiqiy tezlik farqi (10–100 baravar) — sof Python siklidan vektorlashtirilgan NumPy amaliga o'tishda keladi:
import numpy as np
sonlar = list(range(1_000_000))
sum(x * x for x in sonlar) # ~25–50 ms (kompyuterga bog'liq)
arr = np.arange(1_000_000)
(arr * arr).sum() # ~1 ms19-bobda bu farqning sababini to'liq ochamiz.
Nima noto'g'ri ketishi mumkin#
| Holat | Natija | To'g'risi |
|---|---|---|
| Iteratorni ikki marta aylanish | Ikkinchisi bo'sh — xatosiz! | list() ga aylantiring yoki qayta yarating |
zip, map, filter natijasini ikki marta ishlatish | Xuddi shu | list(zip(...)) |
Katta fayl uchun f.readlines() | Butun fayl xotirada | for line in f: |
| Juda murakkab comprehension | O'qilmaydi | Oddiy sikl yoki funksiya |
[x if x > 0 for x in lst] | SyntaxError | Filtr uchun if oxirida; ifoda uchun if/else boshida |
groupby saralanmagan ma'lumotda | Bitta kalit bir necha guruhda | Avval sorted(..., key=) |
Generatorga len() | TypeError | Sanash uchun sum(1 for _ in gen) (generatorni tugatadi!) |
| Generator ichida xato | Faqat iteratsiya paytida chiqadi | Pipeline'ni kichik ma'lumotda sinab ko'ring |
1. Comprehension'ga aylantiring
Quyidagi sikllarni comprehension bilan qayta yozing:
a = []
for s in so_zlar:
if len(s) > 3:
a.append(s.lower())
b = {}
for i, s in enumerate(so_zlar):
b[s] = i
c = []
for x in sonlar:
if x < 0:
c.append(0)
else:
c.append(x)Maslahatni ko'rsatish
Har biri bitta qator: filtr if oxirida, almashtirish if/else boshida, lug'at uchun {k: v for ...}.
2. Matnni bo'laklash (chunking)
RAG tizimlarida hujjat bo'laklarga bo'linadi. chunk_text(text, size, overlap)
generatorini yozing: har bir bo'lak size belgidan, qo'shni bo'laklar
overlap belgi bilan ustma-ust tushsin. list(chunk_text("abcdefghij", 4, 1))
→ ['abcd', 'defg', 'ghij', 'j']. Oxirgi juda qisqa bo'lakni (masalan,
overlapdan qisqa) tashlab yuborish variantini ham qo'shing.
Maslahatni ko'rsatish
Generator: start=0; while start < len(text): yield text[start:start+size]; start += size - overlap. overlap >= size bo'lsa ValueError.
3. Log tahlili pipeline'i
Million qatorli log faylidan (o'zingiz generator bilan yasang:
f"2026-10-01 {random.choice(['INFO','WARN','ERROR'])} msg {i}")
generatorlar zanjiri orqali faqat ERROR qatorlarini olib, ularning
soatlar bo'yicha taqsimotini Counter bilan hisoblang. Xotira
sarfini tracemalloc bilan o'lchab, ro'yxatli versiya bilan solishtiring.
Maslahatni ko'rsatish
read_lines → parse (generator) → filter (generator) → Counter. Hech qaysi bosqich ro'yxat yaratmasin.
4. Grid search
itertools.product yordamida {"model": ["small", "large"], "lr": [0.1, 0.01], "epochs": [5, 10]}
gridining barcha kombinatsiyalarini lug'atlar ro'yxati sifatida yarating.
Har biri uchun soxta "score" hisoblang (random.random(), seed bilan) va
eng yaxshi 3 tasini chiqaring.
- 01Comprehension: [ifoda for x in iterable if shart]; lug'at va to'plam uchun ham; o'qilishi uchun tanlang.
- 02for sikli iter() va next() ustida ishlaydi; StopIteration tugashni bildiradi.
- 03Iterator bir marta ishlatiladi va ikkinchi marta xatosiz bo'sh qaytadi — eng xavfli tuzoq.
- 04yield — funksiyani 'muzlatib' qiymat qaytaradi; generator xotirada faqat joriy holatni saqlaydi.
- 05Generatorlar zanjiri — katta ma'lumotlar uchun dangasa pipeline.
- 06itertools: islice, chain, batched, pairwise, product, groupby — tayyor asboblar.
- 07Katta tezlik farqi comprehension'dan emas, vektorlashtirishdan (NumPy) keladi.
Mustahkamlash
Test va amaliy masalalar
Avval testni ishlang — har javobdan keyin izoh chiqadi. Keyin masalalarni shu yerning o'zida yechib, brauzerdagi haqiqiy Python bilan avtomatik tekshiring. Natijalaringiz kurs progressi sahifasida jamlanadi.
Bilimni tekshirish
Bob bo'yicha test
12 ta savol · har javobdan keyin to'g'ri yoki xatoligi va izoh ko'rsatiladi · oxirida natija foizda.
Amaliy masalalar
Kodni yozing va «Tekshirish»ni bosing (yoki Ctrl/⌘ + Enter). Kodingiz shu brauzerda saqlanib turadi.
01Matnni bo'laklash (generator)
chunk_text(text, size, overlap) — generator bo'lsin: har bo'lak size belgi, qo'shni bo'laklar overlap belgi ustma-ust. list(chunk_text("abcdefghij", 4, 1)) → ['abcd', 'defg', 'ghij'] — oxirgi bo'lak avvalgisi ichida to'liq bo'lsa, qo'shilmaydi. overlap >= size bo'lsa ValueError.
02Batch generatori
batched(items, size) — istalgan iterable'dan (generator ham bo'lishi mumkin) size tadan ro'yxatlar chiqaruvchi generator. Oxirgi to'liq bo'lmagan batch ham chiqsin. len() ishlatmang — kirish generator bo'lishi mumkin.
03Parametrlar filtri (dict comprehension)
build_params(**kwargs) — None bo'lmagan barcha argumentlardan lug'at qaytarsin, bitta qator dict comprehension bilan. build_params(model="opus", max_tokens=100, system=None) → {"model": "opus", "max_tokens": 100}.