IlmHamroh
Python kursi/Malumot formatlari8/8-dars22 daqiqa
Mundarija (23)

16.8-dars: Katta fayllarni oqim bilan o'qish

16-QISM — MA'LUMOT FORMATLARI · 8-dars · 16-qism yakuni


1. Kirish va motivatsiya

Bu qismda ma'lumotning ko'p formatini o'rgandik: matn, JSON, CSV, pickle, XML, YAML/TOML, SQLite. Har birida bitta umumiy tuzoq bor edi: butun faylni xotiraga yuklash. Kichik fayl uchun bu muammo emas, lekin ma'lumot gigabaytlarga o'sganda json.load(fayl) yoki fayl.read() serverni cho'ktiradi.

Yechim — oqim bilan o'qish (streaming): faylni butunlay yuklamasdan, bo'lak-bo'lak (odatda qatorma-qator) qayta ishlash. Bu darsda barcha formatlar uchun umumiy naqshni — generatorlar quvuri ni o'rganamiz.

Real vaziyat. Bir jamoa server jurnallarini tahlil qilardi: kunlik .jsonl.gz fayl 8 GB. Birinchi urinish yozuvlar = [json.loads(q) for q in ochilgan_fayl] — server 16 GB RAM da ham ishlamadi, MemoryError. Ular kodni bitta g'oya bilan qayta yozdi: hech qachon hammasini xotiraga olmaslik — har qatorni o'qish, qayta ishlash, tashlab yuborish, faqat yakuniy natijani (sanoq, yig'indi) saqlash. Xotira sarfi 16 GB dan bir necha megabaytga tushdi.

Bu darsda oqimli qayta ishlash naqshlarini o'rganamiz va 16-qismni yakunlaymiz.

Bu darsda:

  • Generatorlar quvuri: o'qish → filtrlash → o'zgartirish
  • Xotira: to'liq yuklash vs oqim (tracemalloc bilan o'lchov)
  • Oqimli agregatsiya: yugurib borayotgan yig'indi, Counter
  • Bo'laklarga bo'lish: itertools.batched
  • Formatlar bo'ylab: gzip, iterparse, CSV, fetchmany
  • Nima qilmaslik: .read(), .readlines(), json.load, fetchall
  • Amaliy: xotira-cheklangan ETL quvuri
  • 16-qism yakuni

2. Nazariya — chuqur tushuntirish

2.1. Oqimli o'qishning asosiy g'oyasi

Yondashuv Xotira Katta faylda
f.read() Butun fayl MemoryError
f.readlines() Barcha qatorlar ro'yxati MemoryError
for qator in f: Bitta qator doimiy
Generator quvuri Bitta element o'tadi doimiy

Fayl obyekti o'zi iterator — for qator in f har safar bitta qatorni o'qiydi, butun faylni emas. Bu — oqimli o'qishning poydevori.

2.2. Generatorlar quvuri (pipeline)

Har bosqich generator — ma'lumot ular orasidan bittalab oqadi, hech bir bosqich hammasini to'plamaydi:

python
def oqi(yol):
    with open(yol, encoding="utf-8") as f:
        for qator in f:
            yield qator.rstrip("\n")

def filtrla(qatorlar, shart):
    for q in qatorlar:
        if shart(q):
            yield q

def ozgartir(qatorlar, f):
    for q in qatorlar:
        yield f(q)

# hech narsa yuklanmaydi — element bittalab oqadi:
natija = ozgartir(filtrla(oqi("katta.txt"), shart), qayta_ishla)

Bu 6-qismdagi (iteratorlar/generatorlar) va 15.8 dagi (itertools) g'oyalarning amaliy cho'qqisi.

2.3. Xotira: dangasa (lazy) hisoblash

Generator dangasa: qiymatni faqat so'ralganda hisoblaydi. Ro'yxat esa hammasini darhol xotiraga oladi:

python
[json.loads(q) for q in f]     # ❌ barcha yozuvlar xotirada
(json.loads(q) for q in f)     # ✅ bittalab, doimiy xotira

tracemalloc bilan o'lchaganda farq katta: bir xil ishni to'liq yuklash o'nlab MB, oqim esa bir necha KB oladi. Farq fayl kattaligiga proporsional o'sadi.

2.4. Oqimli agregatsiya

Ko'p vazifani hammani yuklamasdan hal qilish mumkin — yugurib borayotgan holat saqlab:

Vazifa Oqimli usul
Yig'indi, o'rtacha sum va sanoqni bosqichma-bosqich
Sanash collections.Counter
Eng katta/kichik max/min yoki heapq.nlargest
Noyoblar set (agar noyoblar soni cheklangan bo'lsa)
Guruhlash defaultdict yoki bazaga (SQL GROUP BY)
python
jami = sum(json.loads(q)["ball"] for q in f)      # butun fayl xotirada emas

Ba'zi vazifalar (masalan to'liq saralash) baribir hammasini talab qiladi — ular uchun tashqi saralash yoki baza 16.7-bob kerak.

