IlmHamroh
Python kursi/Malumot tuzilmalari14/18-dars34 daqiqa
Mundarija (21)

6.14-dars: Ichma-ich tuzilmalar

6-QISM — MA'LUMOT TUZILMALARI · 14-dars


1. Kirish va motivatsiya

Haqiqiy ma'lumot kamdan-kam tekis bo'ladi. API javobi, konfiguratsiya fayli, ma'lumotlar bazasi natijasi — hammasi ichma-ich:

python
javob = {
    "foydalanuvchi": {
        "id": 42,
        "ism": "Aziz",
        "manzillar": [
            {"turi": "uy", "shahar": "Toshkent", "kocha": "Amir Temur 1"},
            {"turi": "ish", "shahar": "Samarqand", "kocha": None},
        ],
        "sozlamalar": {
            "bildirishnoma": {"email": True, "sms": False},
            "til": "uz",
        },
    },
    "meta": {"versiya": 2, "vaqt": "2026-09-08T10:00:00"},
}

Bunday ma'lumotdan qiymat olish tez qiyinlashadi:

python
shahar = javob["foydalanuvchi"]["manzillar"][0]["shahar"]        # ✅

shahar = javob["foydalanuvchi"]["manzillar"][5]["shahar"]        # ❌ IndexError
email = javob["foydalanuvchi"]["sozlama"]["email"]               # ❌ KeyError
kocha = javob["foydalanuvchi"]["manzillar"][1]["kocha"].upper()  # ❌ AttributeError

Va o'zgartirish yanada xavfliroq:

python
nusxa = javob.copy()
nusxa["foydalanuvchi"]["ism"] = "Boshqa"
print(javob["foydalanuvchi"]["ism"])        # "Boshqa" ← asl ham o'zgardi!

Bu darsda:

  • Ichma-ich tuzilmalarni xavfsiz o'qish (get zanjiri, yordamchi funksiya)
  • Yaratish tuzoqlari ([[0]*3]*3, fromkeys)
  • Aylanib chiqish (rekursiv aylanish)
  • Tekislash (flatten) va teskarisi
  • Ichma-ich birlashtirish, filtrlash, transformatsiya
  • JSON bilan ishlash va validatsiya

2. Nazariya — chuqur tushuntirish

2.1. Yaratish tuzoqlari

* n bilan ko'paytirish (6.1-dars):

python
matritsa = [[0] * 3] * 3            # ❌ BITTA ro'yxatga 3 havola
matritsa[0][0] = 1
print(matritsa)                     # [[1,0,0], [1,0,0], [1,0,0]]

matritsa = [[0] * 3 for _ in range(3)]      # ✅ 3 ta alohida
matritsa[0][0] = 1
print(matritsa)                     # [[1,0,0], [0,0,0], [0,0,0]]

Qoida: * n ichki o'lchamda xavfsiz ([0] * 3 — int o'zgarmas), tashqi o'lchamda xavfli.

dict.fromkeys (6.10-dars):

python
d = dict.fromkeys("abc", [])        # ❌ bitta ro'yxat
d = {k: [] for k in "abc"}          # ✅

Ko'p o'lchovli:

python
# 3D massiv
kub = [[[0] * 4 for _ in range(3)] for _ in range(2)]   # 2×3×4

# Ichma-ich lug'at
from collections import defaultdict
d = defaultdict(lambda: defaultdict(int))
d["a"]["b"] += 1

# Cheksiz daraxt
def daraxt():
    return defaultdict(daraxt)

t = daraxt()
t["a"]["b"]["c"] = 1

defaultdict(daraxt) ni JSON ga aylantirish:

python
import json
json.dumps(t)                       # ✅ ishlaydi (defaultdict — dict)
# Lekin o'qishda kalit yaratadi:
t["yo'q"]["ham"]["yo'q"]            # ⚠️ butun yo'l yaratiladi

2.2. Xavfsiz o'qish

Muammo: har daraja KeyError, IndexError yoki TypeError berishi mumkin.

1. get zanjiri:

python
shahar = (javob
          .get("foydalanuvchi", {})
          .get("manzillar", [{}])[0]
          .get("shahar"))

Uzun, o'qish qiyin, va ro'yxat indeksi bilan baribir xavfli.

2. try/except:

python
try:
    shahar = javob["foydalanuvchi"]["manzillar"][0]["shahar"]
except (KeyError, IndexError, TypeError):
    shahar = None

Ishlaydi, lekin har murojaat uchun blok yozish kerak.

3. Yordamchi funksiya:

python
def chuqur_ol(malumot, *yol, sukut=None):
    """Ichma-ich tuzilmadan xavfsiz o'qish."""
    joriy = malumot
    for qadam in yol:
        try:
            joriy = joriy[qadam]
        except (KeyError, IndexError, TypeError):
            return sukut
    return joriy


chuqur_ol(javob, "foydalanuvchi", "manzillar", 0, "shahar")      # "Toshkent"
chuqur_ol(javob, "foydalanuvchi", "yo'q", 5, "x", sukut="—")     # "—"

4. Nuqtali yo'l:

python
def yol_ol(malumot, yol: str, sukut=None):
    """'a.b.0.c' shaklidagi yo'l bilan."""
    joriy = malumot
    for qadam in yol.split("."):
        try:
            joriy = joriy[int(qadam)] if qadam.isdigit() else joriy[qadam]
        except (KeyError, IndexError, TypeError, ValueError):
            return sukut
    return joriy


yol_ol(javob, "foydalanuvchi.manzillar.0.shahar")

Nuqtali yo'l cheklovi: kalitda nuqta bo'lsa ishlamaydi.

5. Tashqi kutubxonalar:

python
# glom
from glom import glom
glom(javob, "foydalanuvchi.manzillar.0.shahar")

# jmespath
import jmespath
jmespath.search("foydalanuvchi.manzillar[0].shahar", javob)

Murakkab so'rovlar uchun foydali, lekin oddiy holatda o'z funksiyangiz yetarli.

2.3. Yozish va yo'l yaratish

python
def chuqur_qoy(malumot: dict, yol: list, qiymat) -> None:
    """Oraliq lug'atlarni yaratib, qiymat yozadi."""
    joriy = malumot
    for qadam in yol[:-1]:
        joriy = joriy.setdefault(qadam, {})
    joriy[yol[-1]] = qiymat


d = {}
chuqur_qoy(d, ["a", "b", "c"], 1)
print(d)                            # {'a': {'b': {'c': 1}}}

Mavjud non-dict qiymatni bosib o'tish:

python
d = {"a": 5}
chuqur_qoy(d, ["a", "b"], 1)        # ❌ AttributeError: 'int' has no 'setdefault'

Xavfsizroq:

python
def chuqur_qoy(malumot: dict, yol: list, qiymat, majburiy=False) -> bool:
    joriy = malumot
    for qadam in yol[:-1]:
        keyingi = joriy.get(qadam)
        if not isinstance(keyingi, dict):
            if keyingi is not None and not majburiy:
                return False        # mavjud qiymatni buzmaymiz
            joriy[qadam] = {}
        joriy = joriy[qadam]
    joriy[yol[-1]] = qiymat
    return True

2.4. Rekursiv aylanib chiqish

Barcha barg qiymatlarni topish:

python
def barglar(malumot, yol=()):
    """(yol, qiymat) juftliklarini beradi."""
    if isinstance(malumot, dict):
        for k, v in malumot.items():
            yield from barglar(v, yol + (k,))
    elif isinstance(malumot, (list, tuple)):
        for i, v in enumerate(malumot):
            yield from barglar(v, yol + (i,))
    else:
        yield yol, malumot


for yol, qiymat in barglar(javob):
    print(".".join(map(str, yol)), "=", qiymat)

Satr — iteratsiya qilinadigan, lekin barg:

python
isinstance("abc", (list, tuple))    # False ✅
# Lekin agar Sequence tekshirsangiz:
from collections.abc import Sequence
isinstance("abc", Sequence)         # True ⚠️ — satr belgilarga bo'linadi

Doim str va bytes ni alohida tekshiring.

Aylanma havolalar — cheksiz rekursiya:

python
d = {"a": 1}
d["o'zi"] = d                       # aylanma!

list(barglar(d))                    # ❌ RecursionError

Himoya:

python
def barglar(malumot, yol=(), korilgan=None):
    if korilgan is None:
        korilgan = set()
    if id(malumot) in korilgan:
        yield yol, "<AYLANMA>"
        return
    if isinstance(malumot, (dict, list, tuple)):
        korilgan = korilgan | {id(malumot)}
    ...

Rekursiya chuqurligi:

python
import sys
sys.getrecursionlimit()             # 1000 (sukut)

Juda chuqur tuzilmalar uchun iterativ yechim (stek bilan):

