IlmHamroh
Python kursi/Ilgor til imkoniyatlari1/17-dars27 daqiqa
Mundarija (23)

10.1-dars: Iterator va iterable

10-QISM — ILG'OR TIL IMKONIYATLARI · 1-dars


1. Kirish va motivatsiya

Bu kod nima chiqaradi?

python
sonlar = map(int, ["1", "2", "3"])

print(sum(sonlar))
print(sum(sonlar))

Ko'pchilik 6 va 6 deb javob beradi. Aslida:

text
6
0

Ikkinchi sum bo'sh narsani qo'shdi. sonlar ro'yxat emas edi — u iterator, va iterator faqat bir marta aylanadi.

Endi bunisi:

python
royxat = [1, 2, 3]

print(sum(royxat))
print(sum(royxat))
text
6
6

Ro'yxat — iterable. Undan istalgancha marta yangi iterator olish mumkin.

Bu farqni tushunmaslik real loyihalarda juda sirli xatolarga olib keladi:

  • Fayldan o'qilgan qatorlar ikkinchi marta "yo'qolib qoladi"
  • zip bilan juftlangan ma'lumotning yarmi "g'oyib" bo'ladi
  • if x in generator: tekshiruvidan keyin sikl elementlarni o'tkazib yuboradi
  • Funksiya bir marta to'g'ri, ikkinchi marta bo'sh natija qaytaradi

Bu darsda:

  • Iterable va iterator — ikki xil tushuncha
  • for sikli aslida qanday ishlaydi
  • iter() va next() — protokolning ikki funksiyasi
  • Iteratorning tugashi va bir martalik tabiati
  • iter(funksiya, belgi) — kam ma'lum ikki argumentli shakl
  • Dangasa hisoblash va xotira
  • Keng tarqalgan tuzoqlar

Keyingi darsda 10.2-bob o'z iterator sinflaringizni yozasiz. Bu darsda esa protokolni ishlatish va ichidan tushunishni o'rganamiz.


2. Nazariya — chuqur tushuntirish

2.1. Ikki tushuncha

Iterable — "aylanib chiqsa bo'ladigan" obyekt. U iter() ga berilganda iterator qaytaradi.

Iterator — "hozir qayerda turganini biladigan" obyekt. U next() ga berilganda keyingi elementni qaytaradi, elementlar tugasa StopIteration ko'taradi.

python
royxat = [10, 20, 30]           # iterable

it = iter(royxat)               # iterator
next(it)                        # 10
next(it)                        # 20
next(it)                        # 30
next(it)                        # ❌ StopIteration

O'xshatish:

Kitob (iterable) Xatcho'p (iterator)
Nima Mazmun Joriy sahifa
Nechta Bitta Istalgancha
Holati O'zgarmaydi Oldinga siljiydi
Qayta o'qish Yangi xatcho'p qo'ying Orqaga qaytmaydi

Bitta kitobga bir nechta xatcho'p qo'yish mumkin — har biri o'z sahifasida turadi. Xuddi shunday, bitta ro'yxatdan bir nechta mustaqil iterator olish mumkin.

2.2. Protokol: __iter__ va __next__

Python bu tushunchalarni ikki "dunder" metod orqali aniqlaydi:

Tushuncha Qaysi metod kerak Chaqiruvchi
Iterable __iter__() → iterator qaytaradi iter(obj)
Iterator __next__() → keyingi element next(it)
Iterator __iter__() → o'zini qaytaradi iter(it)

Muhim qoida: har bir iterator ham iterable. Uning __iter__ metodi self ni qaytaradi. Shuning uchun iteratorni ham for ga berish mumkin:

python
it = iter([1, 2, 3])
iter(it) is it                  # True — iterator o'zini qaytaradi

Lekin teskarisi to'g'ri emas: ro'yxat iterable, ammo iterator emas:

python
royxat = [1, 2, 3]
iter(royxat) is royxat          # False — har safar YANGI iterator
next(royxat)                    # ❌ TypeError: 'list' object is not an iterator

2.3. for sikli aslida nima qiladi

Bu kod:

python
for x in royxat:
    print(x)

Python uchun taxminan quyidagicha:

python
_it = iter(royxat)              # 1. iterator olish
while True:
    try:
        x = next(_it)           # 2. keyingi element
    except StopIteration:       # 3. tugadi — sikldan chiqish
        break
    print(x)                    # 4. sikl tanasi

Shundan uchta muhim xulosa:

  1. for har safar boshida iter() ni bir marta chaqiradi
  2. Elementlar next() bilan birma-bir olinadi — hammasi oldindan xotiraga yuklanmaydi
  3. StopIteration — xato emas, "tugadi" signali

Xuddi shu protokolni ishlatadigan boshqa joylar:

python
list(x)   tuple(x)   set(x)   dict(x)     # konstruktorlar
sum(x)    min(x)     max(x)   sorted(x)   # yig'uvchilar
a, b, c = x                               # ochish
[*x]      {*x}       f(*x)                # yulduzcha
x in y                                    # (y da __contains__ bo'lmasa)
zip(x, y) map(f, x)  enumerate(x)         # moslashtiruvchilar

2.4. Iterator bir martalik

Iterator faqat oldinga yuradi va qaytmaydi:

python
it = iter([1, 2, 3])
list(it)                        # [1, 2, 3]
list(it)                        # []  ⚠️ tugagan

Qaysi obyektlar iterator (bir martalik) va qaysilari iterable (qayta ishlatiladigan):

Qayta aylanadi (iterable) Bir martalik (iterator)
list, tuple, str iter(...) natijasi
dict, set, frozenset map(...), filter(...), zip(...)
range(...) enumerate(...), reversed(...)
dict.keys(), .values(), .items() generator ifodasi (x for x in ...)
fayl obyekti open(...)
itertools funksiyalari natijasi

range va dict.keys() ko'pincha iterator deb o'ylanadi, lekin ular qayta ishlatiladigan iterable:

python
r = range(3)
list(r), list(r)                # ([0, 1, 2], [0, 1, 2])  ✅

Qanday tekshirish:

python
iter(x) is x                    # True → iterator (bir martalik)
                                # False → iterable konteyner