2.5. Bo'laklarga bo'lish (itertools.batched)

Ba'zan elementlarni to'plamlarga bo'lib qayta ishlash kerak (masalan bazaga 1000 talab yozish):

python
from itertools import batched
for bolak in batched(yozuvlar, 1000):     # 3.12+
    con.executemany("INSERT ...", bolak)  # har 1000 tani bir tranzaksiyada

Bu ham oqimli: batched iteratorni oladi, faqat bitta bo'lakni xotirada saqlaydi.

2.6. Formatlar bo'ylab oqim

Har formatning o'z oqimli usuli bor:

Format To'liq () Oqim ()
Matn f.read() for qator in f
JSON Lines [json.loads(q) ...] (json.loads(q) for q in f)
JSON (bitta obyekt) json.load(f) ijson (tashqi) yoki JSON Lines ga o'ting
CSV list(reader) for qator in reader
XML ET.parse ET.iterparse + elem.clear() (16.5)
SQLite fetchall() iterator yoki fetchmany(n)
gzip ochib, keyin o'qish gzip.open(yol, "rt") — oqimli

Oddiy JSON (bitta katta obyekt/massiv) oqimli o'qishga yaramaydi — shuning uchun katta ma'lumot uchun JSON Lines afzal.

2.7. Siqilgan fayllar (gzip)

gzip.open(yol, "rt") siqilgan faylni oqim bilan o'qiydi — butun faylni xotiraga ochmaydi:

python
import gzip
with gzip.open("log.jsonl.gz", "rt", encoding="utf-8") as f:
    for qator in f:
        ...

Katta jurnallar odatda .gz bo'ladi; oqimli o'qish ularni diskda ham, xotirada ham ochmaydi.

2.8. Nima qilmaslik

f.read(), f.readlines() for qator in f
json.load(katta) JSON Lines + generator
list(csv.reader(f)) for q in csv.reader(f)
ET.parse(katta) ET.iterparse + clear()
cur.fetchall() iterator yoki fetchmany
Oqim o'rtasida list(...) quvurni oxirigacha dangasa qoldiring

3. Tez ma'lumotnoma

python
# generator quvuri — doimiy xotira
def oqi(yol):
    with open(yol, encoding="utf-8") as f:
        yield from f

jami = sum(json.loads(q)["ball"] for q in oqi("katta.jsonl"))   # oqimli agregatsiya

import gzip
with gzip.open("log.jsonl.gz", "rt", encoding="utf-8") as f:    # siqilgan oqim
    for qator in f: ...

from itertools import batched
for bolak in batched(manba, 1000):                              # bo'laklab
    con.executemany("INSERT ...", bolak)

from collections import Counter
sanoq = Counter(json.loads(q)["tur"] for q in oqi("katta.jsonl"))

Qoidalar

hech qachon .read()/.readlines()/json.load katta faylda
fayl obyekti — iterator: for qator in f
generator quvuri: yield, dangasa
agregatsiya: sum/Counter/max — hammani yuklamasdan
bo'laklab: itertools.batched
katta ma'lumot: JSON Lines, oddiy JSON emas
siqilgan: gzip.open(..., "rt")
to'liq saralash kerak bo'lsa: baza yoki tashqi saralash

4. Batafsil misollar

Misol 1 — Generator quvuri va xotira

python
"""fayl obyekti iterator; generator quvuri (oqi→filtrla→ozgartir); dangasa hisoblash; tracemalloc bilan ro'yxat vs generator xotirasi."""

import io
import itertools
import json
import tracemalloc


def oqi(matnlar):
    for qator in matnlar:
        yield qator.rstrip("\n")


def filtrla(qatorlar, shart):
    for q in qatorlar:
        if shart(q):
            yield q


def ozgartir(qatorlar, funksiya):
    for q in qatorlar:
        yield funksiya(q)