python
def barglar_iterativ(malumot):
    stek = [((), malumot)]
    while stek:
        yol, joriy = stek.pop()
        if isinstance(joriy, dict):
            stek.extend((yol + (k,), v) for k, v in reversed(joriy.items()))
        elif isinstance(joriy, (list, tuple)):
            stek.extend((yol + (i,), v) for i, v in reversed(list(enumerate(joriy))))
        else:
            yield yol, joriy

2.5. Tekislash va tiklash

Tekislash (flatten):

python
def tekisla(malumot, ajratgich=".", prefiks=""):
    """{'a': {'b': 1}} → {'a.b': 1}"""
    natija = {}
    if isinstance(malumot, dict):
        for k, v in malumot.items():
            yangi = f"{prefiks}{ajratgich}{k}" if prefiks else str(k)
            natija |= tekisla(v, ajratgich, yangi)
    elif isinstance(malumot, list):
        for i, v in enumerate(malumot):
            yangi = f"{prefiks}{ajratgich}{i}" if prefiks else str(i)
            natija |= tekisla(v, ajratgich, yangi)
    else:
        natija[prefiks] = malumot
    return natija

Tiklash (unflatten):

python
def tikla(tekis, ajratgich="."):
    """{'a.b': 1} → {'a': {'b': 1}}"""
    natija = {}
    for yol, qiymat in tekis.items():
        joriy = natija
        qadamlar = yol.split(ajratgich)
        for qadam in qadamlar[:-1]:
            joriy = joriy.setdefault(qadam, {})
        joriy[qadamlar[-1]] = qiymat
    return natija

Tekislash yo'qotishlari:

  • Ro'yxat indekslari satrga aylanadi ("0", "1") — tiklaganda lug'at bo'ladi
  • Kalitda ajratgich bo'lsa buziladi
  • Bo'sh lug'at/ro'yxat yo'qoladi

Aniq tiklash kerak bo'lsa ro'yxat indekslarini alohida belgilang: a[0].b.

Ro'yxatlarni tekislash:

python
# Bir daraja
[x for qism in ichma_ich for x in qism]
list(itertools.chain.from_iterable(ichma_ich))

# Barcha daraja
def tekisla_royxat(r):
    for x in r:
        if isinstance(x, (list, tuple)):
            yield from tekisla_royxat(x)
        else:
            yield x

2.6. Nusxa olish (18-darsga kirish)

python
import copy

d = {"a": {"b": [1, 2]}}

sayoz = d.copy()                    # ichki obyektlar UMUMIY
chuqur = copy.deepcopy(d)           # to'liq alohida

sayoz["a"]["b"].append(3)
print(d)                            # {'a': {'b': [1, 2, 3]}}  ⚠️

chuqur["a"]["b"].append(4)
print(d)                            # o'zgarmadi ✅

Qoida: ichma-ich tuzilmani "nusxalash" deganda deyarli har doim deepcopy kerak.

deepcopy sekin:

python
# Katta tuzilma uchun
import json
nusxa = json.loads(json.dumps(d))   # ~5-10x tezroq, lekin faqat JSON turlari

18-darsda batafsil.

2.7. Ichma-ich birlashtirish va transformatsiya

Chuqur birlashtirish (6.11-dars):

python
def chuqur_birlashtir(a: dict, b: dict) -> dict:
    natija = dict(a)
    for k, v in b.items():
        if k in natija and isinstance(natija[k], dict) and isinstance(v, dict):
            natija[k] = chuqur_birlashtir(natija[k], v)
        else:
            natija[k] = v
    return natija

Rekursiv transformatsiya:

python
def ozgartir(malumot, f):
    """Barcha barg qiymatlarga f ni qo'llaydi."""
    if isinstance(malumot, dict):
        return {k: ozgartir(v, f) for k, v in malumot.items()}
    if isinstance(malumot, list):
        return [ozgartir(v, f) for v in malumot]
    if isinstance(malumot, tuple):
        return tuple(ozgartir(v, f) for v in malumot)
    return f(malumot)


ozgartir(javob, lambda x: x.strip() if isinstance(x, str) else x)

Kalitlarni o'zgartirish:

python
def kalitlarni_ozgartir(malumot, f):
    if isinstance(malumot, dict):
        return {f(k): kalitlarni_ozgartir(v, f) for k, v in malumot.items()}
    if isinstance(malumot, list):
        return [kalitlarni_ozgartir(v, f) for v in malumot]
    return malumot


# camelCase → snake_case
import re
kalitlarni_ozgartir(javob, lambda k: re.sub(r"(?<!^)(?=[A-Z])", "_", k).lower())

Rekursiv filtrlash:

python
def bosh_ni_ochir(malumot):
    """None, bo'sh satr, bo'sh ro'yxat/lug'atni olib tashlaydi."""
    if isinstance(malumot, dict):
        natija = {k: bosh_ni_ochir(v) for k, v in malumot.items()}
        return {k: v for k, v in natija.items() if v not in (None, "", [], {})}
    if isinstance(malumot, list):
        natija = [bosh_ni_ochir(v) for v in malumot]
        return [v for v in natija if v not in (None, "", [], {})]
    return malumot

v not in (None, "", [], {}) — 0 va False ham tushib qoladimi?

python
0 in (None, "", [], {})             # ⚠️ False — 0 != None, 0 != ""
False in (None, "", [], {})         # ⚠️ False

Yaxshi, ular saqlanadi. Lekin ehtiyot bo'ling:

python
0 == False                          # True
0 in (False,)                       # True ⚠️

Aniq tekshiruv:

python
def boshmi(v):
    return v is None or (isinstance(v, (str, list, dict, tuple)) and not v)

3. Tez ma'lumotnoma

Yaratish tuzoqlari

python
[[0]*3]*3               ❌ bitta ro'yxat
[[0]*3 for _ in range(3)]   ✅

dict.fromkeys("ab", [])     ❌ bitta ro'yxat
{k: [] for k in "ab"}       ✅

defaultdict(lambda: defaultdict(int))       ikki daraja
def daraxt(): return defaultdict(daraxt)    cheksiz

Xavfsiz o'qish

python
def chuqur_ol(m, *yol, sukut=None):
    joriy = m
    for q in yol:
        try: joriy = joriy[q]
        except (KeyError, IndexError, TypeError): return sukut
    return joriy

Aylanib chiqish

python
def barglar(m, yol=()):
    if isinstance(m, dict):
        for k, v in m.items(): yield from barglar(v, yol+(k,))
    elif isinstance(m, (list, tuple)):
        for i, v in enumerate(m): yield from barglar(v, yol+(i,))
    else: yield yol, m

⚠️ str/bytes alohida — Sequence deb tekshirmang
⚠️ Aylanma havola → id() to'plami bilan himoya
⚠️ Chuqur → iterativ (stek bilan)

Nusxa

python
d.copy()                sayoz — ichki UMUMIY  ⚠️
copy.deepcopy(d)        chuqur ✅
json.loads(json.dumps(d))   tezroq, faqat JSON turlari

Transformatsiya

python
tekisla(d)              {'a': {'b': 1}} → {'a.b': 1}
tikla(d)                teskari
chuqur_birlashtir(a,b)  rekursiv
ozgartir(d, f)          barg qiymatlarga f
kalitlarni_ozgartir(d, f)   kalitlarga f

4. Batafsil misollar

Misol 1 — Yaratish va xavfsiz o'qish

python
"""Ichma-ich tuzilmalarni to'g'ri yaratish va o'qish."""

from collections import defaultdict

print("=== 1. ⚠️ Yaratish tuzoqlari ===")

print("  Matritsa:\n")

yomon = [[0] * 3] * 3
yaxshi = [[0] * 3 for _ in range(3)]

yomon[0][0] = 1
yaxshi[0][0] = 1

print(f"    [[0]*3]*3:                  {yomon}")
print(f"    [[0]*3 for _ in range(3)]:  {yaxshi}")
print(f"\n    yomon[0] is yomon[1]:   {yomon[0] is yomon[1]}   ⚠️")
print(f"    yaxshi[0] is yaxshi[1]: {yaxshi[0] is yaxshi[1]}   ✅")

print(f"\n  ⭐ Qoida: * n ICHKI o'lchamda xavfsiz ([0]*3 — int o'zgarmas),")
print(f"            TASHQI o'lchamda xavfli")

print("\n  Lug'at:\n")
d_yomon = dict.fromkeys("abc", [])
d_yaxshi = {k: [] for k in "abc"}

d_yomon["a"].append(1)
d_yaxshi["a"].append(1)

print(f"    dict.fromkeys('abc', []): {d_yomon}   ⚠️")
print(f"    {{k: [] for k in 'abc'}}:   {d_yaxshi}   ✅")


print("\n=== 2. Ko'p o'lchovli tuzilmalar ===")