2.5. next() ning sukut qiymati

next() ikkinchi argument oladi — tugaganda StopIteration o'rniga shu qaytadi:

python
it = iter([])
next(it, None)                  # None — xato yo'q
next(it, "tugadi")              # 'tugadi'

Bu "birinchi mos elementni topish"da juda qulay:

python
birinchi_juft = next((x for x in sonlar if x % 2 == 0), None)

2.6. iter(funksiya, belgi) — ikki argumentli shakl

iter() ning kam ma'lum shakli: funksiyani belgi qiymat qaytarguncha qayta-qayta chaqiradi.

python
iter(chaqiriluvchi, belgi)
python
import random

zarlar = iter(lambda: random.randint(1, 6), 6)
list(zarlar)                    # 6 chiqquncha tashlanganlar, masalan [3, 1, 4]

Klassik qo'llanilishi — faylni bo'laklab o'qish:

python
with open("katta.bin", "rb") as f:
    for bolak in iter(lambda: f.read(4096), b""):
        ishla(bolak)            # b"" — fayl oxiri belgisi

while True + if not bolak: break o'rniga bitta toza qator.

2.7. Dangasa hisoblash

Iterator elementni so'ralganda hisoblaydi. Bu ikki katta foyda beradi:

1. Xotira:

python
sum([x * x for x in range(10_000_000)])     # ⚠️ 10M elementli ro'yxat yaratiladi
sum(x * x for x in range(10_000_000))       # ✅ bir vaqtda bitta element

2. Erta to'xtash:

python
katta = map(qimmat_hisob, range(1_000_000))
next(x for x in katta if x > 100)           # birinchisi topilishi bilan to'xtaydi

Lekin dangasalikning narxi bor: xato ham kechikadi.

python
natijalar = map(int, ["1", "2", "uch"])     # ❌ hali xato YO'Q
print("tayyor")                             # bajariladi
list(natijalar)                             # ❌ ValueError — faqat shu yerda

2.8. Iteratsiya paytida o'zgartirish

python
d = {"a": 1, "b": 2}
for k in d:
    d["c"] = 3                  # ❌ RuntimeError: dictionary changed size during iteration

set da ham xuddi shunday. Ro'yxatda esa xato chiqmaydi, lekin elementlar o'tkazib yuboriladi (5.7-dars):

python
r = [1, 2, 2, 3]
for x in r:
    if x == 2:
        r.remove(x)             # ⚠️ ikkinchi 2 o'tkazib yuboriladi
# r == [1, 2, 3]

Yechim — nusxa ustida aylanish yoki yangi to'plam yasash:

python
for k in list(d):  ...          # kalitlar nusxasi
r = [x for x in r if x != 2]    # yangi ro'yxat

2.9. collections.abc bilan tekshirish

python
from collections.abc import Iterable, Iterator

isinstance([1, 2], Iterable)            # True
isinstance([1, 2], Iterator)            # False
isinstance(iter([1, 2]), Iterator)      # True

isinstance(x, Iterable) faqat __iter__ borligini tekshiradi. Faqat eski __getitem__ protokoli bilan ishlaydigan obyektlarni u ko'rmaydi (8.14-dars). Eng ishonchli tekshiruv — urinib ko'rish:

python
try:
    iter(x)
except TypeError:
    print("aylanib bo'lmaydi")

3. Tez ma'lumotnoma

Tushunchalar

Iterable   __iter__() → iterator          list, dict, str, range, set
Iterator   __next__() → element           map, zip, filter, generator, fayl
           __iter__() → self              (iterator ham iterable)

Asosiy funksiyalar

python
iter(x)                  iterator olish (TypeError — aylanmaydi)
next(it)                 keyingi element (StopIteration — tugadi)
next(it, sukut)          tugasa sukut qaytaradi
iter(f, belgi)           f() belgi bermaguncha chaqiradi
iter(x) is x             True → bir martalik iterator

for ning ichki ishi

python
_it = iter(x)
while True:
    try: el = next(_it)
    except StopIteration: break
    ...

Tuzoqlar

Iteratorni ikki marta aylanish           ikkinchisi bo'sh
x in iterator                            elementlarni iste'mol qiladi
zip(it, it)                              bitta iteratordan ikki marta oladi
dict/set ni aylanishda o'zgartirish     RuntimeError
map/filter dagi xato                     faqat iste'mol qilinganda chiqadi

4. Batafsil misollar

Misol 1 — Protokolni ichidan ko'rish

python
"""Iterable va iterator protokolini qadamma-qadam kuzatish."""

from collections.abc import Iterable, Iterator

print("=== 1. iter() va next() qo'lda ===")
royxat = ["olma", "anor", "behi"]
it = iter(royxat)
print(f"  royxat turi:   {type(royxat).__name__}")
print(f"  iterator turi: {type(it).__name__}")
print(f"  next(it) → {next(it)!r}")
print(f"  next(it) → {next(it)!r}")
print(f"  next(it) → {next(it)!r}")
try:
    next(it)
except StopIteration:
    print("  next(it) → ❌ StopIteration  (elementlar tugadi)")

print("\n=== 2. Iterator o'zini qaytaradi, konteyner — yo'q ===")
it = iter(royxat)
print(f"  iter(it) is it           → {iter(it) is it}")
print(f"  iter(royxat) is royxat   → {iter(royxat) is royxat}")
print(f"  iter(royxat) is iter(royxat) → {iter(royxat) is iter(royxat)}")
try:
    next(royxat)
except TypeError as e:
    print(f"  next(royxat) → ❌ TypeError: {e}")