def main() -> None:
    print("=== 1. Generator quvuri ===")
    data = io.StringIO("\n".join(f'{{"id": {i}, "ball": {i * 10}}}' for i in range(10)))
    quvur = ozgartir(
        filtrla(oqi(data), lambda q: json.loads(q)["ball"] >= 30),
        lambda q: json.loads(q)["id"],
    )
    print(f"  quvur turi: {type(quvur).__name__} (dangasa — hali hech narsa o'qilmadi)")
    print(f"  birinchi 3 natija: {list(itertools.islice(quvur, 3))}")

    print("\n=== 2. Dangasa hisoblash ===")
    hisoblangan = []

    def kuzat(x):
        hisoblangan.append(x)
        return x

    gen = (kuzat(i) for i in range(1000))
    uchta = list(itertools.islice(gen, 3))
    print(f"  1000 elementli generatordan 3 ta olindi: {uchta}")
    print(f"  aslida hisoblangan: {len(hisoblangan)} ta (hammasi emas!)")

    print("\n=== 3. ⚠️ Xotira: ro'yxat vs generator ===")
    tracemalloc.start()
    royxat = [i * i for i in range(200_000)]
    royxat_xotira = tracemalloc.get_traced_memory()[1]
    tracemalloc.stop()

    tracemalloc.start()
    generator = (i * i for i in range(200_000))
    generator_xotira = tracemalloc.get_traced_memory()[1]
    tracemalloc.stop()

    print(f"  ro'yxat elementlari: {len(royxat)}")
    print(f"  generator — hali hisoblanmagan (dangasa)")
    print(f"  ro'yxat xotirasi generatordan kamida 100 barobar katta: {royxat_xotira > generator_xotira * 100}")
    print(f"  ikkalasining yig'indisi bir xil: {sum(royxat) == sum(i * i for i in range(200_000))}")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Generator quvuri ===
  quvur turi: generator (dangasa — hali hech narsa o'qilmadi)
  birinchi 3 natija: [3, 4, 5]

=== 2. Dangasa hisoblash ===
  1000 elementli generatordan 3 ta olindi: [0, 1, 2]
  aslida hisoblangan: 3 ta (hammasi emas!)

=== 3. ⚠️ Xotira: ro'yxat vs generator ===
  ro'yxat elementlari: 200000
  generator — hali hisoblanmagan (dangasa)
  ro'yxat xotirasi generatordan kamida 100 barobar katta: True
  ikkalasining yig'indisi bir xil: True

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

Misol 2 — Oqimli agregatsiya va xotira farqi

python
"""katta JSON Lines faylni to'liq yuklash vs oqim — tracemalloc bilan peak xotira; yugurib borayotgan yig'indi va Counter; itertools.batched bilan bo'laklash."""

import itertools
import json
import tempfile
import tracemalloc
from collections import Counter
from pathlib import Path


def yarat(yol: Path, n: int) -> None:
    with open(yol, "w", encoding="utf-8") as f:
        for i in range(n):
            f.write(json.dumps({"id": i, "shahar": ["Toshkent", "Samarqand", "Buxoro"][i % 3], "ball": i % 100}) + "\n")


def main() -> None:
    with tempfile.TemporaryDirectory() as katalog:
        yol = Path(katalog) / "katta.jsonl"
        yarat(yol, 50_000)

        print("=== 1. ⚠️ To'liq yuklash vs oqim ===")
        tracemalloc.start()
        yozuvlar = [json.loads(q) for q in open(yol, encoding="utf-8")]
        toliq_yigindi = sum(y["ball"] for y in yozuvlar)
        toliq_peak = tracemalloc.get_traced_memory()[1]
        tracemalloc.stop()

        tracemalloc.start()
        oqim_yigindi = sum(json.loads(q)["ball"] for q in open(yol, encoding="utf-8"))
        oqim_peak = tracemalloc.get_traced_memory()[1]
        tracemalloc.stop()

        print(f"  yig'indi bir xil: {toliq_yigindi == oqim_yigindi}")
        print(f"  to'liq yuklash peak xotirasi oqimdan kamida 10 barobar katta: {toliq_peak > oqim_peak * 10}")

        print("\n=== 2. Oqimli agregatsiya ===")
        jami = soni = 0
        eng_kop = 0
        for qator in open(yol, encoding="utf-8"):
            y = json.loads(qator)
            jami += y["ball"]
            soni += 1
            eng_kop = max(eng_kop, y["ball"])
        print(f"  yozuvlar: {soni}, o'rtacha ball: {jami / soni:.2f}, eng ko'p: {eng_kop}")

        print("\n=== 3. Counter bilan guruhlash ===")
        sanoq = Counter(json.loads(q)["shahar"] for q in open(yol, encoding="utf-8"))
        for shahar, soni in sorted(sanoq.items()):
            print(f"  {shahar:12} {soni}")

        print("\n=== 4. itertools.batched bilan bo'laklash ===")
        manba = (json.loads(q)["id"] for q in open(yol, encoding="utf-8"))
        bolaklar_soni = 0
        oxirgi_bolak = None
        for bolak in itertools.batched(manba, 10_000):
            bolaklar_soni += 1
            oxirgi_bolak = bolak
        print(f"  50000 yozuv 10000 lik bo'laklarda: {bolaklar_soni} bo'lak")
        print(f"  oxirgi bo'lak uzunligi: {len(oxirgi_bolak)}, faqat bitta bo'lak xotirada")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. ⚠️ To'liq yuklash vs oqim ===
  yig'indi bir xil: True
  to'liq yuklash peak xotirasi oqimdan kamida 10 barobar katta: True

=== 2. Oqimli agregatsiya ===
  yozuvlar: 50000, o'rtacha ball: 49.50, eng ko'p: 99

=== 3. Counter bilan guruhlash ===
  Buxoro       16666
  Samarqand    16667
  Toshkent     16667

=== 4. itertools.batched bilan bo'laklash ===
  50000 yozuv 10000 lik bo'laklarda: 5 bo'lak
  oxirgi bo'lak uzunligi: 10000, faqat bitta bo'lak xotirada

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

Misol 3 — Formatlar bo'ylab oqim

python
"""gzip oqimli o'qish; XML iterparse + clear; CSV oqimli; sqlite fetchmany va iterator — barcha formatlarda 'hammasini yuklamaslik' naqshi."""

import csv
import gzip
import io
import json
import sqlite3
import tempfile
import xml.etree.ElementTree as ET
from pathlib import Path


def main() -> None:
    with tempfile.TemporaryDirectory() as katalog:
        r = Path(katalog)

        print("=== 1. gzip — siqilgan oqim ===")
        gz = r / "log.jsonl.gz"
        with gzip.open(gz, "wt", encoding="utf-8") as f:
            for i in range(100):
                f.write(json.dumps({"id": i, "ball": i}) + "\n")
        jami = 0
        with gzip.open(gz, "rt", encoding="utf-8") as f:
            for qator in f:                                  # butun fayl ochilmaydi
                jami += json.loads(qator)["ball"]
        print(f"  siqilgan fayl hajmi: {gz.stat().st_size} bayt, oqimli yig'indi: {jami}")

        print("\n=== 2. XML iterparse ===")
        xml = "<lenta>" + "".join(f"<yozuv><ball>{i}</ball></yozuv>" for i in range(50)) + "</lenta>"
        jami = soni = 0
        for hodisa, elem in ET.iterparse(io.StringIO(xml), events=("end",)):
            if elem.tag == "yozuv":
                jami += int(elem.findtext("ball"))
                soni += 1
                elem.clear()                                 # ⭐ xotirani bo'shatish
        print(f"  {soni} yozuv, yig'indi: {jami} (daraxt xotirada to'planmadi)")

        print("\n=== 3. CSV oqimli ===")
        yol = r / "data.csv"
        with open(yol, "w", encoding="utf-8", newline="") as f:
            w = csv.writer(f)
            w.writerow(["id", "ball"])
            w.writerows([(i, i * 2) for i in range(30)])
        with open(yol, encoding="utf-8", newline="") as f:
            jami = sum(int(q["ball"]) for q in csv.DictReader(f))     # qatorma-qator
        print(f"  CSV oqimli yig'indi: {jami}")

        print("\n=== 4. SQLite fetchmany va iterator ===")
        con = sqlite3.connect(":memory:")
        con.execute("CREATE TABLE t(id INTEGER, ball INTEGER)")
        con.executemany("INSERT INTO t VALUES(?, ?)", [(i, i) for i in range(100)])
        cur = con.execute("SELECT ball FROM t ORDER BY id")
        birinchi_bolak = cur.fetchmany(10)
        print(f"  fetchmany(10): {len(birinchi_bolak)} qator, boshi: {birinchi_bolak[0][0]}")
        jami = sum(ball for (ball,) in con.execute("SELECT ball FROM t"))   # iterator
        print(f"  iterator bilan yig'indi: {jami}")
        # agregatsiyani SQL o'zi qiladi — eng oqimli usul:
        print(f"  SQL AVG (baza o'zi hisoblaydi): {con.execute('SELECT AVG(ball) FROM t').fetchone()[0]}")
        con.close()


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. gzip — siqilgan oqim ===
  siqilgan fayl hajmi: 438 bayt, oqimli yig'indi: 4950

=== 2. XML iterparse ===
  50 yozuv, yig'indi: 1225 (daraxt xotirada to'planmadi)

