Mundarija (23)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Oqimli o'qishning asosiy g'oyasi
- 2.2. Generatorlar quvuri (pipeline)
- 2.3. Xotira: dangasa (lazy) hisoblash
- 2.4. Oqimli agregatsiya
- 2.5. Bo'laklarga bo'lish (itertools.batched)
- 2.6. Formatlar bo'ylab oqim
- 2.7. Siqilgan fayllar (gzip)
- 2.8. Nima qilmaslik
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Generator quvuri va xotira
- Misol 2 — Oqimli agregatsiya va xotira farqi
- Misol 3 — Formatlar bo'ylab oqim
- Misol 4 — Amaliy: xotira-cheklangan ETL quvuri
- 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
- 16-qism yakuni
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 (
tracemallocbilan 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:
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:
[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) |
jami = sum(json.loads(q)["ball"] for q in f) # butun fayl xotirada emasBa'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):
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:
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
# 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 saralash4. Batafsil misollar
Misol 1 — Generator quvuri va xotira
"""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:
=== 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: TrueNima ko'rsatdi: 2.1, 2.2, 2.3-bo'limlar.
Misol 2 — Oqimli agregatsiya va xotira farqi
"""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:
=== 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 xotiradaNima ko'rsatdi: 2.3, 2.4, 2.5-bo'limlar.
Misol 3 — Formatlar bo'ylab oqim
"""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:
=== 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.5Nima 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.
"""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:
=== 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 yuklanmadiNima 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
data = open("katta.txt").read() # ❌
for qator in open("katta.txt", encoding="utf-8"): # ✅
...2. Ro'yxatga yig'ish
yozuvlar = [json.loads(q) for q in f] # ❌ hammasi xotirada
jami = sum(json.loads(q)["x"] for q in f) # ✅ oqimli3. Katta JSON ni json.load
json.load(open("10gb.json")) # ❌
# ✅ JSON Lines ga o'ting yoki ijson (oqimli parser)4. fetchall katta natijada
qatorlar = cur.fetchall() # ❌
for qator in con.execute("SELECT ..."): # ✅
...5. Oqim o'rtasida materializatsiya
list(generator)[0] # ❌ hammasini oladi
next(generator) # ✅ yoki islice6. iterparse da clear yo'q
for _, e in ET.iterparse(f): ... # ❌ daraxt to'planadi
...; e.clear() # ✅7. Guruhlashni Python'da (baza bor bo'lsa)
d = defaultdict(list) # ⚠️ hammasi xotirada
for q in f: d[q["shahar"]].append(q)
# ✅ bazaga yuklab SQL GROUP BY, yoki Counter bilan faqat sanoq8. Siqilgan faylni butun o'qish
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
Katta faylni hech qachon
.read()/.readlines()/json.loadbilan o'qimang.Generator quvuri: o'qish → filtrlash → o'zgartirish, hammasi dangasa.
Agregatsiyani yugurib borayotgan holat bilan (
sum,Counter,max).Bazaga yozishni
itertools.batchedva tranzaksiya bilan bo'laklang.Katta ma'lumot uchun JSON Lines (oddiy JSON emas) va
.gzoqimli o'qish.iterparseda har yozuvdan keyinelem.clear().To'liq saralash yoki murakkab guruhlash kerak bo'lsa — bazaga topshiring.
Xotira sarfini
tracemallocbilan o'lchab tasdiqlang.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
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
0 2[0, 1, 2][(0, 1, 2), (3, 4, 5), (6,)]36generator[0, 1, 2, 3][('b', 3)][]— generator bir marta ishlatiladi('a', 'b')3[1, 2, 3, 4]
Vazifa 2: Xatolarni tuzating
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
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 soniVazifa 3: Oqimli grep
grep(namuna, *yollar) — millionlab qatorli fayllarda:
- Qatorma-qator o'qib, mos qatorlarni
(fayl, raqam, matn)bilan bersin (generator) .gzfayllarni ham qo'llasin (kengaytmaga qarab)--count,--after N(moslikdan keyingi N qator) variantlari- Xotira sarfini
tracemallocbilan katta faylda tekshiring
Vazifa 4: Tashqi saralash (external merge sort)
- Xotiraga sig'maydigan faylni bo'laklarga bo'lib, har bo'lakni saralang va vaqtinchalik faylga yozing
- Saralangan bo'laklarni
heapq.mergebilan birlashtiring - 1 000 000 qatorli faylni cheklangan xotirada saralang
- Natijani
sorted(open(...))bilan solishtiring (kichik faylda)
Vazifa 5: Oqimli JSON Lines validatori
- Katta
.jsonlni sxema bo'yicha tekshiring (16.2 g'oyasi), oqimli - Buzuq qatorlarni raqami bilan alohida faylga yozing
- Yaxshi/yomon nisbati va xato turlari statistikasi (
Counter) --fix— tuzatib bo'ladiganlarni tuzatib qayta yozing
Vazifa 6: Real vaqtli quvur (tail -f)
- Faylning oxiriga qo'shilayotgan qatorlarni kuzatuvchi generator (
f.seekbilan) - Har yangi qatorni JSON deb tahlil qilib, ERROR larni sanang
- Har 100 qatorda joriy statistikani chop eting
- 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
- Avval oddiy oqimli Python bilan sinang — ko'p "big data" aslida "bitta mashina data"
- Bir mashina RAM/disk/vaqt chegarasiga urilganda o'lchang, keyin taqsimlashga o'ting
- Taqsimlangan tizim murakkablik va narx qo'shadi — faqat kerak bo'lganda
- Oqim g'oyasi ikkalasida ham bir xil: Spark ham ichida bo'laklab, dangasa ishlaydi
5. Xulosa
- Oqim g'oyasi doimiy xotirada istalgan kattalikni ishlaydi
- Ko'p vazifa butun ma'lumotni ko'rishni talab qilmaydi
- Taqsimlangan tizim — disk/vaqt/global bog'lanish chegarasida
- 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:
Hech qachon hammasini xotiraga olmang. Fayl obyektining o'zi iterator —
for qator in fbutun 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'rtasidagilist()— katta fayldaMemoryErrorsabablari.Ko'p vazifa oqimli hal bo'ladi. Yig'indi, o'rtacha, eng katta, sanoq (
Counter) — yugurib borayotgan holat bilan butun faylni yuklamasdan hisoblanadi; bazaga yuklashniitertools.batchedva 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.Oqim g'oyasi miqyoslanadi.
tracemallocbilan 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:
- Format vazifaga mos bo'lsin: almashinuv — JSON, jadval — CSV, sozlama — TOML/YAML, Python-ichi — pickle/sqlite, katta ma'lumot — JSON Lines + oqim.
- Ishonchsiz ma'lumot — hamma joyda tekshiring:
pickle/yaml.load/XML entity/SQL injection — bularning barchasi ishonchsiz kirishda xavfli. - Kodlash va turlarga e'tibor: UTF-8, BOM,
Decimalpul uchun, sana formatlari. - 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.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!