print("\n=== 3. Qaysi obyekt bir martalik? ===")
NAMUNALAR = [
    ("list", [1, 2, 3]),
    ("tuple", (1, 2, 3)),
    ("str", "abc"),
    ("dict", {"a": 1}),
    ("dict.items()", {"a": 1}.items()),
    ("set", {1, 2}),
    ("range(3)", range(3)),
    ("map(...)", map(str, [1, 2])),
    ("filter(...)", filter(None, [0, 1])),
    ("zip(...)", zip("ab", "cd")),
    ("enumerate(...)", enumerate("ab")),
    ("reversed(...)", reversed([1, 2])),
    ("(x for x in ...)", (x for x in [1, 2])),
]
print(f"  {'Obyekt':<20} {'Iterable':<10} {'Iterator':<10} {'Ikkinchi list()'}")
print("  " + "─" * 62)
for nom, obj in NAMUNALAR:
    birinchi = list(obj)
    ikkinchi = list(obj)
    holat = "✅ bir xil" if birinchi == ikkinchi else f"⚠️ {ikkinchi}"
    print(f"  {nom:<20} {str(isinstance(obj, Iterable)):<10} "
          f"{str(isinstance(obj, Iterator)):<10} {holat}")

print("\n=== 4. for sikli qo'lda ===")


def mening_for(iterable, tana):
    """for x in iterable: tana(x) — ning while bilan yozilgani."""
    _it = iter(iterable)
    qadam = 0
    while True:
        try:
            x = next(_it)
        except StopIteration:
            print(f"    StopIteration → {qadam} qadamdan keyin to'xtadi")
            break
        qadam += 1
        tana(x)


mening_for("Hi!", lambda harf: print(f"    element: {harf!r}"))

print("\n=== 5. Mustaqil iteratorlar ===")
harflar = "abcd"
it1 = iter(harflar)
it2 = iter(harflar)
print(f"  next(it1), next(it1) → {next(it1)!r}, {next(it1)!r}")
print(f"  next(it2)            → {next(it2)!r}   ⭐ it2 boshidan boshladi")
print(f"  next(it1)            → {next(it1)!r}   ⭐ it1 o'z joyidan davom etdi")

print("\n=== 6. next() ning sukut qiymati ===")
sonlar = [3, 7, 9, 12, 15]
birinchi_juft = next((x for x in sonlar if x % 2 == 0), None)
birinchi_manfiy = next((x for x in sonlar if x < 0), None)
print(f"  birinchi juft:   {birinchi_juft}")
print(f"  birinchi manfiy: {birinchi_manfiy}   (topilmadi — xato yo'q)")

Natijaning muhim qismi:

text
=== 1. iter() va next() qo'lda ===
  royxat turi:   list
  iterator turi: list_iterator
  next(it) → 'olma'
  next(it) → 'anor'
  next(it) → 'behi'
  next(it) → ❌ StopIteration  (elementlar tugadi)

=== 2. Iterator o'zini qaytaradi, konteyner — yo'q ===
  iter(it) is it           → True
  iter(royxat) is royxat   → False
  iter(royxat) is iter(royxat) → False
  next(royxat) → ❌ TypeError: 'list' object is not an iterator

=== 3. Qaysi obyekt bir martalik? ===
  Obyekt               Iterable   Iterator   Ikkinchi list()
  ──────────────────────────────────────────────────────────────
  list                 True       False      ✅ bir xil
  tuple                True       False      ✅ bir xil
  str                  True       False      ✅ bir xil
  dict                 True       False      ✅ bir xil
  dict.items()         True       False      ✅ bir xil
  set                  True       False      ✅ bir xil
  range(3)             True       False      ✅ bir xil
  map(...)             True       True       ⚠️ []
  filter(...)          True       True       ⚠️ []
  zip(...)             True       True       ⚠️ []
  enumerate(...)       True       True       ⚠️ []
  reversed(...)        True       True       ⚠️ []
  (x for x in ...)     True       True       ⚠️ []

=== 4. for sikli qo'lda ===
    element: 'H'
    element: 'i'
    element: '!'
    StopIteration → 3 qadamdan keyin to'xtadi

=== 5. Mustaqil iteratorlar ===
  next(it1), next(it1) → 'a', 'b'
  next(it2)            → 'a'   ⭐ it2 boshidan boshladi
  next(it1)            → 'c'   ⭐ it1 o'z joyidan davom etdi