=== 3. CSV oqimli ===
  CSV oqimli yig'indi: 870

=== 4. SQLite fetchmany va iterator ===
  fetchmany(10): 10 qator, boshi: 0
  iterator bilan yig'indi: 4950
  SQL AVG (baza o'zi hisoblaydi): 49.5

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

Misol 4 — Amaliy: xotira-cheklangan ETL quvuri

Kunlik server jurnali — siqilgan JSON Lines (.jsonl.gz), millionlab qator. Vazifa: xatolarni filtrlash, foydalanuvchi bo'yicha guruhlash, statistika chiqarish va tozalangan natijani SQLite bazaga yuklash — hammasi doimiy xotirada, faylni hech qachon butunlay yuklamasdan. Bu — 16-qismning barcha g'oyalarini birlashtiruvchi ETL (Extract-Transform-Load) quvuri.

python
"""ETL quvuri: gzip JSONL o'qish (Extract) → filtrlash/tozalash generatori (Transform) → SQLite ga batched yuklash + oqimli statistika (Load); doimiy xotira."""

import gzip
import json
import sqlite3
import tempfile
from collections import Counter
from contextlib import closing
from itertools import batched
from pathlib import Path


def yarat_jurnal(yol: Path, n: int) -> None:
    darajalar = ["INFO", "INFO", "INFO", "WARNING", "ERROR"]
    with gzip.open(yol, "wt", encoding="utf-8") as f:
        for i in range(n):
            yozuv = {"vaqt": f"2026-09-17T{i % 24:02d}:00:00", "daraja": darajalar[i % 5],
                     "foydalanuvchi": f"user{i % 7}", "davomiylik_ms": (i % 50) * 10}
            if i % 500 == 499:
                f.write("buzuq qator emas json\n")             # ataylab buzuq qator
            else:
                f.write(json.dumps(yozuv) + "\n")


def oqi(yol: Path):
    """Extract: gzip ni oqimli o'qib, buzuq qatorlarga bardosh berib, yozuv beradi."""
    with gzip.open(yol, "rt", encoding="utf-8") as f:
        for raqam, qator in enumerate(f, 1):
            try:
                yield json.loads(qator)
            except json.JSONDecodeError:
                continue                                      # buzuq qatorni o'tkazib yuborish


def tozala(yozuvlar):
    """Transform: faqat WARNING/ERROR va kerakli maydonlar."""
    for y in yozuvlar:
        if y.get("daraja") in {"WARNING", "ERROR"}:
            yield (y["vaqt"], y["daraja"], y["foydalanuvchi"], y["davomiylik_ms"])


def main() -> None:
    with tempfile.TemporaryDirectory() as katalog:
        r = Path(katalog)
        jurnal = r / "server.jsonl.gz"
        yarat_jurnal(jurnal, 10_000)
        print(f"=== 1. Kirish: {jurnal.stat().st_size} bayt (siqilgan) ===")

        with closing(sqlite3.connect(r / "hodisalar.db")) as con:
            con.execute("CREATE TABLE hodisa(vaqt TEXT, daraja TEXT, foydalanuvchi TEXT, davomiylik_ms INTEGER)")

            # Load: batched INSERT + oqimli statistika (bir o'tishda)
            daraja_sanoq: Counter = Counter()
            foydalanuvchi_sanoq: Counter = Counter()
            jami_davomiylik = qatorlar = 0

            def kuzatib_otkaz(yozuvlar):
                nonlocal jami_davomiylik, qatorlar
                for vaqt, daraja, fydl, ms in yozuvlar:
                    daraja_sanoq[daraja] += 1
                    foydalanuvchi_sanoq[fydl] += 1
                    jami_davomiylik += ms
                    qatorlar += 1
                    yield (vaqt, daraja, fydl, ms)

            with con:
                for bolak in batched(kuzatib_otkaz(tozala(oqi(jurnal))), 1000):
                    con.executemany("INSERT INTO hodisa VALUES(?, ?, ?, ?)", bolak)

            print(f"\n=== 2. Yuklandi: {qatorlar} hodisa (WARNING/ERROR) ===")
            print(f"  bazadagi qatorlar: {con.execute('SELECT COUNT(*) FROM hodisa').fetchone()[0]}")

            print("\n=== 3. Statistika (oqim davomida yig'ilgan) ===")
            for daraja, soni in sorted(daraja_sanoq.items()):
                print(f"  {daraja:8} {soni}")
            print(f"  o'rtacha davomiylik: {jami_davomiylik / qatorlar:.1f} ms")
            print(f"  eng faol foydalanuvchi: {foydalanuvchi_sanoq.most_common(1)[0]}")

            print("\n=== 4. Bazadan agregatsiya (SQL) ===")
            for fydl, soni, ortacha in con.execute(
                    "SELECT foydalanuvchi, COUNT(*), AVG(davomiylik_ms) FROM hodisa "
                    "GROUP BY foydalanuvchi ORDER BY COUNT(*) DESC LIMIT 3"):
                print(f"  {fydl:8} {soni} hodisa, o'rtacha {ortacha:.0f} ms")
            print("\n  ⭐ butun quvur davomida fayl xotiraga to'liq yuklanmadi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Kirish: 43309 bayt (siqilgan) ===

=== 2. Yuklandi: 3980 hodisa (WARNING/ERROR) ===
  bazadagi qatorlar: 3980

=== 3. Statistika (oqim davomida yig'ilgan) ===
  ERROR    1980
  WARNING  2000
  o'rtacha davomiylik: 258.8 ms
  eng faol foydalanuvchi: ('user3', 569)

=== 4. Bazadan agregatsiya (SQL) ===
  user6    569 hodisa, o'rtacha 259 ms
  user4    569 hodisa, o'rtacha 258 ms
  user3    569 hodisa, o'rtacha 259 ms

  ⭐ butun quvur davomida fayl xotiraga to'liq yuklanmadi

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


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

Noto'g'ri fikr To'g'risi
"f.read() qulay va tez" Katta faylda MemoryError
"Generator ro'yxatdan sekinroq" Xotira tejaydi; tezlik ko'pincha yaqin
"json.load har qanday JSON uchun" Katta bitta obyekt — xotira; JSON Lines kerak
"Barcha vazifa hammani yuklashni talab qiladi" Yig'indi, sanoq, max — oqimli mumkin
"gzip faylni avval ochish kerak" gzip.open(..., "rt") oqimli o'qiydi
"iterparse o'zi xotirani boshqaradi" elem.clear() shart
"Oqim o'rtasida list() zararsiz" Butun oqimni xotiraga oladi
"Saralashni ham oqimli qilsa bo'ladi" To'liq saralash — baza yoki tashqi saralash

6. Keng tarqalgan xatolar va yechimlari

1. Butun faylni o'qish

python
data = open("katta.txt").read()                     # ❌
for qator in open("katta.txt", encoding="utf-8"):   # ✅
    ...

2. Ro'yxatga yig'ish

python
yozuvlar = [json.loads(q) for q in f]               # ❌ hammasi xotirada
jami = sum(json.loads(q)["x"] for q in f)           # ✅ oqimli

3. Katta JSON ni json.load

python
json.load(open("10gb.json"))                        # ❌
# ✅ JSON Lines ga o'ting yoki ijson (oqimli parser)

4. fetchall katta natijada

python
qatorlar = cur.fetchall()                           # ❌
for qator in con.execute("SELECT ..."):             # ✅
    ...

5. Oqim o'rtasida materializatsiya

python
list(generator)[0]                                  # ❌ hammasini oladi
next(generator)                                     # ✅ yoki islice

6. iterparse da clear yo'q

python
for _, e in ET.iterparse(f): ...                    # ❌ daraxt to'planadi
    ...; e.clear()                                  # ✅

7. Guruhlashni Python'da (baza bor bo'lsa)

python
d = defaultdict(list)                               # ⚠️ hammasi xotirada
for q in f: d[q["shahar"]].append(q)
# ✅ bazaga yuklab SQL GROUP BY, yoki Counter bilan faqat sanoq

8. Siqilgan faylni butun o'qish

python
gzip.open("log.gz", "rt").read()                    # ❌
for qator in gzip.open("log.gz", "rt", encoding="utf-8"):   # ✅
    ...

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 6-qism (o'tilgan): iteratorlar va generatorlar — bu darsning poydevori
  • 15.8–15.9-darslar (o'tilgan): itertools — islice, batched, chain
  • 16.2–16.7-darslar (o'tilgan): har format uchun oqimli usul
  • 24-qism: pandas chunksize — katta ma'lumotni bo'laklab tahlil
  • 26-qism: jurnal va fayllarni avtomatlashtirish
  • 29-qism: miqyoslash — katta ma'lumot quvurlarini optimallashtirish

8. Eng yaxshi amaliyotlar

  1. Katta faylni hech qachon .read()/.readlines()/json.load bilan o'qimang.

  2. Generator quvuri: o'qish → filtrlash → o'zgartirish, hammasi dangasa.

  3. Agregatsiyani yugurib borayotgan holat bilan (sum, Counter, max).

  4. Bazaga yozishni itertools.batched va tranzaksiya bilan bo'laklang.

  5. Katta ma'lumot uchun JSON Lines (oddiy JSON emas) va .gz oqimli o'qish.

  6. iterparse da har yozuvdan keyin elem.clear().

  7. To'liq saralash yoki murakkab guruhlash kerak bo'lsa — bazaga topshiring.

  8. Xotira sarfini tracemalloc bilan o'lchab tasdiqlang.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
import itertools, json, io
1.  g = (i * 2 for i in range(5)); print(next(g), next(g))
2.  print(list(itertools.islice((i for i in range(100)), 3)))
3.  print(list(itertools.batched(range(7), 3)))
4.  f = io.StringIO("a\nb\nc\n"); print(sum(1 for _ in f))
5.  f = io.StringIO("1\n2\n3\n"); print(sum(int(x) for x in f))
6.  g = (x for x in [1, 2, 3]); print(type(g).__name__)
7.  print(list(itertools.islice(itertools.count(), 4)))
8.  from collections import Counter; print(Counter("aabbbc").most_common(1))
9.  g = (i for i in range(3)); list(g); print(list(g))
10. print(next(iter(itertools.batched("abcdef", 2))))
11. f = io.StringIO('{"x":1}\n{"x":2}\n'); print(sum(json.loads(l)["x"] for l in f))
12. print(list(itertools.chain([1, 2], [3, 4])))
Javoblar
  1. 0 2
  2. [0, 1, 2]
  3. [(0, 1, 2), (3, 4, 5), (6,)]
  4. 3
  5. 6
  6. generator
  7. [0, 1, 2, 3]
  8. [('b', 3)]
  9. [] — generator bir marta ishlatiladi
  10. ('a', 'b')
  11. 3
  12. [1, 2, 3, 4]

Vazifa 2: Xatolarni tuzating

python
1.  def qatorlar_soni(yol):
        return len(open(yol).read().split("\n"))

2.  def jami_ball(yol):
        yozuvlar = [json.loads(q) for q in open(yol)]        # 5 GB fayl
        return sum(y["ball"] for y in yozuvlar)

3.  def xatolar(yol):
        return list(ET.parse(yol).iterfind(".//error"))      # katta XML

4.  def eksport(con, yol):
        qatorlar = con.execute("SELECT * FROM katta").fetchall()
        with open(yol, "w") as f:
            for q in qatorlar: f.write(str(q))

5.  def gz_oqi(yol):
        matn = gzip.open(yol, "rt").read()                   # 8 GB siqilgan
        return matn.count("ERROR")
Javoblar
python
1.  def qatorlar_soni(yol):
        with open(yol, encoding="utf-8") as f:
            return sum(1 for _ in f)

2.  def jami_ball(yol):
        with open(yol, encoding="utf-8") as f:
            return sum(json.loads(q)["ball"] for q in f)

3.  def xatolar(yol):
        natija = []
        for _, e in ET.iterparse(yol, events=("end",)):
            if e.tag == "error":
                natija.append(e.text)
                e.clear()
        return natija

4.  def eksport(con, yol):
        with open(yol, "w", encoding="utf-8") as f:
            for q in con.execute("SELECT * FROM katta"):     # iterator
                f.write(str(q) + "\n")

5.  def gz_oqi(yol):
        soni = 0
        with gzip.open(yol, "rt", encoding="utf-8") as f:
            for qator in f:
                soni += qator.count("ERROR")
        return soni

Vazifa 3: Oqimli grep

grep(namuna, *yollar) — millionlab qatorli fayllarda:

  1. Qatorma-qator o'qib, mos qatorlarni (fayl, raqam, matn) bilan bersin (generator)
  2. .gz fayllarni ham qo'llasin (kengaytmaga qarab)
  3. --count, --after N (moslikdan keyingi N qator) variantlari
  4. Xotira sarfini tracemalloc bilan katta faylda tekshiring

Vazifa 4: Tashqi saralash (external merge sort)

  1. Xotiraga sig'maydigan faylni bo'laklarga bo'lib, har bo'lakni saralang va vaqtinchalik faylga yozing
  2. Saralangan bo'laklarni heapq.merge bilan birlashtiring
  3. 1 000 000 qatorli faylni cheklangan xotirada saralang
  4. Natijani sorted(open(...)) bilan solishtiring (kichik faylda)

Vazifa 5: Oqimli JSON Lines validatori

  1. Katta .jsonl ni sxema bo'yicha tekshiring (16.2 g'oyasi), oqimli
  2. Buzuq qatorlarni raqami bilan alohida faylga yozing
  3. Yaxshi/yomon nisbati va xato turlari statistikasi (Counter)
  4. --fix — tuzatib bo'ladiganlarni tuzatib qayta yozing

Vazifa 6: Real vaqtli quvur (tail -f)

  1. Faylning oxiriga qo'shilayotgan qatorlarni kuzatuvchi generator (f.seek bilan)
  2. Har yangi qatorni JSON deb tahlil qilib, ERROR larni sanang
  3. Har 100 qatorda joriy statistikani chop eting
  4. Fayl aylantirilsa (rotation) qayta ochilsin

Vazifa 7: O'ylash

"Katta ma'lumot" (big data) atrofida Hadoop, Spark kabi murakkab tizimlar qurilgan, lekin ko'p amaliy vazifa oddiy Python generatorlari bilan bitta mashinada hal qilinadi — chunki asosiy g'oya bitta: ma'lumotni oqim bilan qayta ishlash, hammasini xotiraga olmasdan. Nima uchun bu oddiy g'oya shunchalik kuchli, va qachon haqiqatan taqsimlangan tizim (Spark) kerak bo'ladi?

Javob

Qisqa javob: Oqimli qayta ishlash kuchli, chunki ko'p vazifada butun ma'lumotni bir vaqtda ko'rish shart emas — har elementni ko'rib, holatni yangilab, tashlab yuborish yetarli (yig'indi, sanoq, filtr, transformatsiya). Bu doimiy xotirada istalgan kattalikdagi faylni ishlaydi. Taqsimlangan tizim faqat ma'lumot bitta mashina diskiga yoki vaqtiga sig'maganda, yoki vazifa butun ma'lumot bo'ylab murakkab bog'lanishlar (join, global sort) talab qilganda kerak.

1. Nega oqim g'oyasi kuchli

Sabab Tafsilot
Doimiy xotira Fayl kattaligidan mustaqil
Kompozitsiya Generatorlar quvuri — modulli, qayta ishlatiladigan
Dangasalik Faqat kerakli qism hisoblanadi
Universallik Har formatga (JSON, CSV, XML, gzip) mos
Oddiylik Qo'shimcha infratuzilma yo'q

2. Ko'p vazifa bitta mashinada yetarli

Vazifa Oqimli mumkinmi
Filtrlash, transformatsiya toza oqim
Yig'indi, o'rtacha, sanoq yugurib borayotgan holat
Top-N heapq
Guruhlab agregatsiya (cheklangan kalitlar) Counter/dict
Bazaga yuklab SQL SQLite/Postgres

3. Qachon taqsimlangan tizim kerak

Holat Sabab
Ma'lumot diskka sig'maydi Bir necha mashina disklari
Vaqt cheklovi (soatlar → daqiqalar) Parallel hisoblash
Global join/sort katta ma'lumotda Bir mashina xotirasidan oshadi
Uzluksiz oqim (real-time, katta hajm) Kafka, Flink kabi
Ko'p manba, murakkab quvur Orkestratsiya (Airflow, Spark)

4. Amaliy qoida

  1. Avval oddiy oqimli Python bilan sinang — ko'p "big data" aslida "bitta mashina data"
  2. Bir mashina RAM/disk/vaqt chegarasiga urilganda o'lchang, keyin taqsimlashga o'ting
  3. Taqsimlangan tizim murakkablik va narx qo'shadi — faqat kerak bo'lganda
  4. Oqim g'oyasi ikkalasida ham bir xil: Spark ham ichida bo'laklab, dangasa ishlaydi

5. Xulosa

  1. Oqim g'oyasi doimiy xotirada istalgan kattalikni ishlaydi
  2. Ko'p vazifa butun ma'lumotni ko'rishni talab qilmaydi
  3. Taqsimlangan tizim — disk/vaqt/global bog'lanish chegarasida
  4. Avval oddiy yechim, o'lchov, keyin murakkablik

Nimani mustahkamlaydi: 2.1–2.8-bo'limlar.


Xulosa

Bu darsda katta fayllarni doimiy xotirada — oqim bilan — qayta ishlashni o'rgandik.

Eng muhim uch fikr:

  1. Hech qachon hammasini xotiraga olmang. Fayl obyektining o'zi iterator — for qator in f butun faylni emas, bitta qatorni o'qiydi. Generatorlar quvuri (o'qish → filtrlash → o'zgartirish) ma'lumotni bittalab oqizadi va hech bir bosqich hammasini to'plamaydi. .read(), .readlines(), json.load, fetchall, oqim o'rtasidagi list() — katta faylda MemoryError sabablari.

  2. Ko'p vazifa oqimli hal bo'ladi. Yig'indi, o'rtacha, eng katta, sanoq (Counter) — yugurib borayotgan holat bilan butun faylni yuklamasdan hisoblanadi; bazaga yuklashni itertools.batched va tranzaksiya bilan bo'laklang. Har formatning oqimli usuli bor: gzip.open(..., "rt"), ET.iterparse + elem.clear(), CSV va SQLite iteratorlari, va katta ma'lumot uchun oddiy JSON emas, JSON Lines. To'liq saralash yoki murakkab guruhlash kerak bo'lsa — bazaga topshiring.

  3. Oqim g'oyasi miqyoslanadi. tracemalloc bilan o'lchaganda to'liq yuklash o'nlab barobar ko'p xotira oladi — farq fayl kattaligiga o'sadi. Bu oddiy g'oya bilan ko'p "big data" vazifasi bitta mashinada hal bo'ladi; haqiqiy taqsimlangan tizim faqat ma'lumot bir mashina disk/vaqtiga sig'maganda kerak.

Bu bilan 16-qism — Ma'lumot formatlari yakunlandi. Keyingi 17-qismda dasturimiz to'g'ri ishlashini kafolatlaydigan sohaga — testlashga o'tamiz: pytest bilan test yozish, fixture lar, parametrlash, mock va TDD.


16-qism yakuni

Ma'lumot formatlari qismida ma'lumotni saqlash, almashish va o'qishning barcha asosiy usullarini o'rgandik — har birini qachon ishlatish va qanday xavflaridan ehtiyot bo'lish bilan.

Dars Format Asosiy g'oya Muhim xavf/tuzoq
16.1 Matn va binar encoding, newline, matn vs binar rejim Kodlash, universal newline
16.2 JSON Universal almashinuv, dumps/loads Turlar mosligi, NaN, katta son (JS)
16.3 CSV Jadval ma'lumoti, DictReader split emas, newline="", injection
16.4 pickle Python obyektini saqlash Ishonchsiz load — kod bajaradi
16.5 XML Hujjat, nom fazolari, iterparse Billion laughs, defusedxml
16.6 YAML/TOML Odam yozadigan sozlama YAML safe_load, Norway; TOML qat'iy
16.7 sqlite3 O'rnatilgan baza, SQL SQL injection — parametrli so'rov
16.8 Oqim bilan o'qish Doimiy xotira, generatorlar Hammasini yuklamaslik

Umumiy tamoyillar:

  1. Format vazifaga mos bo'lsin: almashinuv — JSON, jadval — CSV, sozlama — TOML/YAML, Python-ichi — pickle/sqlite, katta ma'lumot — JSON Lines + oqim.
  2. Ishonchsiz ma'lumot — hamma joyda tekshiring: pickle/yaml.load/XML entity/SQL injection — bularning barchasi ishonchsiz kirishda xavfli.
  3. Kodlash va turlarga e'tibor: UTF-8, BOM, Decimal pul uchun, sana formatlari.
  4. Katta hajmda oqim: hech qachon butun faylni xotiraga olmang.

Keyingi bosqich — 4-bosqichning davomi: testlash (17), sifat va vositalar (18), paketlash (19) orqali professional darajada ishonchli dastur yaratish.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
16.8-dars: Katta fayllarni oqim bilan o'qish — IlmHamroh