# 3D
kub = [[[0] * 4 for _ in range(3)] for _ in range(2)]
kub[0][1][2] = 9
print(f"  3D massiv (2×3×4), kub[0][1][2] = 9:")
for i, qavat in enumerate(kub):
    print(f"    Qavat {i}:")
    for qator in qavat:
        print(f"      {qator}")

# Ikki darajali defaultdict
print(f"\n  defaultdict(lambda: defaultdict(int)):")
sotuvlar = defaultdict(lambda: defaultdict(int))
MALUMOT = [
    ("Toshkent", "non", 120), ("Toshkent", "sut", 80),
    ("Samarqand", "non", 200), ("Toshkent", "non", 50),
]
for shahar, mahsulot, soni in MALUMOT:
    sotuvlar[shahar][mahsulot] += soni

for shahar, mahsulotlar in sotuvlar.items():
    print(f"    {shahar}:")
    for mahsulot, soni in mahsulotlar.items():
        print(f"      {mahsulot:<8} {soni}")

# Cheksiz daraxt
def daraxt():
    return defaultdict(daraxt)


t = daraxt()
t["loyiha"]["src"]["main.py"] = "1.2 KB"
t["loyiha"]["src"]["utils"]["helper.py"] = "0.8 KB"
t["loyiha"]["README.md"] = "3.1 KB"


def chiz(d, prefiks=""):
    elementlar = list(d.items())
    for i, (nom, qiymat) in enumerate(elementlar):
        oxirgi = i == len(elementlar) - 1
        belgi = "└── " if oxirgi else "├── "
        if isinstance(qiymat, dict):
            print(f"    {prefiks}{belgi}{nom}/")
            chiz(qiymat, prefiks + ("    " if oxirgi else "│   "))
        else:
            print(f"    {prefiks}{belgi}{nom}  ({qiymat})")


print(f"\n  Cheksiz daraxt (fayl tizimi):")
chiz(t)


print("\n\n=== 3. Xavfsiz o'qish ===")

JAVOB = {
    "foydalanuvchi": {
        "id": 42,
        "ism": "Aziz",
        "manzillar": [
            {"turi": "uy", "shahar": "Toshkent", "kocha": "Amir Temur 1"},
            {"turi": "ish", "shahar": "Samarqand", "kocha": None},
        ],
        "sozlamalar": {
            "bildirishnoma": {"email": True, "sms": False},
            "til": "uz",
        },
    },
    "meta": {"versiya": 2},
}


def chuqur_ol(malumot, *yol, sukut=None):
    """Ichma-ich tuzilmadan xavfsiz o'qish."""
    joriy = malumot
    for qadam in yol:
        try:
            joriy = joriy[qadam]
        except (KeyError, IndexError, TypeError):
            return sukut
    return joriy


def yol_ol(malumot, yol: str, sukut=None):
    """'a.b.0.c' shaklidagi yo'l."""
    joriy = malumot
    for qadam in yol.split("."):
        try:
            joriy = joriy[int(qadam)] if qadam.lstrip("-").isdigit() else joriy[qadam]
        except (KeyError, IndexError, TypeError, ValueError):
            return sukut
    return joriy


YOLLAR = [
    ("foydalanuvchi", "ism"),
    ("foydalanuvchi", "manzillar", 0, "shahar"),
    ("foydalanuvchi", "manzillar", 1, "kocha"),
    ("foydalanuvchi", "sozlamalar", "bildirishnoma", "email"),
    ("foydalanuvchi", "manzillar", 5, "shahar"),
    ("foydalanuvchi", "yo'q", "kalit"),
    ("meta", "versiya"),
    ("foydalanuvchi", "ism", "ichida"),
]

print(f"  {'Yo`l':<48} {'Natija'}")
print("  " + "─" * 66)
for yol in YOLLAR:
    natija = chuqur_ol(JAVOB, *yol, sukut="<yo'q>")
    korinish = " → ".join(map(str, yol))
    print(f"  {korinish:<48} {natija!r}")

print(f"\n  Nuqtali yo'l bilan:")
for yol in ["foydalanuvchi.ism",
            "foydalanuvchi.manzillar.0.shahar",
            "foydalanuvchi.sozlamalar.bildirishnoma.sms",
            "foydalanuvchi.manzillar.9.shahar"]:
    print(f"    {yol:<44} {yol_ol(JAVOB, yol, '<yo`q>')!r}")


print("\n=== 4. Usullarni solishtirish ===")

print("  ❌ To'g'ridan-to'g'ri:")
try:
    JAVOB["foydalanuvchi"]["manzillar"][5]["shahar"]
except IndexError as x:
    print(f"    [...][5][...]  → IndexError: {x}")

print("\n  ⚠️ get zanjiri:")
natija = (JAVOB
          .get("foydalanuvchi", {})
          .get("manzillar", [{}])[0]
          .get("shahar"))
print(f"    .get(...).get(...)[0].get(...)  → {natija!r}")
print(f"    Lekin [5] uchun baribir IndexError")

print("\n  ✅ try/except:")
try:
    natija = JAVOB["foydalanuvchi"]["manzillar"][5]["shahar"]
except (KeyError, IndexError, TypeError):
    natija = None
print(f"    try/except  → {natija!r}")

print("\n  ⭐ Yordamchi funksiya:")
print(f"    chuqur_ol(...)  → "
      f"{chuqur_ol(JAVOB, 'foydalanuvchi', 'manzillar', 5, 'shahar')!r}")
print(f"    Bir marta yoziladi, hamma joyda ishlatiladi")


print("\n=== 5. Yozish va yo'l yaratish ===")


def chuqur_qoy(malumot: dict, yol: list, qiymat, majburiy=False) -> bool:
    """Oraliq lug'atlarni yaratib qiymat yozadi."""
    joriy = malumot
    for qadam in yol[:-1]:
        keyingi = joriy.get(qadam)
        if not isinstance(keyingi, dict):
            if keyingi is not None and not majburiy:
                return False
            joriy[qadam] = {}
        joriy = joriy[qadam]
    joriy[yol[-1]] = qiymat
    return True


d = {}
QOYISHLAR = [
    (["a", "b", "c"], 1),
    (["a", "b", "d"], 2),
    (["a", "x"], 3),
    (["y"], 4),
]

print(f"  Bo'sh lug'atdan boshlab:\n")
for yol, qiymat in QOYISHLAR:
    chuqur_qoy(d, yol, qiymat)
    print(f"    chuqur_qoy(d, {yol}, {qiymat})")
    print(f"      → {d}")

print(f"\n  ⚠️ Mavjud non-dict qiymatni bosib o'tish:")
d2 = {"a": 5}
natija = chuqur_qoy(d2, ["a", "b"], 1)
print(f"    d = {{'a': 5}}")
print(f"    chuqur_qoy(d, ['a','b'], 1)             → {natija}  (bekor qilindi)")
print(f"    d = {d2}   ✅ o'zgarmadi")

natija = chuqur_qoy(d2, ["a", "b"], 1, majburiy=True)
print(f"\n    chuqur_qoy(..., majburiy=True)         → {natija}")
print(f"    d = {d2}   ⚠️ 5 yo'qoldi")

Natijaning muhim qismi:

text
=== 1. ⚠️ Yaratish tuzoqlari ===
  Matritsa:

    [[0]*3]*3:                  [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
    [[0]*3 for _ in range(3)]:  [[1, 0, 0], [0, 0, 0], [0, 0, 0]]

    yomon[0] is yomon[1]:   True   ⚠️
    yaxshi[0] is yaxshi[1]: False   ✅

=== 3. Xavfsiz o'qish ===
  Yo`l                                             Natija
  ──────────────────────────────────────────────────────────────────
  foydalanuvchi → ism                              'Aziz'
  foydalanuvchi → manzillar → 0 → shahar           'Toshkent'
  foydalanuvchi → manzillar → 1 → kocha            None
  foydalanuvchi → manzillar → 5 → shahar           "<yo'q>"
  foydalanuvchi → ism → ichida                     "<yo'q>"

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

Misol 2 — Aylanib chiqish va tekislash

python
"""Rekursiv aylanish, tekislash va tiklash."""

import sys

MALUMOT = {
    "loyiha": "Wisar",
    "versiya": 2,
    "muallif": {"ism": "Sarvarbek", "email": "s@wisar.uz"},
    "bogliqliklar": ["next", "react", "prisma"],
    "sozlamalar": {
        "baza": {"host": "localhost", "port": 5432},
        "kesh": {"ttl": 300, "yoqilgan": True},
    },
    "teglar": [
        {"nom": "python", "og'irlik": 5},
        {"nom": "veb", "og'irlik": 3},
    ],
}

print("=== 1. Barcha barglarni topish ===")


def barglar(malumot, yol=(), korilgan=None):
    """(yol, qiymat) juftliklari. Aylanma havoladan himoyalangan."""
    if korilgan is None:
        korilgan = frozenset()

    if isinstance(malumot, (dict, list, tuple)):
        if id(malumot) in korilgan:
            yield yol, "<AYLANMA>"
            return
        korilgan = korilgan | {id(malumot)}

    if isinstance(malumot, dict):
        for k, v in malumot.items():
            yield from barglar(v, yol + (k,), korilgan)
    elif isinstance(malumot, (list, tuple)):
        for i, v in enumerate(malumot):
            yield from barglar(v, yol + (i,), korilgan)
    else:
        yield yol, malumot


natijalar = list(barglar(MALUMOT))
print(f"  {len(natijalar)} ta barg:\n")
for yol, qiymat in natijalar:
    korinish = ".".join(map(str, yol))
    print(f"    {korinish:<34} = {qiymat!r}")


print("\n=== 2. Aylanma havola ===")

aylanma = {"a": 1, "ichki": {"b": 2}}
aylanma["o'zi"] = aylanma
aylanma["ichki"]["ota"] = aylanma

print(f"  d = {{'a': 1, 'ichki': {{...}}}};  d['o`zi'] = d\n")
for yol, qiymat in barglar(aylanma):
    print(f"    {'.'.join(map(str, yol)):<20} = {qiymat!r}")

print(f"\n  ⭐ id() to'plami bilan himoya — cheksiz rekursiya yo'q")


print("\n=== 3. Chuqur tuzilma — iterativ yechim ===")


def chuqur_yaratish(n):
    """n darajali ichma-ich lug'at."""
    d = {"qiymat": "eng chuqur"}
    for i in range(n):
        d = {"daraja": d}
    return d


def barglar_iterativ(malumot):
    """Rekursiyasiz — chuqurlik cheklovi yo'q."""
    stek = [((), malumot)]
    while stek:
        yol, joriy = stek.pop()
        if isinstance(joriy, dict):
            stek.extend((yol + (k,), v) for k, v in reversed(list(joriy.items())))
        elif isinstance(joriy, (list, tuple)):
            stek.extend((yol + (i,), v)
                        for i, v in reversed(list(enumerate(joriy))))
        else:
            yield yol, joriy


print(f"  Rekursiya chegarasi: {sys.getrecursionlimit()}\n")

for n in [100, 500, 2000]:
    chuqur = chuqur_yaratish(n)

    try:
        natija = list(barglar(chuqur))
        rekursiv = f"✅ {len(natija)} barg"
    except RecursionError:
        rekursiv = "❌ RecursionError"

    natija = list(barglar_iterativ(chuqur))
    iterativ = f"✅ {len(natija)} barg"

    print(f"  {n:>5} daraja:  rekursiv: {rekursiv:<22} iterativ: {iterativ}")

print(f"\n  ⭐ Juda chuqur tuzilmalar uchun stek bilan iterativ yechim")


print("\n=== 4. Tekislash ===")


def tekisla(malumot, ajratgich=".", prefiks=""):
    natija = {}
    if isinstance(malumot, dict) and malumot:
        for k, v in malumot.items():
            yangi = f"{prefiks}{ajratgich}{k}" if prefiks else str(k)
            natija |= tekisla(v, ajratgich, yangi)
    elif isinstance(malumot, list) and malumot:
        for i, v in enumerate(malumot):
            yangi = f"{prefiks}{ajratgich}{i}" if prefiks else str(i)
            natija |= tekisla(v, ajratgich, yangi)
    else:
        natija[prefiks] = malumot
    return natija


tekis = tekisla(MALUMOT)
print(f"  {len(tekis)} ta yozuv:\n")
for yol, qiymat in tekis.items():
    print(f"    {yol:<34} = {qiymat!r}")


print("\n=== 5. Tiklash ===")


def tikla(tekis, ajratgich="."):
    natija = {}
    for yol, qiymat in tekis.items():
        joriy = natija
        qadamlar = yol.split(ajratgich)
        for qadam in qadamlar[:-1]:
            joriy = joriy.setdefault(qadam, {})
        joriy[qadamlar[-1]] = qiymat
    return natija


tiklangan = tikla(tekis)

print(f"  Asl == tiklangan: {MALUMOT == tiklangan}\n")
print(f"  ⚠️ Farq — ro'yxatlar lug'atga aylandi:")
print(f"    Asl:       {MALUMOT['bogliqliklar']}")
print(f"    Tiklangan: {tiklangan['bogliqliklar']}")

print(f"\n  ✅ Ro'yxatlarni ham tiklaydigan versiya:")


def tikla_royxatlar(d):
    """Kalitlari 0,1,2... bo'lgan lug'atlarni ro'yxatga aylantiradi."""
    if not isinstance(d, dict):
        return d
    natija = {k: tikla_royxatlar(v) for k, v in d.items()}
    kalitlar = list(natija)
    if kalitlar and all(k.isdigit() for k in kalitlar):
        if sorted(int(k) for k in kalitlar) == list(range(len(kalitlar))):
            return [natija[str(i)] for i in range(len(kalitlar))]
    return natija


toliq = tikla_royxatlar(tikla(tekis))
print(f"    Asl == to'liq tiklangan: {MALUMOT == toliq}")
print(f"    bogliqliklar: {toliq['bogliqliklar']}")

print("""
  ⚠️ Tekislash yo'qotishlari:
     • Ro'yxat indekslari satrga aylanadi
     • Kalitda ajratgich bo'lsa buziladi
     • Bo'sh lug'at/ro'yxat noaniq bo'lib qoladi
""")

print("  Kalitda nuqta muammosi:")
muammoli = {"a.b": {"c": 1}, "a": {"b.c": 2}}
t = tekisla(muammoli)
print(f"    Asl:      {muammoli}")
print(f"    Tekis:    {t}   ⚠️ ikkalasi 'a.b.c' bo'ldi!")
print(f"    Tiklangan: {tikla(t)}")


print("\n=== 6. Ro'yxatlarni tekislash ===")

ICHMA_ICH = [1, [2, 3], [4, [5, 6, [7, 8]]], 9, [[10]]]

print(f"  Asl: {ICHMA_ICH}\n")

# Bir daraja
from itertools import chain
bir_daraja = [1, [2, 3], [4, 5]]
print(f"  Bir daraja:")
print(f"    [x for q in r for x in q]           "
      f"{[x for q in [[1, 2], [3, 4]] for x in q]}")
print(f"    list(chain.from_iterable(r))        "
      f"{list(chain.from_iterable([[1, 2], [3, 4]]))}")


def tekisla_royxat(r):
    for x in r:
        if isinstance(x, (list, tuple)):
            yield from tekisla_royxat(x)
        else:
            yield x


print(f"\n  Barcha daraja:")
print(f"    {list(tekisla_royxat(ICHMA_ICH))}")


def tekisla_royxat_daraja(r, daraja=1):
    """Faqat n darajagacha."""
    for x in r:
        if isinstance(x, (list, tuple)) and daraja > 0:
            yield from tekisla_royxat_daraja(x, daraja - 1)
        else:
            yield x


print(f"\n  Chegaralangan chuqurlik:")
for d in [1, 2, 3, 10]:
    print(f"    daraja={d}: {list(tekisla_royxat_daraja(ICHMA_ICH, d))}")

print(f"\n  ⚠️ Satr — iteratsiya qilinadigan, lekin barg:")
matnli = ["abc", ["def", "gh"]]
print(f"    {matnli}")
print(f"    → {list(tekisla_royxat(matnli))}   ✅ satrlar bo'linmadi")

from collections.abc import Sequence


def yomon_tekisla(r):
    for x in r:
        if isinstance(x, Sequence) and not isinstance(x, str):
            yield from yomon_tekisla(x)
        else:
            yield x


print(f"    Sequence bilan (str istisnosiz) — cheksiz rekursiya xavfi")

Natijaning muhim qismi:

text
=== 2. Aylanma havola ===
  d = {'a': 1, 'ichki': {...}};  d['o`zi'] = d

    a                    = 1
    ichki.b              = 2
    ichki.ota            = '<AYLANMA>'
    o'zi                 = '<AYLANMA>'

=== 3. Chuqur tuzilma — iterativ yechim ===
  Rekursiya chegarasi: 1000

    100 daraja:  rekursiv: ✅ 1 barg               iterativ: ✅ 1 barg
    500 daraja:  rekursiv: ✅ 1 barg               iterativ: ✅ 1 barg
   2000 daraja:  rekursiv: ❌ RecursionError       iterativ: ✅ 1 barg

=== 5. Tiklash ===
  Asl == tiklangan: False

  ⚠️ Farq — ro'yxatlar lug'atga aylandi:
    Asl:       ['next', 'react', 'prisma']
    Tiklangan: {'0': 'next', '1': 'react', '2': 'prisma'}

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

Misol 3 — Transformatsiya

python
"""Ichma-ich tuzilmalarni o'zgartirish."""

import re
import copy
import json
import time

XOM = {
    "userId": 42,
    "userName": "  Aziz Karimov  ",
    "emailAddress": "AZIZ@MAIL.UZ",
    "isActive": True,
    "lastLogin": None,
    "profileSettings": {
        "displayName": "  aziz  ",
        "notificationPrefs": {"emailEnabled": True, "smsEnabled": None},
        "themeName": "",
    },
    "recentOrders": [
        {"orderId": 1, "totalAmount": "120000", "orderNotes": "  tez  "},
        {"orderId": 2, "totalAmount": "80000", "orderNotes": ""},
    ],
    "emptyList": [],
    "emptyDict": {},
}

print("=== 1. Qiymatlarni o'zgartirish ===")


def ozgartir(malumot, f):
    """Barcha BARG qiymatlarga f ni qo'llaydi."""
    if isinstance(malumot, dict):
        return {k: ozgartir(v, f) for k, v in malumot.items()}
    if isinstance(malumot, list):
        return [ozgartir(v, f) for v in malumot]
    if isinstance(malumot, tuple):
        return tuple(ozgartir(v, f) for v in malumot)
    return f(malumot)


def tozala(x):
    if isinstance(x, str):
        return x.strip()
    return x


tozalangan = ozgartir(XOM, tozala)

print(f"  strip() barcha satrlarga:\n")
print(f"    userName:                  {XOM['userName']!r}")
print(f"                            →  {tozalangan['userName']!r}")
print(f"    profileSettings.displayName: {XOM['profileSettings']['displayName']!r}")
print(f"                            →  {tozalangan['profileSettings']['displayName']!r}")
print(f"    recentOrders[0].orderNotes: {XOM['recentOrders'][0]['orderNotes']!r}")
print(f"                            →  {tozalangan['recentOrders'][0]['orderNotes']!r}")


print("\n=== 2. Kalitlarni o'zgartirish ===")


def kalitlarni_ozgartir(malumot, f):
    if isinstance(malumot, dict):
        return {f(k): kalitlarni_ozgartir(v, f) for k, v in malumot.items()}
    if isinstance(malumot, list):
        return [kalitlarni_ozgartir(v, f) for v in malumot]
    return malumot


def camel_to_snake(nom: str) -> str:
    return re.sub(r"(?<!^)(?=[A-Z])", "_", nom).lower()


def snake_to_camel(nom: str) -> str:
    qismlar = nom.split("_")
    return qismlar[0] + "".join(q.capitalize() for q in qismlar[1:])


snake = kalitlarni_ozgartir(XOM, camel_to_snake)
qaytgan = kalitlarni_ozgartir(snake, snake_to_camel)

print(f"  camelCase → snake_case:\n")
for eski, yangi in list(zip(XOM, snake))[:5]:
    print(f"    {eski:<22} → {yangi}")

print(f"\n  Ichma-ich:")
print(f"    profileSettings.notificationPrefs.emailEnabled")
print(f"    → profile_settings.notification_prefs.email_enabled")

print(f"\n  Teskari aylantirish to'g'rimi: {qaytgan.keys() == XOM.keys()}")


print("\n=== 3. Bo'sh qiymatlarni olib tashlash ===")


def boshmi(v) -> bool:
    """None yoki bo'sh konteyner."""
    if v is None:
        return True
    if isinstance(v, (str, list, dict, tuple, set)) and len(v) == 0:
        return True
    return False


def bosh_ni_ochir(malumot):
    if isinstance(malumot, dict):
        natija = {k: bosh_ni_ochir(v) for k, v in malumot.items()}
        return {k: v for k, v in natija.items() if not boshmi(v)}
    if isinstance(malumot, list):
        natija = [bosh_ni_ochir(v) for v in malumot]
        return [v for v in natija if not boshmi(v)]
    return malumot


toza = bosh_ni_ochir(tozalangan)

print(f"  Asl kalitlar:  {len(XOM)}")
print(f"  Toza kalitlar: {len(toza)}\n")
print(f"  Olib tashlangan:")
for k in XOM:
    if k not in toza:
        print(f"    {k:<22} = {XOM[k]!r}")

print(f"\n  Ichma-ich ham:")
print(f"    profileSettings: {list(XOM['profileSettings'])}")
print(f"                  →  {list(toza['profileSettings'])}")

print(f"\n  ⚠️ 0 va False saqlanadi:")
sinov = {"nol": 0, "yolgon": False, "bosh_satr": "", "yoq": None, "bosh_royxat": []}
print(f"    {sinov}")
print(f"    → {bosh_ni_ochir(sinov)}")


print("\n=== 4. Turlarni aylantirish ===")


def turlarni_tikla(malumot):
    """Satr ko'rinishidagi sonlarni va bayroqlarni tiklaydi."""
    if isinstance(malumot, dict):
        return {k: turlarni_tikla(v) for k, v in malumot.items()}
    if isinstance(malumot, list):
        return [turlarni_tikla(v) for v in malumot]
    if isinstance(malumot, str):
        past = malumot.lower()
        if past in ("true", "ha", "yes"):
            return True
        if past in ("false", "yo'q", "no"):
            return False
        if past in ("null", "none", "nil"):
            return None
        if malumot.lstrip("-").isdigit():
            return int(malumot)
        try:
            return float(malumot)
        except ValueError:
            return malumot
    return malumot


SATRLI = {
    "port": "5432",
    "ssl": "true",
    "timeout": "30.5",
    "nom": "wisar",
    "kesh": {"ttl": "300", "yoqilgan": "false"},
    "hostlar": ["db1", "5432", "true"],
}

tiklangan = turlarni_tikla(SATRLI)

print(f"  {'Kalit':<16} {'Asl':<16} {'Tiklangan':<16} Turi")
print("  " + "─" * 58)


def solishtir(a, b, prefiks=""):
    if isinstance(a, dict):
        for k in a:
            solishtir(a[k], b[k], f"{prefiks}{k}.")
    elif isinstance(a, list):
        for i, (x, y) in enumerate(zip(a, b)):
            solishtir(x, y, f"{prefiks}{i}.")
    else:
        nom = prefiks.rstrip(".")
        print(f"  {nom:<16} {str(a):<16} {str(b):<16} {type(b).__name__}")


solishtir(SATRLI, tiklangan)


print("\n=== 5. Chuqur birlashtirish ===")


def chuqur_birlashtir(*lugatlar, royxat_qoshish=False):
    natija = {}
    for d in lugatlar:
        for k, v in d.items():
            if k in natija and isinstance(natija[k], dict) and isinstance(v, dict):
                natija[k] = chuqur_birlashtir(natija[k], v,
                                              royxat_qoshish=royxat_qoshish)
            elif (royxat_qoshish and k in natija
                  and isinstance(natija[k], list) and isinstance(v, list)):
                natija[k] = natija[k] + v
            else:
                natija[k] = v
    return natija


STANDART = {
    "baza": {"host": "localhost", "port": 5432, "ssl": False},
    "kesh": {"ttl": 300},
    "hostlar": ["localhost"],
    "debug": False,
}
FAYLDAN = {
    "baza": {"host": "db.internal", "ssl": True},
    "hostlar": ["db1", "db2"],
    "log": "INFO",
}
MUHITDAN = {
    "baza": {"port": 5433},
    "debug": True,
}

print(f"  Uch qatlam:\n")
for nom, d in [("STANDART", STANDART), ("FAYLDAN", FAYLDAN), ("MUHITDAN", MUHITDAN)]:
    print(f"    {nom}: {d}")

sayoz = {**STANDART, **FAYLDAN, **MUHITDAN}
chuqur = chuqur_birlashtir(STANDART, FAYLDAN, MUHITDAN)
chuqur_royxat = chuqur_birlashtir(STANDART, FAYLDAN, MUHITDAN, royxat_qoshish=True)

print(f"\n  Sayoz ({{**a, **b}}):")
for k, v in sayoz.items():
    print(f"    {k:<10} {v}")

print(f"\n  Chuqur:")
for k, v in chuqur.items():
    print(f"    {k:<10} {v}")

print(f"\n  Chuqur + ro'yxatlarni qo'shish:")
print(f"    hostlar: {chuqur_royxat['hostlar']}")

print(f"""
  ⭐ Sayoz: baza to'liq almashtirildi — 'ssl' va 'host' yo'qoldi
     Chuqur: barcha kalitlar saqlandi
""")


print("\n=== 6. Nusxa olish tezligi ===")

KATTA = {
    f"kalit{i}": {
        "sonlar": list(range(20)),
        "ichki": {"a": i, "b": [{"x": j} for j in range(5)]},
    }
    for i in range(200)
}

USULLAR = []

boshlandi = time.perf_counter()
n1 = KATTA.copy()
USULLAR.append(("d.copy() — SAYOZ ⚠️", time.perf_counter() - boshlandi))

boshlandi = time.perf_counter()
n2 = copy.deepcopy(KATTA)
USULLAR.append(("copy.deepcopy()", time.perf_counter() - boshlandi))

boshlandi = time.perf_counter()
n3 = json.loads(json.dumps(KATTA))
USULLAR.append(("json roundtrip", time.perf_counter() - boshlandi))

eng_tez = min(v for _, v in USULLAR)
print(f"  200 ta ichma-ich yozuv:\n")
print(f"  {'Usul':<26} {'Vaqt':>10} {'Nisbat':>9}")
print("  " + "─" * 48)
for nom, vaqt in USULLAR:
    print(f"  {nom:<26} {vaqt * 1000:>7.2f} ms {vaqt / eng_tez:>8.1f}x")

print(f"\n  Nusxalar mustaqilmi:")
for nom, n in [("copy()", n1), ("deepcopy()", n2), ("json", n3)]:
    n["kalit0"]["sonlar"].append(999)
    mustaqil = 999 not in KATTA["kalit0"]["sonlar"]
    print(f"    {nom:<12} {'✅ mustaqil' if mustaqil else '❌ ASL O`ZGARDI'}")
    if not mustaqil:
        KATTA["kalit0"]["sonlar"].remove(999)

print("""
  ⚠️ json roundtrip tezroq, LEKIN:
     • Faqat JSON turlari (dict, list, str, int, float, bool, None)
     • tuple → list, set → ❌, datetime → ❌
     • Kalitlar satrga aylanadi
""")

Natijaning muhim qismi:

text
=== 3. Bo'sh qiymatlarni olib tashlash ===
  Asl kalitlar:  9
  Toza kalitlar: 6

  Olib tashlangan:
    lastLogin              = None
    emptyList              = []
    emptyDict              = {}

  ⚠️ 0 va False saqlanadi:
    {'nol': 0, 'yolgon': False, 'bosh_satr': '', 'yoq': None, 'bosh_royxat': []}
    → {'nol': 0, 'yolgon': False}

=== 6. Nusxa olish tezligi ===
  Usul                             Vaqt    Nisbat
  ────────────────────────────────────────────────
  d.copy() — SAYOZ ⚠️              0.01 ms      1.0x
  copy.deepcopy()                 84.21 ms   8421.0x
  json roundtrip                  12.42 ms   1242.0x

  Nusxalar mustaqilmi:
    copy()       ❌ ASL O`ZGARDI
    deepcopy()   ✅ mustaqil
    json         ✅ mustaqil

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

Misol 4 — Amaliy: JSON validator va tahlilchi

python
"""Ichma-ich tuzilmalar bilan to'liq vosita."""

import json
from typing import Any
from collections import Counter, defaultdict

print("=== JSON tahlilchi va validator ===\n")

JAVOB = {
    "status": "ok",
    "data": {
        "users": [
            {
                "id": 1,
                "name": "Aziz",
                "email": "aziz@mail.uz",
                "roles": ["admin", "user"],
                "profile": {"age": 30, "city": "Toshkent", "bio": None},
            },
            {
                "id": 2,
                "name": "Bobur",
                "email": "invalid-email",
                "roles": [],
                "profile": {"age": "25", "city": "", "bio": "Dasturchi"},
            },
            {
                "id": 3,
                "name": "",
                "email": "aziza@mail.uz",
                "roles": ["user"],
                "profile": {"age": -5, "city": "Samarqand"},
            },
        ],
        "total": 3,
        "page": 1,
    },
    "meta": {"version": "2.1", "timestamp": 1725782400, "cached": False},
}


class JSONTahlilchi:
    def __init__(self, malumot):
        self.malumot = malumot

    def barglar(self, malumot=None, yol=(), korilgan=None):
        if malumot is None:
            malumot = self.malumot
        if korilgan is None:
            korilgan = frozenset()

        if isinstance(malumot, (dict, list)):
            if id(malumot) in korilgan:
                yield yol, "<AYLANMA>"
                return
            korilgan = korilgan | {id(malumot)}

        if isinstance(malumot, dict):
            for k, v in malumot.items():
                yield from self.barglar(v, yol + (k,), korilgan)
        elif isinstance(malumot, list):
            for i, v in enumerate(malumot):
                yield from self.barglar(v, yol + (i,), korilgan)
        else:
            yield yol, malumot

    @property
    def chuqurlik(self) -> int:
        return max((len(yol) for yol, _ in self.barglar()), default=0)

    def statistika(self) -> dict:
        turlar = Counter()
        barglar_soni = 0
        for _, qiymat in self.barglar():
            turlar[type(qiymat).__name__] += 1
            barglar_soni += 1

        tugunlar = Counter()

        def sana(m):
            tugunlar[type(m).__name__] += 1
            if isinstance(m, dict):
                for v in m.values():
                    sana(v)
            elif isinstance(m, list):
                for v in m:
                    sana(v)

        sana(self.malumot)

        return {
            "chuqurlik": self.chuqurlik,
            "barglar": barglar_soni,
            "barg_turlari": dict(turlar.most_common()),
            "tugunlar": dict(tugunlar.most_common()),
        }

    def qidir(self, shart) -> list[tuple]:
        """Shartga mos barglarni topadi."""
        return [(yol, q) for yol, q in self.barglar() if shart(yol, q)]

    def kalit_yollari(self, kalit: str) -> list[tuple]:
        """Berilgan kalit qayerlarda uchraydi."""
        return [yol for yol, _ in self.barglar() if kalit in yol]

    _BOSH = object()                            # ⭐ "berilmagan" belgisi

    def sxema(self, malumot=_BOSH, daraja=0) -> str:
        """Tuzilma sxemasi.

        ⚠️ malumot=None sukuti bilan yozilsa, JSON dagi null (None)
           qiymatda rekursiya ILDIZDAN qayta boshlanadi → RecursionError.
           None haqiqiy qiymat bo'lishi mumkin bo'lsa — alohida belgi obyekt.
        """
        if malumot is self._BOSH:
            malumot = self.malumot
        bo = "  " * daraja

        if isinstance(malumot, dict):
            qatorlar = [f"{{"]
            for k, v in malumot.items():
                ichki = self.sxema(v, daraja + 1)
                qatorlar.append(f"{bo}  {k}: {ichki}")
            qatorlar.append(f"{bo}}}")
            return "\n".join(qatorlar)
        if isinstance(malumot, list):
            if not malumot:
                return "[]"
            ichki = self.sxema(malumot[0], daraja)
            return f"[{ichki}] × {len(malumot)}"
        return type(malumot).__name__


t = JSONTahlilchi(JAVOB)

print("=== 1. Statistika ===\n")
stat = t.statistika()
print(f"  Maksimal chuqurlik:  {stat['chuqurlik']}")
print(f"  Barglar soni:        {stat['barglar']}\n")

print(f"  Barg turlari:")
for tur, soni in stat["barg_turlari"].items():
    print(f"    {tur:<12} {'█' * soni} {soni}")

print(f"\n  Barcha tugunlar:")
for tur, soni in stat["tugunlar"].items():
    print(f"    {tur:<12} {soni}")


print("\n\n=== 2. Sxema ===\n")
for qator in t.sxema().splitlines():
    print(f"  {qator}")


print("\n\n=== 3. Qidiruv ===\n")

QIDIRUVLAR = [
    ("None qiymatlar",      lambda y, q: q is None),
    ("Bo'sh satrlar",       lambda y, q: q == ""),
    ("Manfiy sonlar",       lambda y, q: isinstance(q, int) and not isinstance(q, bool) and q < 0),
    ("Satr sonlar",         lambda y, q: isinstance(q, str) and q.lstrip("-").isdigit()),
    ("'email' yo'lida",     lambda y, q: "email" in y),
]

for nom, shart in QIDIRUVLAR:
    natijalar = t.qidir(shart)
    print(f"  {nom} ({len(natijalar)}):")
    for yol, qiymat in natijalar:
        print(f"    {'.'.join(map(str, yol)):<40} = {qiymat!r}")
    print()


print("=== 4. Validatsiya ===\n")

SXEMA = {
    "data.users[].id":              ("int", lambda x: x > 0),
    "data.users[].name":            ("str", lambda x: len(x.strip()) > 0),
    "data.users[].email":           ("str", lambda x: "@" in x and "." in x.split("@")[-1]),
    "data.users[].roles":           ("list", lambda x: len(x) > 0),
    "data.users[].profile.age":     ("int", lambda x: 0 < x < 150),
    "data.users[].profile.city":    ("str", lambda x: len(x.strip()) > 0),
    "data.total":                   ("int", lambda x: x >= 0),
    "meta.version":                 ("str", lambda x: bool(x)),
}


def yol_matni(yol: tuple) -> str:
    """(  'data','users',0,'id' ) → 'data.users[].id'"""
    qismlar = []
    for q in yol:
        if isinstance(q, int):
            if qismlar:
                qismlar[-1] += "[]"
        else:
            qismlar.append(str(q))
    return ".".join(qismlar)


xatolar = []
tekshirilgan = 0

for yol, qiymat in t.barglar():
    naqsh = yol_matni(yol)
    if naqsh not in SXEMA:
        continue
    tekshirilgan += 1
    kutilgan_tur, tekshiruv = SXEMA[naqsh]
    haqiqiy_tur = type(qiymat).__name__

    if haqiqiy_tur != kutilgan_tur:
        xatolar.append((yol, f"tur: {kutilgan_tur} kutildi, {haqiqiy_tur} keldi", qiymat))
    else:
        try:
            if not tekshiruv(qiymat):
                xatolar.append((yol, "qiymat tekshiruvidan o'tmadi", qiymat))
        except Exception as x:
            xatolar.append((yol, f"tekshiruv xatosi: {x}", qiymat))

print(f"  {tekshirilgan} maydon tekshirildi, {len(xatolar)} xato:\n")
for yol, sabab, qiymat in xatolar:
    print(f"    ❌ {'.'.join(map(str, yol))}")
    print(f"       {sabab}")
    print(f"       qiymat: {qiymat!r}\n")

if not xatolar:
    print(f"    ✅ Barcha maydon to'g'ri")


print("=== 5. Tozalash ===\n")


def tozala(malumot):
    """Bo'sh qiymatlarni olib tashlaydi, satrlarni tozalaydi."""
    if isinstance(malumot, dict):
        natija = {}
        for k, v in malumot.items():
            tozalangan = tozala(v)
            if tozalangan is not None and tozalangan != "" and tozalangan != []:
                natija[k] = tozalangan
        return natija
    if isinstance(malumot, list):
        natija = [tozala(v) for v in malumot]
        return [v for v in natija
                if v is not None and v != "" and v != [] and v != {}]
    if isinstance(malumot, str):
        return malumot.strip()
    return malumot


toza = tozala(JAVOB)

asl_barglar = len(list(t.barglar()))
toza_barglar = len(list(JSONTahlilchi(toza).barglar()))

print(f"  Barglar: {asl_barglar} → {toza_barglar} "
      f"({asl_barglar - toza_barglar} olib tashlandi)\n")

print(f"  Foydalanuvchilar:")
for asl, yangi in zip(JAVOB["data"]["users"], toza["data"]["users"]):
    yoqolgan = set(asl) - set(yangi)
    profil_yoqolgan = set(asl["profile"]) - set(yangi.get("profile", {}))
    print(f"    id={asl['id']}:")
    if yoqolgan:
        print(f"      olib tashlandi: {sorted(yoqolgan)}")
    if profil_yoqolgan:
        print(f"      profile dan:    {sorted(profil_yoqolgan)}")
    if not yoqolgan and not profil_yoqolgan:
        print(f"      (o'zgarishsiz)")


print("\n\n=== 6. Yakuniy JSON ===\n")
chiqish = json.dumps(toza, indent=2, ensure_ascii=False, sort_keys=True)
for qator in chiqish.splitlines()[:22]:
    print(f"  {qator}")
print(f"  ...")
print(f"\n  Hajm: {len(json.dumps(JAVOB))} → {len(json.dumps(toza))} bayt")

Natijaning muhim qismi:

text
=== 1. Statistika ===

  Maksimal chuqurlik:  5
  Barglar soni:        26

  Barg turlari:
    str          █████████████████ 17
    int          ████████ 8
    NoneType     1
    bool         █ 1

=== 3. Qidiruv ===

  Manfiy sonlar (1):
    data.users.2.profile.age                 = -5

  Satr sonlar (1):
    data.users.1.profile.age                 = '25'

=== 4. Validatsiya ===

  17 maydon tekshirildi, 5 xato:

    ❌ data.users.1.email
       qiymat tekshiruvidan o'tmadi
       qiymat: 'invalid-email'

    ❌ data.users.1.profile.age
       tur: int kutildi, str keldi
       qiymat: '25'

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


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

Noto'g'ri fikr To'g'risi
"[[0]*3]*3 — 3×3 matritsa" Bitta ro'yxatga 3 havola
"d.copy() ichma-ichni ham nusxalaydi" Sayoz — ichki obyektlar umumiy
"get zanjiri xavfsiz" Ro'yxat indeksi bilan baribir IndexError
"Rekursiya har doim ishlaydi" 1000 daraja chegara. Chuqur uchun iterativ
"Satr — konteyner, uni ham aylanish kerak" Barg sifatida qarang
"Tekislash teskari qaytariladi" Ro'yxatlar lug'atga aylanadi
"update chuqur birlashtiradi" Sayoz — ichki lug'at almashtiriladi
"json roundtrip deepcopy o'rnini bosadi" Faqat JSON turlari
"Aylanma havola bo'lmaydi" Bo'ladi — id() bilan himoya kerak

6. Keng tarqalgan xatolar va yechimlari

1. * n bilan matritsa

python
m = [[0] * 3] * 3                       # ❌
m = [[0] * 3 for _ in range(3)]         # ✅

2. Sayoz nusxa

python
n = d.copy()                            # ⚠️ ichki umumiy
n = copy.deepcopy(d)                    # ✅

3. Himoyasiz murojaat

python
x = d["a"]["b"][0]["c"]                 # ❌
x = chuqur_ol(d, "a", "b", 0, "c")      # ✅

4. Satrni konteyner deb qarash

python
if isinstance(x, Sequence):             # ⚠️ satr ham Sequence
if isinstance(x, (list, tuple)):        # ✅

5. Aylanma havolada rekursiya

python
def yur(d):
    for v in d.values(): yur(v)         # ❌ RecursionError

def yur(d, korilgan=frozenset()):       # ✅
    if id(d) in korilgan: return

6. Chuqur tuzilmada rekursiya

python
def yur(d): ...                         # ⚠️ 1000 daraja chegara
def yur_iterativ(d):                    # ✅ stek bilan
    stek = [d]

7. update bilan chuqur birlashtirish

python
d1.update(d2)                           # ⚠️ ichki lug'at almashtiriladi
chuqur_birlashtir(d1, d2)               # ✅

8. Tekislashda ajratgich to'qnashuvi

python
tekisla({"a.b": {"c": 1}})              # ⚠️ "a.b.c" — noaniq
tekisla(d, ajratgich="\x00")            # ✅ kalitda bo'lmaydigan belgi

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 6.1, 6.10-darslar (o'tilgan): [[0]*3]*3 va fromkeys tuzoqlari
  • 6.18-dars: copy va deepcopy — batafsil
  • 6.15, 6.16-darslar: generatorlar bilan transformatsiya
  • 7, 10-qismlar: rekursiv funksiyalar, yield from
  • 9-qism: xatolarni ushlash zanjiri
  • 16-qism: JSON, fayllar bilan ishlash
  • 13-qism: pydantic — sxema validatsiyasi
  • API bilan ishlash: har qanday REST javob ichma-ich

8. Eng yaxshi amaliyotlar

  1. Xavfsiz o'qish uchun yordamchi funksiya yozing. Bir marta, hamma joyda.

  2. * n faqat o'zgarmas elementlar uchun. Ichki o'lchamda — xavfsiz, tashqisida — yo'q.

  3. Nusxa kerak bo'lsa deepcopy. copy() ichma-ich uchun deyarli har doim noto'g'ri.

  4. Rekursiyada str/bytes ni alohida. Ular iteratsiya qilinadi, lekin barg.

  5. Aylanma havoladan himoyalaning. id() to'plami bilan.

  6. Chuqur tuzilma uchun iterativ. 1000 daraja — real chegara.

  7. Chuqur birlashtirish uchun rekursiv funksiya. update va | sayoz.

  8. Murakkab so'rovlar uchun glom yoki jmespath. Oddiy holatda o'z funksiyangiz.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
1.  m = [[0]*2]*2; m[0][0] = 1; print(m)
2.  m = [[0]*2 for _ in range(2)]; m[0][0] = 1; print(m)
3.  d = {"a": {"b": 1}}; n = d.copy(); n["a"]["b"] = 2; print(d)
4.  d = {"a": 1}; n = d.copy(); n["a"] = 2; print(d)
5.  print([[0]*2][0] is [[0]*2][0])
6.  d = dict.fromkeys("ab", {}); d["a"]["x"] = 1; print(d)
7.  from itertools import chain
    print(list(chain.from_iterable([[1,[2]],[3]])))
8.  print(isinstance("abc", (list, tuple)))
9.  d = {}; d["x"] = d; print(len(d))
10. import copy; d = {"a":[1]}; n = copy.deepcopy(d); n["a"].append(2); print(d)
Javoblar
  1. [[1, 0], [1, 0]] — bitta ro'yxat
  2. [[1, 0], [0, 0]]
  3. {'a': {'b': 2}} — sayoz nusxa
  4. {'a': 1} — yuqori daraja alohida
  5. False — turli obyektlar
  6. {'a': {'x': 1}, 'b': {'x': 1}} — bitta lug'at
  7. [1, [2], 3] — faqat bir daraja
  8. False — satr list/tuple emas
  9. 1 — aylanma havola muammo emas
  10. {'a': [1]} chuqur nusxa

Vazifa 2: Xatolarni tuzating

python
1.  taxta = [["."]*8]*8
2.  nusxa = konfiguratsiya.copy()
    nusxa["baza"]["host"] = "yangi"
3.  x = javob["data"]["users"][0]["name"]
4.  def yur(d):
        for v in d.values(): yur(v)
5.  d1.update(d2)     # ichki lug'atlar saqlanishi kerak
6.  def tekisla(x):
        if isinstance(x, Sequence): ...
7.  d = dict.fromkeys(kalitlar, {})
8.  natija = json.loads(json.dumps(malumot))   # datetime bor
Javoblar
python
1.  taxta = [["."]*8 for _ in range(8)]
2.  nusxa = copy.deepcopy(konfiguratsiya)
3.  x = chuqur_ol(javob, "data", "users", 0, "name")
4.  def yur(d, korilgan=frozenset()):
        if id(d) in korilgan: return
        korilgan = korilgan | {id(d)}
5.  d1 = chuqur_birlashtir(d1, d2)
6.  if isinstance(x, (list, tuple)):
7.  d = {k: {} for k in kalitlar}
8.  natija = copy.deepcopy(malumot)    # yoki maxsus JSON encoder

Vazifa 3: Yo'l kutubxonasi

Funksiyalar yozing:

  1. ol(m, yol, sukut=None) — o'qish ("a.b[0].c" sintaksisi)
  2. qoy(m, yol, qiymat) — yozish, oraliqlarni yaratish
  3. ochir(m, yol) — o'chirish
  4. bormi(m, yol) — mavjudligini tekshirish
  5. yollar(m) — barcha yo'llar ro'yxati
  6. Kalitda nuqta bo'lsa qochirish (a\\.b)

Vazifa 4: Diff vositasi

Ichma-ich tuzilmalar uchun:

python
def diff(eski, yangi) -> list[tuple[str, str, Any, Any]]:
    """[(yol, 'qoshildi'|'ochirildi'|'ozgardi', eski, yangi)]"""
  1. Rekursiv taqqoslash
  2. Ro'yxatlar uchun element diff
  3. Chiroyli chiqarish (+, -, ~ ranglar bilan)
  4. JSON Patch (RFC 6902) eksporti
  5. Patch qo'llash (patch(malumot, ozgarishlar))

Vazifa 5: Sxema validatori

Sinf yozing:

  1. Sxema tili: tur, majburiylik, oraliq, naqsh
  2. Ichma-ich sxemalar
  3. Ro'yxat elementlari uchun sxema
  4. Xatolarni yo'l bilan ko'rsatish
  5. Avtomatik tur aylantirish opsiyasi
  6. Sxemani namunadan generatsiya qilish

Vazifa 6: Ma'lumot quvuri (pipeline)

Zanjirlanadigan transformatsiyalar:

python
Quvur(malumot).tozala().kalitlar(snake).turlar().filtrla(shart).natija()
  1. tozala() — strip, bo'shlarni olib tashlash
  2. kalitlar(f) — kalitlarni o'zgartirish
  3. turlar() — avtomatik tur tiklash
  4. filtrla(shart) — rekursiv filtr
  5. xarita(f) — barg qiymatlarga funksiya
  6. tekisla() / tikla()
  7. Har qadam yangi obyekt qaytarsin (o'zgarmas quvur)

Vazifa 7: O'ylash

Nega Python d["a"]["b"]["c"] uchun "xavfsiz murojaat" operatori bermagan (JavaScript dagi d?.a?.b?.c kabi)?

Javob

Uch sabab.

1. None va "yo'q kalit" — turli holatlar.

JavaScript da ?. faqat null/undefined ni tekshiradi:

javascript
d?.a?.b         // d yoki d.a null/undefined bo'lsa → undefined

Pythonda esa uch xil "yo'qlik" bor:

python
d["a"]          # KeyError — kalit yo'q
d[0]            # IndexError — indeks chegaradan tashqarida
d.a             # AttributeError — atribut yo'q
None["a"]       # TypeError — obyekt murojaat qilinmaydi

Bitta operator qaysi birini yutishi kerak? Hammasini yutish — juda ko'p xatoni yashirish demak:

python
# Tasavvur qiling: d?["a"]?["b"]
d?["nomi_notogri_yozilgan"]?["b"]       # jimgina None — xato topilmaydi

2. "Xatolarni yashirmang" prinsipi.

Zen: "Errors should never pass silently."

KeyError — foydali xato. U kalit nomi noto'g'ri yozilganini yoki API o'zgarganini darhol ko'rsatadi. ?. bo'lsa, xato o'nlab qatordan keyin None bilan ishlashda chiqadi — va sababni topish qiyin bo'ladi.

3. Zanjir kamdan-kam kerak, va yechim bor.

Amalda uzun zanjir kerak bo'lgan holat ikkitasi:

  • API javobi — bir marta parse qilinadi, keyin tipli obyektga aylantiriladi
  • Konfiguratsiya — bir marta o'qiladi

Ikkalasi uchun ham yaxshiroq yechim bor:

python
# ✅ pydantic — sxema bilan
class Foydalanuvchi(BaseModel):
    ism: str
    manzil: Manzil | None = None

f = Foydalanuvchi(**javob)
f.manzil.shahar if f.manzil else None   # tipli, IDE yordam beradi

# ✅ dataclass + parse funksiyasi
# ✅ chuqur_ol() yordamchi funksiyasi

PEP taklif qilinganmi?

Ha — PEP 505 (None-aware operators, 2015):

python
a ?? b          # a is None bo'lsa b
a?.b            # xavfsiz atribut
a?[b]           # xavfsiz indeks
a ??= b         # None bo'lsa o'zlashtirish

Guido "deferred" (kechiktirilgan) holatiga qo'ydi. Sabablar:

  1. Sintaksis og'ir ko'rinadi:

    python
    x = a?.b?.c?.d ?? sukut

    Pythonchilar buni "Perl kabi" deb topdi.

  2. None — Pythonda kamroq markaziy. JavaScript da undefined har joyda (yo'q maydon → undefined). Pythonda yo'q kalit → xato, va bu ataylab.

  3. Muqobil yechimlar mavjud (yuqorida).

  4. Muammoning ko'lami kichik. Ko'p kod uzun zanjir ishlatmaydi.

Hozirgi holat: PEP 505 hali ham "deferred". Vaqti-vaqti bilan muhokama qayta boshlanadi, lekin konsensus yo'q.

Amaliy tavsiya:

python
# Bir-ikki daraja — to'g'ridan-to'g'ri
d.get("a", {}).get("b")

# Uch va undan ko'p — yordamchi funksiya
chuqur_ol(d, "a", "b", "c", "d")

# Murakkab so'rov — glom/jmespath
glom(d, "a.b.c.d", default=None)

# Tuzilma ma'lum — sxema
Foydalanuvchi(**d)

Xulosa: Python "sintaksis qo'shish" o'rniga "kutubxona bilan hal qilish" ni tanladi. Bu — tilning umumiy falsafasi: yadro kichik qolsin, ehtiyojlar kutubxona bilan qoplansin.

Nimani mustahkamlaydi: 2.1, 2.2, 2.4, 2.6-bo'limlar.


Xulosa

Bu darsda ichma-ich tuzilmalar bilan ishlashni o'rgandik.

Eng muhim uch fikr:

  1. Xavfsiz murojaat uchun yordamchi funksiya yozing. chuqur_ol(m, *yol, sukut) — try/except ni bir joyga yig'adi va butun loyihada ishlatiladi. get zanjiri ro'yxat indekslarida baribir buziladi.

  2. copy() ichma-ich uchun yaramaydi. Sayoz nusxa ichki obyektlarni umumiy qoldiradi. deepcopy — to'g'ri javob, json roundtrip — tezroq, lekin faqat JSON turlari uchun.

  3. Rekursiyada uch xavf bor. str/bytes ni barg deb qarang, aylanma havoladan id() to'plami bilan himoyalaning, chuqur tuzilmalar uchun stek bilan iterativ yechim yozing.

Keyingi darsda ro'yxat generatorlari ni ko'ramiz: [x for x in ...] sintaksisi, sikl bilan solishtirish, ichma-ich generatorlar va qachon ular o'qilishni yomonlashtiradi.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
6.14-dars: Ichma-ich tuzilmalar — IlmHamroh