=== 6. next() ning sukut qiymati ===
  birinchi juft:   12
  birinchi manfiy: None   (topilmadi — xato yo'q)

Nima ko'rsatdi: 2.1, 2.2, 2.3, 2.4, 2.5-bo'limlar.

Misol 2 — Bir martalik iteratorning tuzoqlari

python
"""Iterator tugashi bilan bog'liq real xatolar."""

import io

print("=== 1. map natijasini ikki marta ishlatish ===")
narxlar_matn = ["12000", "8500", "30000"]
narxlar = map(int, narxlar_matn)
print(f"  jami:     {sum(narxlar)}")
bosh = "— bo'sh!"
print(f"  eng katta: {max(narxlar, default=bosh)}")
narxlar = list(map(int, narxlar_matn))            # ✅ bir marta ro'yxatga
print(f"  ✅ ro'yxat bilan: jami={sum(narxlar)}, eng katta={max(narxlar)}")

print("\n=== 2. `in` iteratorni iste'mol qiladi ===")
kodlar = iter(["A1", "B2", "C3", "D4"])
print(f"  'B2' in kodlar → {'B2' in kodlar}")
print(f"  qolganlari:      {list(kodlar)}   ⚠️ A1 va B2 yo'qoldi")

print("\n=== 3. Fayl obyekti ham bir martalik ===")
fayl = io.StringIO("birinchi\nikkinchi\nuchinchi\n")
print(f"  qatorlar soni: {sum(1 for _ in fayl)}")
print(f"  qatorlar:      {list(fayl)}   ⚠️ kursor oxirida")
fayl.seek(0)
print(f"  seek(0) dan keyin: {[q.strip() for q in fayl]}")

print("\n=== 4. zip bilan bitta iteratorni ikki marta berish ===")
qiymatlar = [1, 2, 3, 4, 5, 6]
it = iter(qiymatlar)
print(f"  zip(it, it)      → {list(zip(it, it))}   ⭐ juftlab olish usuli")
print(f"  zip(r, r)        → {list(zip(qiymatlar, qiymatlar))}   (ro'yxat — har safar yangi)")

print("\n=== 5. Funksiya iteratorni 'yeb qo'yadi' ===")


def tekshir_va_hisobla(elementlar):
    """Bo'sh emasligini tekshiradi, keyin sanaydi."""
    if not any(True for _ in elementlar):
        return "bo'sh"
    return f"{sum(1 for _ in elementlar)} ta element"


print(f"  ro'yxat bilan:   {tekshir_va_hisobla([10, 20, 30])}")
print(f"  iterator bilan:  {tekshir_va_hisobla(iter([10, 20, 30]))}   ⚠️")

print("\n=== 6. Himoya: iteratorni ro'yxatga aylantirish ===")


def xavfsiz_hisobla(elementlar):
    """Iterator kelsa ham to'g'ri ishlaydi."""
    if iter(elementlar) is elementlar:        # bir martalik iterator
        elementlar = list(elementlar)
    if not elementlar:
        return "bo'sh"
    return f"{len(elementlar)} ta element"


print(f"  ro'yxat bilan:   {xavfsiz_hisobla([10, 20, 30])}")
print(f"  iterator bilan:  {xavfsiz_hisobla(iter([10, 20, 30]))}   ✅")
print(f"  generator bilan: {xavfsiz_hisobla(x for x in range(4))}   ✅")

print("\n=== 7. Kechiktirilgan xato ===")
satrlar = ["10", "20", "o'ttiz", "40"]
sonlar = map(int, satrlar)
print("  map yaratildi — hali xato yo'q")
try:
    for s in sonlar:
        print(f"    o'qildi: {s}")
except ValueError as e:
    print(f"  ❌ xato faqat iste'molda: {e}")

Natijaning muhim qismi:

text
=== 1. map natijasini ikki marta ishlatish ===
  jami:     50500
  eng katta: — bo'sh!
  ✅ ro'yxat bilan: jami=50500, eng katta=30000

=== 2. `in` iteratorni iste'mol qiladi ===
  'B2' in kodlar → True
  qolganlari:      ['C3', 'D4']   ⚠️ A1 va B2 yo'qoldi

=== 3. Fayl obyekti ham bir martalik ===
  qatorlar soni: 3
  qatorlar:      []   ⚠️ kursor oxirida
  seek(0) dan keyin: ['birinchi', 'ikkinchi', 'uchinchi']

=== 4. zip bilan bitta iteratorni ikki marta berish ===
  zip(it, it)      → [(1, 2), (3, 4), (5, 6)]   ⭐ juftlab olish usuli
  zip(r, r)        → [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]   (ro'yxat — har safar yangi)

=== 5. Funksiya iteratorni 'yeb qo'yadi' ===
  ro'yxat bilan:   3 ta element
  iterator bilan:  2 ta element   ⚠️

=== 6. Himoya: iteratorni ro'yxatga aylantirish ===
  ro'yxat bilan:   3 ta element
  iterator bilan:  3 ta element   ✅
  generator bilan: 4 ta element   ✅

=== 7. Kechiktirilgan xato ===
  map yaratildi — hali xato yo'q
    o'qildi: 10
    o'qildi: 20
  ❌ xato faqat iste'molda: invalid literal for int() with base 10: "o'ttiz"

Nima ko'rsatdi: 2.4, 2.7-bo'limlar va 3-bo'limdagi tuzoqlar.

Misol 3 — iter(funksiya, belgi) va bo'laklab o'qish

python
"""Ikki argumentli iter() ning amaliy qo'llanilishi."""

import io
import random

print("=== 1. Zar 6 chiqquncha ===")
random.seed(3)
tashlashlar = list(iter(lambda: random.randint(1, 6), 6))
print(f"  6 dan oldingi tashlashlar: {tashlashlar}")
print(f"  urinishlar soni (6 bilan): {len(tashlashlar) + 1}")

print("\n=== 2. Faylni bo'laklab o'qish ===")
malumot = io.BytesIO(bytes(range(256)) * 40)          # 10 240 bayt
BOLAK = 4096
hajmlar = [len(b) for b in iter(lambda: malumot.read(BOLAK), b"")]
print(f"  bo'lak hajmlari: {hajmlar}")
print(f"  jami: {sum(hajmlar):,} bayt")

print("\n=== 3. while bilan solishtirish ===")
malumot.seek(0)
hajmlar_while = []
while True:
    bolak = malumot.read(BOLAK)
    if not bolak:
        break
    hajmlar_while.append(len(bolak))
print(f"  while natijasi:   {hajmlar_while}")
print(f"  bir xilmi:        {hajmlar == hajmlar_while}")

print("\n=== 4. Navbatdan 'STOP' kelguncha o'qish ===")
buyruqlar = iter(["start", "yukla", "saqla", "STOP", "keyingisi"])
bajarilgan = list(iter(lambda: next(buyruqlar), "STOP"))
print(f"  bajarilgan buyruqlar: {bajarilgan}")
print(f"  STOP dan keyin qolgan: {list(buyruqlar)}")

print("\n=== 5. ⚠️ Belgi == bilan solishtiriladi ===")
sanoq = iter([1, 2, 3, 1.0, 5])
natija = list(iter(lambda: next(sanoq), 1.0))
print(f"  belgi 1.0, oqim [1, 2, ...]: {natija}   ⚠️ 1 == 1.0 → darhol to'xtadi")

print("\n=== 6. ⚠️ Birinchi argument chaqiriluvchi bo'lishi shart ===")
try:
    iter([1, 2, 3], 3)
except TypeError as e:
    print(f"  iter([1, 2, 3], 3) → ❌ TypeError: {e}")

Natijaning muhim qismi:

text
=== 1. Zar 6 chiqquncha ===
  6 dan oldingi tashlashlar: [2, 5, 5, 2, 3, 5, 4]
  urinishlar soni (6 bilan): 8

=== 2. Faylni bo'laklab o'qish ===
  bo'lak hajmlari: [4096, 4096, 2048]
  jami: 10,240 bayt

=== 3. while bilan solishtirish ===
  while natijasi:   [4096, 4096, 2048]
  bir xilmi:        True

=== 4. Navbatdan 'STOP' kelguncha o'qish ===
  bajarilgan buyruqlar: ['start', 'yukla', 'saqla']
  STOP dan keyin qolgan: ['keyingisi']

=== 5. ⚠️ Belgi == bilan solishtiriladi ===
  belgi 1.0, oqim [1, 2, ...]: []   ⚠️ 1 == 1.0 → darhol to'xtadi

=== 6. ⚠️ Birinchi argument chaqiriluvchi bo'lishi shart ===
  iter([1, 2, 3], 3) → ❌ TypeError: iter(v, w): v must be callable

Nima ko'rsatdi: 2.5, 2.6-bo'limlar.

Misol 4 — Amaliy: dangasa ma'lumot quvuri

python
"""Katta log faylini iteratorlar bilan qayta ishlash — xotira va erta to'xtash."""

import io
import itertools
import tracemalloc

# ── Sun'iy log: 200 000 qator ──
DARAJALAR = ["INFO", "INFO", "INFO", "WARN", "ERROR"]
qatorlar = []
for i in range(200_000):
    daraja = DARAJALAR[i % len(DARAJALAR)]
    qatorlar.append(f"2026-09-14 12:{i % 60:02d}:00 {daraja} so'rov #{i} {i % 97} ms\n")
LOG = "".join(qatorlar)
del qatorlar


def log_ochish():
    return io.StringIO(LOG)


def xatolar_royxat(fayl):
    """❌ Hammasini xotiraga yuklaydi."""
    qatorlar = fayl.readlines()
    xatolar = [q for q in qatorlar if " ERROR " in q]
    return [int(q.split()[-2]) for q in xatolar]


def xatolar_iterator(fayl):
    """✅ Qatorma-qator, hech narsa to'planmaydi."""
    xato_qatorlar = (q for q in fayl if " ERROR " in q)
    return (int(q.split()[-2]) for q in xato_qatorlar)


def olcha(nom, funksiya):
    fayl = log_ochish()                 # fayl o'lchovdan TASHQARIDA ochiladi
    tracemalloc.start()
    natija = funksiya(fayl)
    _, tepa = tracemalloc.get_traced_memory()
    tracemalloc.stop()
    print(f"  {nom:<26} natija={natija:<11,} xotira cho'qqisi={tepa / 1024:>8,.0f} KB")


print("=== 1. Xotira: ro'yxat vs iterator ===")
olcha("ro'yxatlar bilan (sum)", lambda f: sum(xatolar_royxat(f)))
olcha("iteratorlar bilan (sum)", lambda f: sum(xatolar_iterator(f)))

print("\n=== 2. Erta to'xtash: birinchi sekin xato ===")
oqilgan = 0


def sanab_oqi(fayl):
    global oqilgan
    for q in fayl:
        oqilgan += 1
        yield q


fayl = sanab_oqi(log_ochish())
sekin = next((q for q in fayl if " ERROR " in q and int(q.split()[-2]) > 90), None)
print(f"  topildi: {sekin.strip() if sekin else None}")
print(f"  o'qilgan qatorlar: {oqilgan:,} / 200,000")

print("\n=== 3. Quvur bosqichlari birma-bir ishlaydi ===")
qadamlar = []


def kuzat(nom, oqim):
    for el in oqim:
        qadamlar.append(nom)
        yield el


manba = kuzat("o'qish", iter(["INFO a 1 ms", "ERROR b 95 ms", "ERROR c 99 ms"]))
filtr = kuzat("filtr", (q for q in manba if q.startswith("ERROR")))
sonlar = kuzat("son", (int(q.split()[-2]) for q in filtr))
birinchi = next(sonlar)
print(f"  birinchi natija: {birinchi}")
print(f"  bajarilgan qadamlar: {qadamlar}")
print("  ⭐ 3-qator hali o'qilmagan — faqat kerakli qism hisoblandi")

print("\n=== 4. itertools bilan bo'laklash ===")


def bolaklar(oqim, hajm):
    it = iter(oqim)
    while bolak := list(itertools.islice(it, hajm)):
        yield bolak


xato_vaqtlari = xatolar_iterator(log_ochish())
for tartib, bolak in enumerate(bolaklar(xato_vaqtlari, 10_000), 1):
    print(f"  {tartib}-bo'lak: {len(bolak):>6,} ta, o'rtacha {sum(bolak) / len(bolak):.1f} ms")

print("\n=== 5. ⚠️ Quvurni ikkinchi marta ishlatish ===")
quvur = xatolar_iterator(log_ochish())
print(f"  birinchi sum: {sum(quvur):,}")
print(f"  ikkinchi sum: {sum(quvur):,}   ⚠️ quvur tugagan")
print("  ✅ Yechim: quvurni funksiya qilib, har safar yangisini yarating")
print(f"  qayta yaratilgan: {sum(xatolar_iterator(log_ochish())):,}")

Natijaning muhim qismi:

text
=== 1. Xotira: ro'yxat vs iterator ===
  ro'yxatlar bilan (sum)     natija=1,919,917   xotira cho'qqisi=  19,175 KB
  iteratorlar bilan (sum)    natija=1,919,917   xotira cho'qqisi=       1 KB

=== 2. Erta to'xtash: birinchi sekin xato ===
  topildi: 2026-09-14 12:34:00 ERROR so'rov #94 94 ms
  o'qilgan qatorlar: 95 / 200,000

=== 3. Quvur bosqichlari birma-bir ishlaydi ===
  birinchi natija: 95
  bajarilgan qadamlar: ["o'qish", "o'qish", 'filtr', 'son']
  ⭐ 3-qator hali o'qilmagan — faqat kerakli qism hisoblandi

=== 4. itertools bilan bo'laklash ===
  1-bo'lak: 10,000 ta, o'rtacha 48.0 ms
  2-bo'lak: 10,000 ta, o'rtacha 48.0 ms
  3-bo'lak: 10,000 ta, o'rtacha 48.0 ms
  4-bo'lak: 10,000 ta, o'rtacha 48.0 ms

=== 5. ⚠️ Quvurni ikkinchi marta ishlatish ===
  birinchi sum: 1,919,917
  ikkinchi sum: 0   ⚠️ quvur tugagan
  ✅ Yechim: quvurni funksiya qilib, har safar yangisini yarating
  qayta yaratilgan: 1,919,917

Nima ko'rsatdi: 2.3, 2.4, 2.7-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Iterable va iterator — bir narsa" Iterable iterator beradi; iterator joyini eslaydi
"range — iterator" range qayta ishlatiladigan iterable
"map ro'yxat qaytaradi" Python 3 da bir martalik iterator qaytaradi
"StopIteration — xato" "Tugadi" signali; for uni o'zi ushlaydi
"x in it hech narsani o'zgartirmaydi" Iterator elementlarini topguncha iste'mol qiladi
"Iteratorni qayta boshlash mumkin" Yo'q — yangi iterator yarating
"isinstance(x, Iterable) hammasini aniqlaydi" Faqat __iter__ ni ko'radi; ishonchlisi — iter(x)
"Dangasa hisoblash doim yaxshi" Xato kechikadi va qayta ishlatib bo'lmaydi

6. Keng tarqalgan xatolar va yechimlari

1. Iteratorni ikki marta aylanish

python
sonlar = map(int, satrlar)
jami = sum(sonlar)
eng_katta = max(sonlar)             # ❌ ValueError: max() iterable argument is empty

sonlar = list(map(int, satrlar))    # ✅

2. Tekshirish iteratorni "yeydi"

python
if any(q.startswith("ERROR") for q in fayl):
    for q in fayl: ...              # ❌ qatorlarning bir qismi yo'q

qatorlar = list(fayl)               # ✅ yoki bitta o'tishda ikkalasini qiling

3. next() ni sukutsiz chaqirish

python
birinchi = next(x for x in r if x > 0)          # ❌ topilmasa StopIteration
birinchi = next((x for x in r if x > 0), None)  # ✅

4. Lug'atni aylanishda o'zgartirish

python
for k in d:
    if d[k] is None:
        del d[k]                    # ❌ RuntimeError

for k in list(d):                   # ✅ kalitlar nusxasi
    if d[k] is None:
        del d[k]

5. Ro'yxatdan element o'chirib aylanish

python
for x in r:
    if shart(x): r.remove(x)        # ❌ elementlar o'tkazib yuboriladi

r = [x for x in r if not shart(x)]  # ✅

6. Katta ma'lumotni keraksiz ro'yxatga aylantirish

python
sum([x.narx for x in buyurtmalar])  # ⚠️ oraliq ro'yxat
sum(x.narx for x in buyurtmalar)    # ✅ generator ifodasi

7. Belgi uchun iter() ga funksiya emas, qiymat berish

python
iter(f.read(4096), b"")             # ❌ TypeError: iter(v, w): v must be callable
iter(lambda: f.read(4096), b"")     # ✅

8. while True bilan qo'lda StopIteration ushlash

python
it = iter(r)
while True:
    try: x = next(it)
    except StopIteration: break     # ⚠️ ortiqcha murakkab
    ...

for x in r: ...                     # ✅ for buni o'zi qiladi

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 5.6-dars (o'tilgan): for sikli — endi uning ichki mexanizmini bilasiz
  • 6.17-dars (o'tilgan): generator ifodalari — bir martalik iteratorlarning eng keng tarqalgani
  • 8.14-dars (o'tilgan): konteyner protokoli va eski __getitem__ iteratsiyasi
  • 10.2-dars: o'z iterator sinflaringizni __iter__ va __next__ bilan yozish
  • 10.3–10.5-darslar: generatorlar — iteratorni funksiya ko'rinishida yozish
  • 15-qism: itertools moduli to'liq
  • 16-qism: katta fayllar (CSV, JSON Lines) bilan oqim sifatida ishlash
  • 14-qism: asinxron iteratsiya (__aiter__, async for)

8. Eng yaxshi amaliyotlar

  1. Funksiya iterable qabul qilsa, uni faqat bir marta aylaning. Ikki marta kerak bo'lsa — boshida list() ga aylantiring.

  2. Ro'yxat kerak bo'lmasa, generator ifodasi ishlating. sum(x for x in ...) — oraliq ro'yxat yaratmaydi.

  3. next() ni sukut bilan chaqiring. next(it, None) — StopIteration ning tasodifan tarqalishini oldini oladi.

  4. Bo'laklab o'qishda iter(funksiya, belgi). while True + break dan qisqa va aniq.

  5. Aylanayotgan to'plamni o'zgartirmang. Nusxa ustida aylaning yoki yangisini yasang.

  6. Qayta ishlatiladigan quvurni funksiyaga o'rang. Har chaqiruvda yangi iterator — ikkinchi ishlatishda bo'sh natija bo'lmaydi.

  7. Dangasa kodda xatolarni iste'mol joyida kuting. map/generator yaratilganda emas, aylanilganda ko'tariladi.

  8. "Iteratormi?" savoliga iter(x) is x bilan javob bering. isinstance ga qaraganda ishonchliroq.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
1.  r = [1, 2, 3]
    print(list(r), list(r))
2.  m = map(str, [1, 2, 3])
    print(list(m), list(m))
3.  it = iter([1, 2, 3])
    print(next(it), list(it))
4.  it = iter("salom")
    print("l" in it, "a" in it)
5.  r = range(5)
    print(sum(r), sum(r))
6.  it = iter([1, 2, 3, 4, 5, 6])
    print(list(zip(it, it)))
7.  it = iter([])
    print(next(it, "bo'sh"))
8.  it = iter([10, 20])
    print(iter(it) is it, iter([10, 20]) is iter([10, 20]))
9.  g = (x * 2 for x in range(4))
    print(3 in g, list(g))
10. sonlar = iter([5, 3, 0, 7, 1])
    print(list(iter(lambda: next(sonlar), 0)), list(sonlar))
11. d = {"a": 1, "b": 2}
    it = iter(d.items())
    print(next(it), len(d))
12. from collections.abc import Iterator
    print(isinstance(range(3), Iterator), isinstance(iter(range(3)), Iterator))
Javoblar
  1. [1, 2, 3] [1, 2, 3] — ro'yxat har safar yangi iterator beradi
  2. ['1', '2', '3'] [] — map bir martalik
  3. 1 [2, 3] — next bitta elementni oldi
  4. True False — "l" qidirilganda s, a, l iste'mol qilindi; "a" endi qolgan o, m ichida yo'q
  5. 10 10 — range qayta ishlatiladi
  6. [(1, 2), (3, 4), (5, 6)] — zip bitta iteratordan navbatma-navbat oladi
  7. bo'sh
  8. True False — iterator o'zini qaytaradi; ro'yxatdan har safar yangi iterator
  9. False [] — x * 2 doim juft, 3 topilmaydi va generator oxirigacha iste'mol qilinadi
  10. [5, 3] [7, 1] — 0 kelganda to'xtadi, 0 o'zi ham iste'mol qilingan
  11. ('a', 1) 2 — iterator olish lug'atni o'zgartirmaydi
  12. False True

Vazifa 2: Xatolarni tuzating

python
1.  sonlar = map(float, qatorlar)
    print(sum(sonlar) / len(list(sonlar)))

2.  def bor_mi(oqim, qidiruv):
        return qidiruv in oqim
    fayl = open("log.txt")
    if bor_mi(fayl, "ERROR\n"):
        xatolar = [q for q in fayl if q == "ERROR\n"]

3.  for kalit in sozlamalar:
        if kalit.startswith("_"):
            del sozlamalar[kalit]

4.  birinchi_katta = next(x for x in narxlar if x > 1_000_000)

5.  with open("rasm.png", "rb") as f:
        for bolak in iter(f.read(8192), b""):
            yubor(bolak)

6.  juftlar = zip(royxat, royxat[1:])
    print(len(juftlar))
Javoblar
python
1.  sonlar = list(map(float, qatorlar))
    print(sum(sonlar) / len(sonlar))

2.  qatorlar = list(fayl)               # yoki bitta o'tishda sanash
    if "ERROR\n" in qatorlar:
        xatolar = [q for q in qatorlar if q == "ERROR\n"]

3.  for kalit in list(sozlamalar):
        if kalit.startswith("_"):
            del sozlamalar[kalit]

4.  birinchi_katta = next((x for x in narxlar if x > 1_000_000), None)

5.  for bolak in iter(lambda: f.read(8192), b""):

6.  juftlar = list(zip(royxat, royxat[1:]))   # zip da len() yo'q
    print(len(juftlar))

Vazifa 3: for ni qo'lda yozing

mening_for(iterable, funksiya) funksiyasini yozing — u for siz, faqat iter(), next() va while bilan ishlasin:

  1. Har bir elementga funksiya(element) ni qo'llasin
  2. Nechta element aylanganini qaytarsin
  3. funksiya ichida StopIteration ko'tarilsa, uni sikl tugashi deb qabul qilmasin (maslahat: next() ni alohida try ichida chaqiring)
  4. Ro'yxat, lug'at, fayl va generator bilan sinab ko'ring

Vazifa 4: Xavfsiz statistika

statistika(sonlar) funksiyasini yozing: {"soni", "jami", "ortacha", "eng_kichik", "eng_katta"} qaytarsin.

  1. Ro'yxat berilganda ham, generator berilganda ham to'g'ri ishlasin
  2. Ma'lumotni bitta o'tishda hisoblasin (xotiraga yig'masdan)
  3. Bo'sh kirishda ValueError emas, None qiymatlar qaytarsin
  4. 10 000 000 elementli generator bilan xotira tracemalloc bo'yicha 1 MB dan oshmasligini tekshiring

Vazifa 5: Bo'laklab o'quvchi

bolaklab(fayl_yoli, hajm=65536) funksiyasini yozing:

  1. iter(funksiya, belgi) bilan faylni bo'laklab o'qisin
  2. Har bo'lakni qaytaruvchi iterator bo'lsin
  3. Uning yordamida faylning SHA-256 xeshini hisoblang (hashlib)
  4. Natijani hashlib.file_digest() (3.11+) bilan solishtiring

Vazifa 6: Juftlab va guruhlab

Faqat iteratorlar bilan (ro'yxatga aylantirmasdan) yozing:

  1. juftlab([1, 2, 3, 4, 5, 6]) → (1, 2), (3, 4), (5, 6)
  2. siljuvchi(["a", "b", "c", "d"], 2) → ("a", "b"), ("b", "c"), ("c", "d")
  3. bolakla(range(10), 4) → [0, 1, 2, 3], [4, 5, 6, 7], [8, 9]
  4. Har birini cheksiz oqim (itertools.count()) bilan islice orqali sinang

Vazifa 7: O'ylash

Python 2 da map, filter, zip, range, dict.keys() ro'yxat qaytarardi. Python 3 da ular dangasa obyektlarga aylandi. Bu qaror nega qabul qilindi? U qanday muammolarni hal qildi va qanday yangi muammolar tug'dirdi?

Javob

Qisqa javob: Python 3 "keraksiz ish qilmaslik"ni sukut qildi. Bu xotira va tezlikni sezilarli yaxshiladi, lekin "bir martalik iterator" tuzog'ini hamma dasturchining kundalik muammosiga aylantirdi.

1. Python 2 dagi holat

python
# Python 2
r = range(10000000)       # 10M elementli RO'YXAT darhol yaratiladi
k = d.keys()              # kalitlarning to'liq NUSXASI
m = map(f, katta)         # hammasi darhol hisoblanadi

Dangasa variantlar alohida nomlar bilan mavjud edi: xrange, d.iterkeys(), itertools.imap, itertools.izip. Muammo shundaki, sukut tanlov qimmat edi, tejamli variantni esa ataylab eslab qolish kerak edi. Ko'pchilik range va map ni yozardi va xotirani behuda sarflardi.

2. Python 3 dagi qaror (PEP 3100 va "Python 3000")

Guido van Rossum va jamoa Python 3 da ikki tamoyilga amal qildi:

  • Bir ish uchun bitta aniq yo'l — range/xrange, keys/iterkeys kabi juftliklarni yo'qotish
  • Sukut arzon bo'lsin — kerak bo'lsa list() bilan ro'yxat oling, lekin bu sizning ongli tanlovingiz

Natijada:

Python 2 Python 3
range() → ro'yxat range — dangasa, qayta ishlatiladigan ketma-ketlik
xrange() olib tashlandi
d.keys() → ro'yxat nusxasi d.keys() → ko'rinish (view)
map, filter, zip → ro'yxat bir martalik iterator
itertools.imap, izip olib tashlandi

3. Hal qilingan muammolar

a) Xotira. for i in range(10**8) endi 800 MB emas, bir necha o'nlab bayt oladi.

b) Tezlik va erta to'xtash. next(x for x in map(f, katta) if shart(x)) birinchi mos element topilishi bilan to'xtaydi. Python 2 da map avval butun ro'yxatni hisoblab chiqardi.

c) Cheksiz oqimlar. zip(itertools.count(), fayl) kabi kombinatsiyalar tabiiy ishlaydi. Ro'yxat qaytaradigan zip cheksiz manba bilan osilib qolardi.

d) Ko'rinishlar doim yangi. d.keys() endi nusxa emas, lug'atga jonli oyna. Lug'at o'zgarsa, ko'rinish ham o'zgaradi va to'plam amallarini qo'llab-quvvatlaydi: d1.keys() & d2.keys().

4. Yangi muammolar

a) Bir martalik iterator tuzog'i. Bu darsning asosiy mavzusi. m = map(...) dan keyin ikkinchi sum(m) jimgina 0 qaytaradi. Xato xabari yo'q — faqat noto'g'ri natija. Bu eng yomon turdagi xato.

b) Kechiktirilgan xatolar. map(int, satrlar) dagi ValueError yaratish joyida emas, uzoqdagi iste'mol joyida chiqadi. Traceback xato "tug'ilgan" joyni emas, "topilgan" joyni ko'rsatadi.

c) Debug qilish qiyinlashdi. print(map(f, r)) endi natijani emas, <map object at 0x...> ni ko'rsatadi.

d) Python 2 → 3 ko'chirishdagi yashirin xatolar. Python 2 kodida k = d.keys() va keyin k.sort() yoki k[0] ishlatilgan bo'lsa, Python 3 da AttributeError yoki TypeError beradi. 2to3 vositasi ko'p joyga list(...) qo'shib chiqishga majbur bo'lgan.

5. Nega range iterator emas, lekin map — iterator?

Bu qarorning eng nozik joyi:

  • range ning uzunligi, indeksi va in tekshiruvi hisoblash bilan aniqlanadi (len(range(10**18)) darhol ishlaydi, 10**17 in range(10**18) ham). Unga qayta boshlanadigan ketma-ketlik bo'lish arzon.
  • map(f, manba) esa f ni chaqirmasdan uzunligini ham, n-elementini ham bila olmaydi. Manba o'zi bir martalik bo'lishi mumkin (fayl, tarmoq). Uni qayta ishlatiladigan qilish uchun natijalarni saqlash kerak bo'lardi — bu esa dangasalikning butun ma'nosini yo'qotadi.

Qoida: natijani arzon qayta hisoblash mumkin bo'lsa — qayta ishlatiladigan obyekt; manbani qayta o'qib bo'lmasa — bir martalik iterator.

6. Boshqa tillar

Til Yondashuv
Java Stream — bir martalik; ikkinchi terminal amal IllegalStateException beradi
C# IEnumerable + LINQ — dangasa, lekin odatda qayta aylantirsa bo'ladi (har safar qayta hisoblaydi)
Rust Iterator — bir martalik, lekin egalik tizimi ishlatilgan iteratorni ikkinchi marta ishlatishni kompilyatsiyada taqiqlaydi
JavaScript massiv metodlari (map, filter) — darhol; generatorlar — bir martalik

Rust bu muammoni eng yaxshi hal qilgan: xato ish vaqtida jimgina bo'sh natija emas, kompilyatsiya xatosi bo'ladi. Java esa hech bo'lmaganda ish vaqtida baland ovoz bilan xato beradi. Python esa jim qoladi — shuning uchun bu tuzoqni bilish Python dasturchisi uchun ayniqsa muhim.

7. Xulosa

  1. Python 3 dangasalikni sukut qildi: xotira, tezlik va cheksiz oqimlar yutdi
  2. Narxi — bir martalik iterator tuzog'i va kechiktirilgan xatolar
  3. range va lug'at ko'rinishlari qayta ishlatiladi, chunki ularni qayta hisoblash arzon
  4. map, filter, zip, generator, fayl — bir martalik, chunki manbani qayta o'qib bo'lmasligi mumkin
  5. Amaliy qoida: ikki marta kerak bo'lsa — list() qiling, bir marta kerak bo'lsa — dangasa qoldiring

Nimani mustahkamlaydi: 2.1–2.7-bo'limlar.


Xulosa

Bu darsda Python'dagi iteratsiyaning poydevorini o'rgandik.

Eng muhim uch fikr:

  1. Iterable iterator beradi, iterator esa joyini eslaydi. Ro'yxat, lug'at, satr va range — qayta ishlatiladigan iterable: har for ulardan yangi iterator oladi. map, filter, zip, generator va fayl esa iteratorning o'zi — ular faqat oldinga yuradi va bir marta tugaydi. "Bu obyekt bir martalikmi?" degan savolga iter(x) is x javob beradi.

  2. for — bu iter(), next() va StopIteration ning qisqa yozuvi. Sikl boshida bitta iterator olinadi, elementlar birma-bir so'raladi, StopIteration esa xato emas, "tugadi" signali. list(), sum(), in, ochish va zip — hammasi shu protokolda ishlaydi. next(it, sukut) va iter(funksiya, belgi) bu protokolning amaliy qulayliklari.

  3. Dangasalik xotira va vaqtni tejaydi, lekin ikki narx to'laysiz. Birinchisi — iteratorni ikkinchi marta ishlatganda jimgina bo'sh natija. Ikkinchisi — xatolarning yaratilgan joyda emas, iste'mol qilingan joyda chiqishi. Qoida oddiy: natija ikki marta kerak bo'lsa — list() ga aylantiring.

Keyingi darsda __iter__ va __next__ metodlarini o'zimiz yozib, maxsus iterator sinflarini yaratamiz: qayta ishlatiladigan konteyner bilan bir martalik iteratorni qanday to'g'ri ajratish kerakligini ko'ramiz.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
10.1-dars: Iterator va iterable — IlmHamroh