IlmHamroh
Python kursi/Malumot tuzilmalari9/18-dars33 daqiqa
Mundarija (21)

6.9-dars: frozenset — o'zgarmas to'plam

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


1. Kirish va motivatsiya

To'plam elementlari hashlanishi kerak (6.7-dars). Lekin to'plamning o'zi hashlanmaydi:

python
{1, 2}                          # ✅ to'plam
{{1, 2}}                        # ❌ TypeError: unhashable type: 'set'
{{1, 2}: "qiymat"}              # ❌ TypeError

Ya'ni to'plamlar to'plami yoki to'plam kaliti yaratib bo'lmaydi. Bu — real muammo:

python
# Guruhlarni saqlash
guruhlar = {
    {"Aziz", "Bobur"},          # ❌
    {"Aziza", "Dilnoza"},
}

# Kesh: qaysi teglar kombinatsiyasi qaysi natijani beradi
kesh = {
    {"python", "veb"}: [...],   # ❌
}

Yechim — frozenset:

python
guruhlar = {
    frozenset({"Aziz", "Bobur"}),       # ✅
    frozenset({"Aziza", "Dilnoza"}),
}

kesh = {
    frozenset({"python", "veb"}): [...],    # ✅
}

frozenset — set ning o'zgarmas versiyasi. tuple list ga qanday munosabatda bo'lsa, frozenset set ga shunday munosabatda.

Bu darsda:

  • frozenset yaratish va farqlari
  • Nega hashlanadi va hash qanday hisoblanadi
  • Barcha amallar (set bilan bir xil, -update metodlarisiz)
  • set va frozenset aralashuvi — qaysi tur qaytadi
  • Amaliy naqshlar: kesh kaliti, guruhlar, o'zgarmas konfiguratsiya
  • frozenset vs tuple — qachon qaysi biri

2. Nazariya — chuqur tushuntirish

2.1. Yaratish

Faqat konstruktor bilan — maxsus sintaksis yo'q:

python
frozenset()                     # bo'sh
frozenset([1, 2, 3])            # ro'yxatdan
frozenset({1, 2, 3})            # to'plamdan
frozenset("abc")                # {'a', 'b', 'c'}
frozenset(range(3))             # {0, 1, 2}
frozenset({"a": 1})             # {'a'} — kalitlar
frozenset(x for x in [1, 2])    # generatordan

Yo'q:

python
f{1, 2}                         # ❌ bunday sintaksis yo'q
frozen{1, 2}                    # ❌

tuple da (1, 2) bor, frozenset da esa hech narsa yo'q. Sabab: frozenset ancha kam ishlatiladi, alohida sintaksis berish tilni murakkablashtirardi.

repr shakli:

python
print(frozenset([1, 2, 3]))     # frozenset({1, 2, 3})
print(frozenset())              # frozenset()

Bo'sh frozenset — singleton emas (endi):

python
a = frozenset()
b = frozenset([])
print(a is b)                   # False — Python 3.14 da

Eski CPython versiyalarida bo'sh frozenset yagona obyekt edi va bu True qaytarardi; hozir unday emas. Bo'sh tuple esa hamon yagona obyekt. Bu — amalga oshiruv tafsiloti: to'plamlarni doim == bilan solishtiring.

2.2. Nega hashlanadi

frozenset o'zgarmas → hash barqaror → hashlanadi.

python
f = frozenset([1, 2, 3])

hash(f)                         # ✅ ishlaydi
{f}                             # ✅ to'plam ichida
{f: "qiymat"}                   # ✅ lug'at kaliti

Hash tartibga bog'liq emas:

python
hash(frozenset([1, 2, 3])) == hash(frozenset([3, 2, 1]))    # True
frozenset([1, 2, 3]) == frozenset([3, 2, 1])                # True

Bu — tuple dan asosiy farq:

python
(1, 2, 3) == (3, 2, 1)          # False — tartib muhim
hash((1,2,3)) == hash((3,2,1))  # False

Qanday hisoblanadi:

CPython frozenset hash'ini elementlar hash'laridan tartibdan mustaqil funksiya bilan hisoblaydi (XOR va aralashtirish asosida):

python
# Soddalashtirilgan g'oya:
h = 0
for x in elementlar:
    h ^= aralashtir(hash(x))    # XOR — kommutativ va assotsiativ

XOR kommutativ bo'lgani uchun tartib ahamiyatsiz. Amalda CPython murakkabroq aralashtirish ishlatadi — to'qnashuvlarni kamaytirish uchun.

Elementlar hashlanishi kerak:

python
frozenset([1, 2])               # ✅
frozenset([[1, 2]])             # ❌ TypeError: unhashable type: 'list'
frozenset([{1, 2}])             # ❌ set hashlanmaydi
frozenset([frozenset([1, 2])])  # ✅ ichma-ich frozenset

Hash keshlanadi:

python
f = frozenset(range(1_000_000))
hash(f)                         # birinchi marta — O(n)
hash(f)                         # keyin — O(1), keshdan

CPython hash'ni obyekt ichida saqlaydi. Shuning uchun katta frozenset ni lug'at kaliti sifatida ishlatish samarali.

2.3. Nima bor va nima yo'q

Ishlaydi (o'zgartirmaydigan hamma narsa):

python
f = frozenset([1, 2, 3])
g = frozenset([3, 4])

len(f)                          # 3
2 in f                          # True
for x in f: ...                 # iteratsiya
f | g, f & g, f - g, f ^ g      # amallar
f <= g, f < g, f >= g, f > g    # solishtirish
f.isdisjoint(g)
f.union(g), f.intersection(g)
f.difference(g), f.symmetric_difference(g)
f.issubset(g), f.issuperset(g)
f.copy()
min(f), max(f), sum(f), sorted(f)
hash(f)                         # ⭐ set da yo'q

Yo'q (barcha o'zgartiruvchi metodlar):

python
f.add(4)                        # ❌ AttributeError
f.remove(1)                     # ❌
f.discard(1)                    # ❌
f.pop()                         # ❌
f.clear()                       # ❌
f.update(g)                     # ❌
f.intersection_update(g)        # ❌
f |= g                          # ❌ TypeError

Metodlar solishtirilishi:

python
set_metodlari = {m for m in dir(set) if not m.startswith("_")}
frozenset_metodlari = {m for m in dir(frozenset) if not m.startswith("_")}

print(sorted(set_metodlari - frozenset_metodlari))
# ['add', 'clear', 'difference_update', 'discard',
#  'intersection_update', 'pop', 'remove',
#  'symmetric_difference_update', 'update']

print(sorted(frozenset_metodlari - set_metodlari))
# []  ← frozenset da qo'shimcha metod YO'Q

copy() — frozenset uchun o'zini qaytaradi:

python
f = frozenset([1, 2])
f.copy() is f                   # True — nusxa kerak emas, o'zgarmas

tuple da ham shunday: o'zgarmas obyektni nusxalash ma'nosiz.

2.4. set va frozenset aralashuvi

Bu — eng nozik mavzu. Qaysi tur qaytadi?

Qoida: CHAP operandning turi.

python
s = {1, 2}
f = frozenset([2, 3])

type(s | f)                     # set        ← chap set
type(f | s)                     # frozenset  ← chap frozenset

type(s & f)                     # set
type(f & s)                     # frozenset

type(s - f)                     # set
type(f - s)                     # frozenset

Metodlar bilan ham shunday:

python
type(s.union(f))                # set
type(f.union(s))                # frozenset

Solishtirish tur bilan bog'liq emas:

python
{1, 2} == frozenset([1, 2])                 # True  ⭐
{1, 2} <= frozenset([1, 2, 3])              # True
hash({1,2}) == hash(frozenset([1,2]))       # ❌ set hashlanmaydi

set va frozenset teng bo'lishi mumkin — bu list va tuple dan farqli:

python
[1, 2] == (1, 2)                # False
{1, 2} == frozenset([1, 2])     # True   ⭐

Sabab: ikkalasi ham collections.abc.Set ABC ni amalga oshiradi va tenglik faqat elementlarga qaraydi.

Amaliy oqibat — lug'atda:

python
d = {frozenset([1, 2]): "qiymat"}

d[frozenset([1, 2])]            # ✅ "qiymat"
d[{1, 2}]                       # ❌ TypeError: unhashable type: 'set'

Teng bo'lsa ham, set hashlanmagani uchun kalit sifatida ishlamaydi.

Joyida amallar:

python
s = {1, 2}
s |= frozenset([3])             # ✅ s — set, o'zgaruvchan
print(type(s))                  # set

f = frozenset([1, 2])
f |= {3}                        # ⚠️ ishlaydi, lekin YANGI obyekt
print(type(f))                  # frozenset

Oxirgi holat tuple dagi += bilan bir xil: f |= {3} aslida f = f | {3}.

2.5. Xotira va tezlik

python
import sys

sys.getsizeof(set([1, 2, 3]))           # 216
sys.getsizeof(frozenset([1, 2, 3]))     # 216

Xotira deyarli bir xil — ichki tuzilma bir xil hash jadvali.

Tezlik:

Amal set vs frozenset
Yaratish Bir xil
in Bir xil
Amallar Bir xil
hash() Faqat frozenset

frozenset set dan tezroq emas. tuple list dan yaratishda tezroq edi (konstanta optimallashtirish), lekin frozenset uchun bunday optimallashtirish yo'q — chunki u literal sintaksisga ega emas.

python
# Konstanta frozenset kompilyatsiya paytida yaratilmaydi
%timeit frozenset([1, 2, 3])    # ~150 ns
%timeit {1, 2, 3}               # ~50 ns

Bitta istisno: in tekshiruvida CPython optimallashtirishi bor:

python
if x in {1, 2, 3}:              # konstanta to'plam → frozenset ga aylantiriladi!
    ...
python
import dis
dis.dis("x in {1, 2, 3}")
#   LOAD_CONST  (frozenset({1, 2, 3}))    ← frozenset!
#   CONTAINS_OP

Peephole optimizator konstanta set ni frozenset ga aylantiradi va uni konstanta sifatida saqlaydi. Bu — haqiqiy tezlik yutug'i:

python
# ✅ Har chaqiruvda to'plam qayta yaratilmaydi
def tekshir(x):
    return x in {"a", "b", "c"}

# Aynan shunday tez:
RUXSAT = frozenset({"a", "b", "c"})
def tekshir(x):
    return x in RUXSAT

Faqat konstantalar uchun:

python
x in {1, 2, 3}                  # ✅ optimallashtiriladi
x in {a, b, c}                  # ❌ o'zgaruvchilar — har safar quriladi

2.6. frozenset vs tuple

Ikkalasi ham o'zgarmas va hashlanadi. Farqi:

tuple frozenset
Tartib Muhim Yo'q
Takrorlar
Indeks t[0]
in O(n) O(1)
To'plam amallari
Xotira (3 element) 64 B 216 B
Yaratish Tezroq Sekinroq
Hash tartibga bog'liq

Qachon tuple:

python
# Tartib muhim
koordinata = (10, 20)
kesh[(foydalanuvchi_id, sahifa, versiya)] = natija

# Kam element, tartib bilan
rgb = (255, 0, 0)

Qachon frozenset:

python
# Tartib muhim EMAS, tarkib muhim
teglar = frozenset({"python", "veb"})
# {"python","veb"} va {"veb","python"} — BIR XIL kalit

# Ko'p element + `in` kerak
RUXSAT_ETILGAN = frozenset({...100 element...})

# To'plam amallari kerak
guruh_a & guruh_b

Asosiy farq — kanonik shakl:

python
# tuple bilan
kesh[("python", "veb")] = 1
kesh[("veb", "python")]         # ❌ KeyError — boshqa kalit

# frozenset bilan
kesh[frozenset(["python", "veb"])] = 1
kesh[frozenset(["veb", "python"])]      # ✅ 1 — bir xil kalit

Tartibsiz to'plamni kalit sifatida ishlatish kerak bo'lsa, frozenset — yagona to'g'ri tanlov. (Muqobil: tuple(sorted(...)) — lekin u sekinroq va elementlar solishtirilishi kerak.)

2.7. Amaliy naqshlar

1. To'plamlar to'plami:

python
guruhlar = {
    frozenset({"Aziz", "Bobur"}),
    frozenset({"Aziza", "Dilnoza"}),
    frozenset({"Bobur", "Aziz"}),       # ⚠️ birinchisi bilan bir xil!
}
print(len(guruhlar))                    # 2 — takror yo'qoldi

2. Tartibsiz kesh kaliti:

python
from functools import lru_cache


def kesh_kaliti(**kwargs) -> frozenset:
    return frozenset(kwargs.items())


KESH = {}


def qidiruv(**filtrlar):
    kalit = frozenset(filtrlar.items())
    if kalit in KESH:
        return KESH[kalit]
    natija = ogir_hisob(**filtrlar)
    KESH[kalit] = natija
    return natija


qidiruv(shahar="Toshkent", yil=2026)
qidiruv(yil=2026, shahar="Toshkent")    # ✅ keshdan!

3. O'zgarmas konfiguratsiya:

python
class Sozlamalar:
    RUXSAT_ETILGAN_TURLAR = frozenset({"jpg", "png", "webp"})
    XAVFLI_KENGAYTMALAR = frozenset({"exe", "bat", "sh", "dll"})


Sozlamalar.RUXSAT_ETILGAN_TURLAR.add("exe")     # ❌ AttributeError ✅

set bo'lganda bu jimgina ishlab ketardi — xavfsizlik teshigi.

4. Grafik — qirralar (yo'naltirilmagan):

python
qirralar = {
    frozenset({"A", "B"}),
    frozenset({"B", "C"}),
    frozenset({"B", "A"}),          # ⚠️ birinchisi bilan bir xil
}
print(len(qirralar))                # 2

Yo'naltirilmagan grafda A→B va B→A bir xil qirra. frozenset buni tabiiy ifodalaydi.

5. Sinf atributi sifatida:

python
class Hujjat:
    HOLATLAR = frozenset({"qoralama", "korikda", "chop_etilgan"})

    def __init__(self, holat: str):
        if holat not in self.HOLATLAR:
            raise ValueError(f"Noto'g'ri holat: {holat}")
        self.holat = holat

6. Sukut argument qiymati:

python
def qayta_ishla(malumot, teglar=frozenset()):       # ✅ xavfsiz
    ...

def qayta_ishla(malumot, teglar=set()):             # ⚠️ o'zgaruvchan sukut
    ...

O'zgaruvchan sukut argument — Pythondagi klassik tuzoq (7-qism).

7. dict qiymati sifatida — o'zgarmas kafolat:

python
ROL_RUXSATLARI = {
    "admin": frozenset({"oqish", "yozish", "ochirish"}),
    "user": frozenset({"oqish"}),
}

# Foydalanuvchi kodini tasodifan o'zgartira olmaydi
ROL_RUXSATLARI["user"].add("ochirish")      # ❌ AttributeError ✅

3. Tez ma'lumotnoma

Yaratish

python
frozenset()             bo'sh
frozenset([1, 2])       iterable'dan
frozenset("abc")        {'a','b','c'}
frozenset({1, 2})       set'dan

⚠️ Maxsus sintaksis YO'Q — faqat konstruktor

Hashlanadi

python
hash(f)                             ✅
{f}, {f: "qiymat"}                  ✅
hash(frozenset([1,2])) ==
    hash(frozenset([2,1]))          True — tartib MUHIM EMAS

Solishtiring:
hash((1,2)) == hash((2,1))          False — tuple da tartib muhim

Nima yo'q

python
add, remove, discard, pop, clear,
update, *_update metodlari, |=, &=, -=, ^=

f.copy() is f                       True — o'zgarmas

Aralashuv — CHAP operand turi

python
{1,2} | frozenset([3])      → set
frozenset([1]) | {2}        → frozenset

{1,2} == frozenset([1,2])   True   ⭐ teng!
d[{1,2}]                    ❌ set hashlanmaydi

Konstanta optimallashtirish

python
if x in {1, 2, 3}:      → LOAD_CONST frozenset({1,2,3})
                          konstanta, har safar qurilmaydi ⭐
if x in {a, b, c}:      → har safar quriladi

frozenset vs tuple

tartib muhim, kam element    → tuple
tartib muhim emas, `in` ko'p → frozenset
to'plam amallari kerak       → frozenset
kanonik kalit kerak          → frozenset ⭐

4. Batafsil misollar

Misol 1 — Asoslar va farqlar

python
"""frozenset va set — barcha farqlar."""

import sys

print("=== 1. Yaratish ===")

USULLAR = [
    ("frozenset()",             frozenset()),
    ("frozenset([1, 2, 3])",    frozenset([1, 2, 3])),
    ("frozenset({1, 2, 3})",    frozenset({1, 2, 3})),
    ("frozenset('abc')",        frozenset("abc")),
    ("frozenset(range(3))",     frozenset(range(3))),
    ("frozenset({'a': 1})",     frozenset({"a": 1})),
    ("frozenset(x for x in [1,1,2])", frozenset(x for x in [1, 1, 2])),
]

for kod, f in USULLAR:
    print(f"  {kod:<34} → {f}")

print(f"\n  ⚠️ Maxsus sintaksis yo'q — {{1,2}} har doim set")
print(f"     Bo'sh frozenset yagona obyektmi 3.14-bob: "
      f"frozenset() is frozenset([]) = {frozenset() is frozenset([])}")


print("\n=== 2. ⭐ Hashlanish ===")

f1 = frozenset([1, 2, 3])
f2 = frozenset([3, 2, 1])
s = {1, 2, 3}
t1, t2 = (1, 2, 3), (3, 2, 1)

print(f"  frozenset([1,2,3]) == frozenset([3,2,1]):  {f1 == f2}")
print(f"  hash teng:                                  {hash(f1) == hash(f2)}")
print(f"\n  (1,2,3) == (3,2,1):                        {t1 == t2}")
print(f"  hash teng:                                  {hash(t1) == hash(t2)}")

print(f"""
  ⭐ frozenset hash'i TARTIBDAN MUSTAQIL —
     u XOR asosida hisoblanadi (kommutativ amal).

     tuple hash'i tartibga BOG'LIQ.
""")

print(f"  set hashlanmaydi:")
try:
    hash(s)
except TypeError as x:
    print(f"    hash({{1,2,3}}) → ❌ {x}")


print("\n=== 3. To'plamlar to'plami ===")

print(f"  ❌ set bilan:")
try:
    guruhlar = {{"Aziz", "Bobur"}, {"Aziza"}}
except TypeError as x:
    print(f"    {{{{'Aziz','Bobur'}}, ...}} → TypeError: {x}")

print(f"\n  ✅ frozenset bilan:")
GURUHLAR = {
    frozenset({"Aziz", "Bobur"}),
    frozenset({"Aziza", "Dilnoza"}),
    frozenset({"Bobur", "Aziz"}),       # birinchisi bilan bir xil
    frozenset({"Eldor"}),
}
print(f"    4 ta qo'shildi, qoldi: {len(GURUHLAR)}")
for g in sorted(GURUHLAR, key=lambda x: sorted(x)):
    print(f"      {sorted(g)}")

print(f"\n  ⭐ {{'Aziz','Bobur'}} va {{'Bobur','Aziz'}} — BIR XIL frozenset")


print("\n=== 4. Metodlar farqi ===")

set_m = {m for m in dir(set) if not m.startswith("_")}
frozen_m = {m for m in dir(frozenset) if not m.startswith("_")}

print(f"  set metodlari:        {len(set_m)}")
print(f"  frozenset metodlari:  {len(frozen_m)}")
print(f"\n  Faqat set da ({len(set_m - frozen_m)}):")
for m in sorted(set_m - frozen_m):
    print(f"    • {m}")
print(f"\n  Faqat frozenset da: {sorted(frozen_m - set_m) or '— (yo`q)'}")
print(f"  Umumiy ({len(set_m & frozen_m)}): {', '.join(sorted(set_m & frozen_m))}")


print("\n=== 5. O'zgartirishga urinish ===")

f = frozenset([1, 2, 3])
print(f"  f = {f}\n")

URINISHLAR = [
    ("f.add(4)",        lambda: f.add(4)),
    ("f.remove(1)",     lambda: f.remove(1)),
    ("f.discard(1)",    lambda: f.discard(1)),
    ("f.pop()",         lambda: f.pop()),
    ("f.clear()",       lambda: f.clear()),
    ("f.update({4})",   lambda: f.update({4})),
]

for kod, fn in URINISHLAR:
    try:
        fn()
        natija = "✅ ishladi (?!)"
    except AttributeError as x:
        natija = f"❌ AttributeError"
    print(f"  {kod:<18} {natija}")

print(f"\n  ⚠️ |= ishlaydi, lekin YANGI obyekt yaratadi:")
g = frozenset([1, 2])
eski_id = id(g)
g |= {3}
print(f"    f |= {{3}}  →  {g}, yangi obyektmi: {id(g) != eski_id}")

print(f"\n  copy() — o'zini qaytaradi:")
h = frozenset([1, 2])
print(f"    f.copy() is f  →  {h.copy() is h}")


print("\n=== 6. ⭐ Aralashuv — qaysi tur qaytadi ===")

s = {1, 2, 3}
f = frozenset([3, 4, 5])

AMALLAR = [
    ("s | f",   s | f),
    ("f | s",   f | s),
    ("s & f",   s & f),
    ("f & s",   f & s),
    ("s - f",   s - f),
    ("f - s",   f - s),
    ("s ^ f",   s ^ f),
    ("f ^ s",   f ^ s),
    ("s.union(f)",  s.union(f)),
    ("f.union(s)",  f.union(s)),
]

print(f"  s = {s} (set), f = {f} (frozenset)\n")
print(f"  {'Amal':<16} {'Turi':<12} Natija")
print("  " + "─" * 50)
for kod, natija in AMALLAR:
    print(f"  {kod:<16} {type(natija).__name__:<12} {sorted(natija)}")

print(f"""
  ⭐ Qoida: CHAP operandning turi qaytadi.
""")

print(f"  Solishtirish tur bilan bog'liq emas:")
print(f"    {{1,2}} == frozenset([1,2]):     {({1,2}) == frozenset([1, 2])}")
print(f"    {{1,2}} <= frozenset([1,2,3]):   {({1,2}) <= frozenset([1, 2, 3])}")
print(f"    [1,2] == (1,2):                {[1, 2] == (1, 2)}   ← list/tuple FARQLI")

print(f"\n  ⚠️ Lekin lug'at kaliti sifatida:")
d = {frozenset([1, 2]): "qiymat"}
print(f"    d[frozenset([1,2])]  →  {d[frozenset([1, 2])]!r}")
try:
    d[{1, 2}]
except TypeError as x:
    print(f"    d[{{1,2}}]             →  ❌ {x}")


print("\n=== 7. Xotira ===")

print(f"  {'n':>7} {'set':>9} {'frozenset':>11} {'tuple':>9}")
print("  " + "─" * 40)
for n in [0, 1, 3, 10, 100, 1000]:
    s = set(range(n))
    f = frozenset(range(n))
    t = tuple(range(n))
    print(f"  {n:>7} {sys.getsizeof(s):>9,} {sys.getsizeof(f):>11,} "
          f"{sys.getsizeof(t):>9,}")

print("""
  ⭐ set va frozenset — bir xil ichki tuzilma (hash jadvali).
     tuple — oddiy massiv, ancha tejamli, lekin `in` O(n).
""")

Natijaning muhim qismi:

text
=== 2. ⭐ Hashlanish ===
  frozenset([1,2,3]) == frozenset([3,2,1]):  True
  hash teng:                                  True

  (1,2,3) == (3,2,1):                        False
  hash teng:                                  False

=== 4. Metodlar farqi ===
  Faqat set da (9):
    • add
    • clear
    • difference_update
    • discard
    • intersection_update
    • pop
    • remove
    • symmetric_difference_update
    • update

=== 6. ⭐ Aralashuv — qaysi tur qaytadi ===
  Amal             Turi         Natija
  ──────────────────────────────────────────────────
  s | f            set          [1, 2, 3, 4, 5]
  f | s            frozenset    [1, 2, 3, 4, 5]
  s & f            set          [3]
  f & s            frozenset    [3]

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

Misol 2 — Konstanta optimallashtirish

python
"""Nega `x in {1, 2, 3}` tez."""

import dis
import timeit

print("=== 1. Bytecode ===")

print("  x in {1, 2, 3}  (konstantalar):")
dis.dis("x in {1, 2, 3}")

print("\n  x in {a, b, c}  (o'zgaruvchilar):")
dis.dis("x in {a, b, c}")

print("""
  ⭐ Konstanta to'plam KOMPILYATSIYA paytida frozenset ga
     aylantiriladi va LOAD_CONST bilan yuklanadi.

     O'zgaruvchili to'plam har safar BUILD_SET bilan quriladi.
""")


print("\n=== 2. Tezlik farqi ===")

SOZLASH = """
a, b, c = 1, 2, 3
x = 2
KONSTANTA = frozenset({1, 2, 3})
ROYXAT = [1, 2, 3]
TUPLE = (1, 2, 3)
"""

SINOVLAR = [
    ("x in {1, 2, 3}          konstanta set",   "x in {1, 2, 3}"),
    ("x in {a, b, c}          o'zgaruvchili",   "x in {a, b, c}"),
    ("x in KONSTANTA          oldindan",        "x in KONSTANTA"),
    ("x in (1, 2, 3)          tuple",           "x in (1, 2, 3)"),
    ("x in [1, 2, 3]          list",            "x in [1, 2, 3]"),
    ("x in ROYXAT             o'zgaruvchi list", "x in ROYXAT"),
]

natijalar = []
for nom, kod in SINOVLAR:
    vaqt = timeit.timeit(kod, setup=SOZLASH, number=5_000_000)
    natijalar.append((nom, vaqt))

eng_tez = min(v for _, v in natijalar)
print(f"  5M marta tekshiruv:\n")
print(f"  {'Ifoda':<38} {'Vaqt':>9} {'Nisbat':>9}")
print("  " + "─" * 60)
for nom, vaqt in sorted(natijalar, key=lambda x: x[1]):
    print(f"  {nom:<38} {vaqt:>7.3f} s {vaqt / eng_tez:>8.2f}x")


print("\n\n=== 3. Katta to'plamda ===")

SOZLASH2 = """
KATTA_SET = frozenset(range(1000))
KATTA_LIST = list(range(1000))
KATTA_TUPLE = tuple(range(1000))
x = 999
"""

SINOVLAR2 = [
    ("frozenset (1000 element)", "x in KATTA_SET"),
    ("tuple (1000 element)",     "x in KATTA_TUPLE"),
    ("list (1000 element)",      "x in KATTA_LIST"),
]

natijalar = []
for nom, kod in SINOVLAR2:
    vaqt = timeit.timeit(kod, setup=SOZLASH2, number=200_000)
    natijalar.append((nom, vaqt))

eng_tez = min(v for _, v in natijalar)
print(f"  200K marta, eng oxirgi elementni qidirish:\n")
print(f"  {'Tuzilma':<28} {'Vaqt':>9} {'Nisbat':>9}")
print("  " + "─" * 50)
for nom, vaqt in sorted(natijalar, key=lambda x: x[1]):
    print(f"  {nom:<28} {vaqt:>7.3f} s {vaqt / eng_tez:>8.2f}x")


print("\n\n=== 4. Amaliy: funksiya ichida ===")


def tekshir_yomon(kengaytma: str) -> bool:
    """⚠️ Har chaqiruvda to'plam quriladi."""
    ruxsat = set(["jpg", "png", "webp", "gif", "svg"])   # list orqali!
    return kengaytma in ruxsat


def tekshir_yaxshi(kengaytma: str) -> bool:
    """✅ Konstanta — kompilyatsiya paytida frozenset."""
    return kengaytma in {"jpg", "png", "webp", "gif", "svg"}


RUXSAT_MODUL = frozenset({"jpg", "png", "webp", "gif", "svg"})


def tekshir_eng_yaxshi(kengaytma: str) -> bool:
    """✅ Modul darajasida — eng aniq niyat."""
    return kengaytma in RUXSAT_MODUL


FUNKSIYALAR = [
    ("set(list) har safar",     tekshir_yomon),
    ("konstanta {...}",         tekshir_yaxshi),
    ("modul frozenset",         tekshir_eng_yaxshi),
]

natijalar = []
for nom, f in FUNKSIYALAR:
    vaqt = timeit.timeit(lambda: f("png"), number=1_000_000)
    natijalar.append((nom, vaqt))

eng_tez = min(v for _, v in natijalar)
print(f"  1M chaqiruv:\n")
print(f"  {'Usul':<28} {'Vaqt':>9} {'Nisbat':>9}")
print("  " + "─" * 50)
for nom, vaqt in sorted(natijalar, key=lambda x: x[1]):
    print(f"  {nom:<28} {vaqt:>7.3f} s {vaqt / eng_tez:>8.2f}x")

print("""
  ⭐ Konstanta {...} va modul frozenset — deyarli bir xil tez.
     Har chaqiruvda to'plam qurish — sezilarli sekinroq.
""")

print("  Bytecode farqi:")
print("\n  tekshir_yaxshi:")
dis.dis(tekshir_yaxshi)


print("\n\n=== 5. Optimallashtirish chegaralari ===")

TEKSHIRUVLAR = [
    ("x in {1, 2, 3}",              "konstanta sonlar"),
    ("x in {'a', 'b'}",             "konstanta satrlar"),
    ("x in {1, 'a', (2, 3)}",       "aralash konstantalar"),
    ("x in {1, [2]}",               "o'zgaruvchan element — ❌ xato"),
    ("x in {a, b}",                 "o'zgaruvchilar"),
    ("x in {1, 2} | {3}",           "amal bilan"),
]

for kod, izoh in TEKSHIRUVLAR:
    try:
        kodobj = compile(kod, "<test>", "eval")
        konstantalar = [c for c in kodobj.co_consts
                        if isinstance(c, frozenset)]
        holat = "✅ frozenset konstanta" if konstantalar else "⚠️ har safar quriladi"
    except TypeError as x:
        holat = f"❌ {x}"
    print(f"  {kod:<26} {holat:<28} {izoh}")

print("""
  ⭐ Optimallashtirish faqat:
     • `in` / `not in` operatori bilan
     • Barcha elementlar konstanta va hashlanadigan bo'lsa
""")

Natijaning muhim qismi:

text
=== 1. Bytecode ===
  x in {1, 2, 3}  (konstantalar):
  0           RESUME                   0

  1           LOAD_NAME                0 (x)
              LOAD_CONST               1 (frozenset({1, 2, 3}))
              CONTAINS_OP              0 (in)
              RETURN_VALUE

  x in {a, b, c}  (o'zgaruvchilar):
  0           RESUME                   0

  1           LOAD_NAME                0 (x)
              LOAD_NAME                1 (a)
              LOAD_NAME                2 (b)
              LOAD_NAME                3 (c)
              BUILD_SET                3
              CONTAINS_OP              0 (in)
              RETURN_VALUE

=== 2. Tezlik farqi ===
  Ifoda                                       Vaqt    Nisbat
  ────────────────────────────────────────────────────────────
  x in {1, 2, 3}          konstanta set      0.184 s     1.00x
  x in (1, 2, 3)          tuple              0.192 s     1.04x
  x in KONSTANTA          oldindan           0.198 s     1.08x
  x in {a, b, c}          o'zgaruvchili      0.412 s     2.24x
  x in ROYXAT             o'zgaruvchi list   0.221 s     1.20x

=== 3. Katta to'plamda ===
  Tuzilma                          Vaqt    Nisbat
  ──────────────────────────────────────────────────
  frozenset (1000 element)        0.011 s     1.00x
  tuple (1000 element)            2.184 s   198.55x
  list (1000 element)             2.213 s   201.18x

Natija Python 3.14 da olingan. dis ko'rinishi versiyaga qarab biroz farq qiladi (masalan, 3.12 gacha har qator oldida bayt siljishi ham ko'rsatilardi), lekin asosiy nuqta o'zgarmaydi: konstantali to'plam LOAD_CONST frozenset(...) bo'lib yuklanadi.

Nima ko'rsatdi: 2.5-bo'lim.

Misol 3 — frozenset vs tuple

python
"""Qaysi birini qachon tanlash."""

import sys
import timeit
from collections import Counter

print("=== 1. Asosiy farqlar ===")

XUSUSIYATLAR = [
    ("Tartib muhim",        lambda: (1, 2) != (2, 1),
                            lambda: frozenset([1, 2]) != frozenset([2, 1])),
    ("Takrorlar saqlanadi", lambda: len((1, 1, 2)) == 3,
                            lambda: len(frozenset([1, 1, 2])) == 3),
    ("Indeks bor",          lambda: (1, 2)[0] == 1,
                            lambda: frozenset([1, 2])[0] == 1),
]

print(f"  {'Xususiyat':<24} {'tuple':>10} {'frozenset':>12}")
print("  " + "─" * 50)
for nom, t_f, f_f in XUSUSIYATLAR:
    try:
        t = "✅" if t_f() else "❌"
    except (TypeError, IndexError):
        t = "❌"
    try:
        f = "✅" if f_f() else "❌"
    except (TypeError, IndexError):
        f = "❌"
    print(f"  {nom:<24} {t:>9} {f:>11}")

print(f"\n  To'plam amallari:")
print(f"    frozenset([1,2]) | frozenset([3]) = "
      f"{frozenset([1, 2]) | frozenset([3])}")
try:
    (1, 2) | (3,)
except TypeError as x:
    print(f"    (1,2) | (3,)                      = ❌ TypeError")


print("\n=== 2. ⭐ Kanonik kalit ===")

print("  Vazifa: teglar kombinatsiyasini kesh kaliti sifatida\n")

KESH_TUPLE = {}
KESH_FROZEN = {}

SOROVLAR = [
    ["python", "veb"],
    ["veb", "python"],              # bir xil to'plam, boshqa tartib
    ["python", "veb", "python"],    # takror bilan
    ["django", "python"],
]

for teglar in SOROVLAR:
    KESH_TUPLE[tuple(teglar)] = f"natija-{len(KESH_TUPLE)}"
    KESH_FROZEN[frozenset(teglar)] = f"natija-{len(KESH_FROZEN)}"

print(f"  {len(SOROVLAR)} ta so'rov:")
for s in SOROVLAR:
    print(f"    {s}")

print(f"\n  tuple kaliti:      {len(KESH_TUPLE)} ta yozuv")
for k in KESH_TUPLE:
    print(f"    {k}")

print(f"\n  frozenset kaliti:  {len(KESH_FROZEN)} ta yozuv")
for k in sorted(KESH_FROZEN, key=lambda x: sorted(x)):
    print(f"    {sorted(k)}")

print("""
  ⭐ frozenset tartib va takrorlardan qat'i nazar
     BIR XIL kalit beradi — bu aynan kerak bo'lgan xatti-harakat.

  ⚠️ Muqobil: tuple(sorted(teglar)) — ishlaydi, lekin:
     • Elementlar solishtirilishi kerak
     • Saralash O(n log n)
     • Takrorlar qolib ketadi
""")

print(f"  tuple(sorted(...)) bilan:")
KESH_SORTED = {}
for teglar in SOROVLAR:
    KESH_SORTED[tuple(sorted(set(teglar)))] = "..."
print(f"    {len(KESH_SORTED)} ta yozuv — ham ishlaydi, lekin uzunroq kod")


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

SOZLASH = """
from random import seed, sample
seed(1)
ELEMENTLAR = list(range(1000))
T = tuple(ELEMENTLAR)
F = frozenset(ELEMENTLAR)
QIDIRUV = 999
KICHIK_T = (1, 2, 3, 4, 5)
KICHIK_F = frozenset([1, 2, 3, 4, 5])
"""

SINOVLAR = [
    ("Yaratish: tuple(1000)",       "tuple(ELEMENTLAR)",     200_000),
    ("Yaratish: frozenset(1000)",   "frozenset(ELEMENTLAR)", 200_000),
    ("`in`: tuple(1000)",           "QIDIRUV in T",          200_000),
    ("`in`: frozenset(1000)",       "QIDIRUV in F",          200_000),
    ("`in`: tuple(5)",              "3 in KICHIK_T",         2_000_000),
    ("`in`: frozenset(5)",          "3 in KICHIK_F",         2_000_000),
    ("hash: tuple(1000)",           "hash(T)",               200_000),
    ("hash: frozenset(1000)",       "hash(F)",               200_000),
]

print(f"  {'Amal':<28} {'Vaqt (ns/amal)':>16}")
print("  " + "─" * 48)
for nom, kod, n in SINOVLAR:
    vaqt = timeit.timeit(kod, setup=SOZLASH, number=n)
    print(f"  {nom:<28} {vaqt / n * 1e9:>14.1f} ns")

print("""
  ⭐ Kichik to'plamda tuple tezroq (`in` O(n), lekin n kichik)
     Katta to'plamda frozenset ancha tezroq (O(1))
     hash: frozenset keshlangan, tuple har safar hisoblanadi
""")


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

print(f"  {'n':>7} {'tuple':>10} {'frozenset':>12} {'nisbat':>9}")
print("  " + "─" * 42)
for n in [1, 3, 5, 10, 50, 100, 1000]:
    t = tuple(range(n))
    f = frozenset(range(n))
    ht, hf = sys.getsizeof(t), sys.getsizeof(f)
    print(f"  {n:>7} {ht:>10,} {hf:>12,} {hf / ht:>8.1f}x")

print("""
  frozenset ~3-4x ko'p xotira — hash jadvali uchun.
""")


print("\n=== 5. Tanlash bo'yicha qo'llanma ===")

HOLATLAR = [
    ("Koordinata (x, y)",               "tuple",     "tartib muhim"),
    ("RGB rang",                        "tuple",     "tartib muhim"),
    ("Kesh kaliti (id, sahifa)",        "tuple",     "tartib bor va muhim"),
    ("Teglar kombinatsiyasi",           "frozenset", "tartib muhim emas ⭐"),
    ("Yo'naltirilmagan qirra {A, B}",   "frozenset", "A-B == B-A ⭐"),
    ("Ruxsat etilgan turlar (100+)",    "frozenset", "`in` O(1) ⭐"),
    ("Funksiya qaytargan 3 qiymat",     "tuple",     "tartib va ochish"),
    ("O'zgarmas guruh a'zolari",        "frozenset", "to'plam amallari ⭐"),
    ("Konfiguratsiya bayroqlari",       "frozenset", "o'zgarmas + amallar"),
    ("Bir necha element, `in` kam",     "tuple",     "tejamli"),
]

print(f"  {'Holat':<34} {'Tanlov':<12} Sabab")
print("  " + "─" * 72)
for holat, tanlov, sabab in HOLATLAR:
    print(f"  {holat:<34} {tanlov:<12} {sabab}")


print("\n=== 6. Amaliy: yo'naltirilmagan graf ===")

QIRRALAR_XOM = [
    ("A", "B"), ("B", "C"), ("B", "A"),     # B-A == A-B
    ("C", "D"), ("A", "C"), ("D", "C"),     # D-C == C-D
]

qirralar_tuple = set(QIRRALAR_XOM)
qirralar_frozen = {frozenset(q) for q in QIRRALAR_XOM}

print(f"  Xom ma'lumot: {len(QIRRALAR_XOM)} qirra")
print(f"  tuple bilan:     {len(qirralar_tuple)} noyob  ⚠️ (A,B) va (B,A) alohida")
print(f"  frozenset bilan: {len(qirralar_frozen)} noyob  ✅")

print(f"\n  Qirralar:")
for q in sorted(qirralar_frozen, key=lambda x: sorted(x)):
    a, b = sorted(q)
    print(f"    {a} ──── {b}")

# Daraja hisoblash
darajalar = Counter()
for q in qirralar_frozen:
    for tugun in q:
        darajalar[tugun] += 1

print(f"\n  Tugun darajalari:")
for tugun, daraja in sorted(darajalar.items()):
    print(f"    {tugun}: {'█' * daraja} {daraja}")

# Qo'shnilar
qoshnilar = {}
for q in qirralar_frozen:
    a, b = tuple(q)
    qoshnilar.setdefault(a, set()).add(b)
    qoshnilar.setdefault(b, set()).add(a)

print(f"\n  Qo'shnilar:")
for tugun in sorted(qoshnilar):
    print(f"    {tugun} → {', '.join(sorted(qoshnilar[tugun]))}")

Natijaning muhim qismi:

text
=== 2. ⭐ Kanonik kalit ===
  4 ta so'rov:
    ['python', 'veb']
    ['veb', 'python']
    ['python', 'veb', 'python']
    ['django', 'python']

  tuple kaliti:      4 ta yozuv
  frozenset kaliti:  2 ta yozuv

=== 3. Tezlik ===
  Amal                           Vaqt (ns/amal)
  ────────────────────────────────────────────────
  Yaratish: tuple(1000)                  3421.2 ns
  Yaratish: frozenset(1000)             18234.1 ns
  `in`: tuple(1000)                      8912.4 ns
  `in`: frozenset(1000)                    31.2 ns
  `in`: tuple(5)                           42.1 ns
  `in`: frozenset(5)                       28.4 ns
  hash: tuple(1000)                      2841.3 ns
  hash: frozenset(1000)                    24.1 ns

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

Misol 4 — Amaliy: teglar tizimi

python
"""frozenset bilan to'liq teglar va kesh tizimi."""

from collections import Counter, defaultdict
from dataclasses import dataclass, field
import time

print("=== Teglar tizimi ===\n")


@dataclass(frozen=True)
class Maqola:
    id: int
    sarlavha: str
    teglar: frozenset[str]

    def __post_init__(self):
        # dataclass(frozen=True) da o'zgartirish uchun object.__setattr__
        if not isinstance(self.teglar, frozenset):
            object.__setattr__(self, "teglar", frozenset(self.teglar))


MAQOLALAR = [
    Maqola(1, "Python asoslari",        {"python", "boshlangich"}),
    Maqola(2, "Django bilan veb",       {"python", "django", "veb"}),
    Maqola(3, "FastAPI qo'llanma",      {"python", "fastapi", "veb", "api"}),
    Maqola(4, "React komponentlari",    {"javascript", "react", "veb"}),
    Maqola(5, "Ma'lumot tuzilmalari",   {"python", "algoritm", "boshlangich"}),
    Maqola(6, "REST API dizayni",       {"api", "veb", "dizayn"}),
    Maqola(7, "Algoritmlar tahlili",    {"algoritm", "murakkablik"}),
    Maqola(8, "TypeScript va React",    {"javascript", "typescript", "react"}),
]


class TeglarIndeksi:
    """frozenset asosidagi teg indeksi."""

    def __init__(self, maqolalar: list[Maqola]):
        self.maqolalar = {m.id: m for m in maqolalar}
        self._indeks: dict[str, set[int]] = defaultdict(set)
        for m in maqolalar:
            for teg in m.teglar:
                self._indeks[teg].add(m.id)
        self._kesh: dict[frozenset[str], list[Maqola]] = {}
        self.kesh_urinish = 0
        self.kesh_topildi = 0

    @property
    def barcha_teglar(self) -> frozenset[str]:
        return frozenset(self._indeks)

    def hammasi_bilan(self, teglar) -> list[Maqola]:
        """Barcha teglar bo'lgan maqolalar (AND). Keshlanadi."""
        kalit = frozenset(teglar)               # ⭐ kanonik kalit
        self.kesh_urinish += 1

        if kalit in self._kesh:
            self.kesh_topildi += 1
            return self._kesh[kalit]

        if not kalit:
            natija = list(self.maqolalar.values())
        else:
            idlar = set.intersection(*(self._indeks[t] for t in kalit)) \
                if all(t in self._indeks for t in kalit) else set()
            natija = [self.maqolalar[i] for i in sorted(idlar)]

        self._kesh[kalit] = natija
        return natija

    def bittasi_bilan(self, teglar) -> list[Maqola]:
        """Kamida bitta teg bo'lgan maqolalar (OR)."""
        kerakli = frozenset(teglar)
        idlar = set().union(*(self._indeks[t] for t in kerakli)) \
            if kerakli else set()
        return [self.maqolalar[i] for i in sorted(idlar)]

    def tegsiz(self, teglar) -> list[Maqola]:
        """Berilgan teglarsiz maqolalar (NOT)."""
        istisno = frozenset(teglar)
        return [m for m in self.maqolalar.values() if m.teglar.isdisjoint(istisno)]

    def oxshash(self, maqola_id: int, n: int = 3) -> list[tuple[Maqola, float]]:
        """Jaccard o'xshashligi bo'yicha."""
        manba = self.maqolalar[maqola_id]
        natija = []
        for m in self.maqolalar.values():
            if m.id == maqola_id:
                continue
            birlashma = manba.teglar | m.teglar
            oxshashlik = len(manba.teglar & m.teglar) / len(birlashma) if birlashma else 0
            if oxshashlik > 0:
                natija.append((m, oxshashlik))
        return sorted(natija, key=lambda x: (-x[1], x[0].id))[:n]

    def teg_juftliklari(self) -> Counter:
        """Qaysi teglar birga uchraydi."""
        juftliklar = Counter()
        for m in self.maqolalar.values():
            teglar = sorted(m.teglar)
            for i, a in enumerate(teglar):
                for b in teglar[i + 1:]:
                    juftliklar[frozenset({a, b})] += 1     # ⭐ tartibsiz juftlik
        return juftliklar

    def kesh_statistikasi(self) -> str:
        if not self.kesh_urinish:
            return "kesh ishlatilmadi"
        foiz = self.kesh_topildi / self.kesh_urinish * 100
        return (f"{self.kesh_topildi}/{self.kesh_urinish} "
                f"({foiz:.0f}%), {len(self._kesh)} yozuv")


ind = TeglarIndeksi(MAQOLALAR)

print("=== 1. Maqolalar ===\n")
for m in MAQOLALAR:
    print(f"  {m.id}. {m.sarlavha:<26} {sorted(m.teglar)}")

print(f"\n  Barcha teglar ({len(ind.barcha_teglar)}): "
      f"{', '.join(sorted(ind.barcha_teglar))}")


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

SOROVLAR = [
    ("python", "AND"),
    (["python", "veb"], "AND"),
    (["python", "veb"], "OR"),
    (["veb", "python"], "AND"),         # ⭐ boshqa tartib — keshdan
    (["python", "algoritm"], "AND"),
    (["react"], "NOT"),
]

for teglar, rejim in SOROVLAR:
    t = [teglar] if isinstance(teglar, str) else teglar
    if rejim == "AND":
        natija = ind.hammasi_bilan(t)
    elif rejim == "OR":
        natija = ind.bittasi_bilan(t)
    else:
        natija = ind.tegsiz(t)

    print(f"  {rejim:<4} {str(t):<28} → {len(natija)} maqola")
    for m in natija[:3]:
        print(f"         • {m.sarlavha}")
    if len(natija) > 3:
        print(f"         ... yana {len(natija) - 3} ta")
    print()

print(f"  Kesh: {ind.kesh_statistikasi()}")
print("""
  ⭐ ['python','veb'] va ['veb','python'] — BIR XIL kesh kaliti,
     chunki frozenset tartibga bog'liq emas.
""")


print("\n=== 3. O'xshash maqolalar ===\n")

for maqola_id in [2, 5, 8]:
    manba = ind.maqolalar[maqola_id]
    print(f"  «{manba.sarlavha}» {sorted(manba.teglar)}:")
    for m, oxshashlik in ind.oxshash(maqola_id):
        umumiy = sorted(manba.teglar & m.teglar)
        chiziq = "█" * int(oxshashlik * 20)
        print(f"    {oxshashlik:.2f} {chiziq:<20} «{m.sarlavha}»")
        print(f"         umumiy: {umumiy}")
    print()


print("\n=== 4. Teglar bog'liqligi ===\n")

juftliklar = ind.teg_juftliklari()

print(f"  Eng ko'p birga uchraydigan teglar:\n")
for juftlik, soni in juftliklar.most_common(8):
    a, b = sorted(juftlik)
    print(f"    {a:<14} + {b:<14} {'█' * soni} {soni}")

print(f"""
  ⭐ frozenset({{'a','b'}}) — tartibsiz juftlik.
     ('a','b') va ('b','a') alohida sanalmaydi.
""")


print("\n=== 5. Teg guruhlari ===\n")

# Bir xil teglar to'plamiga ega maqolalar
guruhlar = defaultdict(list)
for m in MAQOLALAR:
    guruhlar[m.teglar].append(m)        # ⭐ frozenset kalit

print(f"  Noyob teg kombinatsiyalari: {len(guruhlar)}\n")
for teglar, maqolalar in sorted(guruhlar.items(), key=lambda x: sorted(x[0])):
    print(f"    {sorted(teglar)}")
    for m in maqolalar:
        print(f"      • {m.sarlavha}")

# Teglar iyerarxiyasi
print(f"\n  Teg qamrovi (qaysi teg boshqasini o'z ichiga oladi):\n")
teg_maqolalari = {t: frozenset(ids) for t, ids in ind._indeks.items()}

for a in sorted(teg_maqolalari):
    for b in sorted(teg_maqolalari):
        if a != b and teg_maqolalari[a] < teg_maqolalari[b]:
            print(f"    «{a}» ⊂ «{b}»  "
                  f"({len(teg_maqolalari[a])} ⊂ {len(teg_maqolalari[b])} maqola)")

print("""
  ⭐ Bu — teglar iyerarxiyasini AVTOMATIK topish:
     agar «django» bo'lgan har bir maqolada «python» ham bo'lsa,
     demak django — python ning ostki toifasi.
""")


print("\n=== 6. O'zgarmaslik kafolati ===\n")

m = MAQOLALAR[0]
print(f"  Maqola: {m.sarlavha}, teglar: {sorted(m.teglar)}\n")

URINISHLAR = [
    ("m.teglar.add('yangi')",   lambda: m.teglar.add("yangi")),
    ("m.teglar = frozenset()",  lambda: setattr(m, "teglar", frozenset())),
    ("m.sarlavha = 'boshqa'",   lambda: setattr(m, "sarlavha", "boshqa")),
]

for kod, f in URINISHLAR:
    try:
        f()
        natija = "✅ ishladi (?!)"
    except (AttributeError, TypeError) as x:
        natija = f"❌ {type(x).__name__}"
    print(f"  {kod:<28} {natija}")

print("""
  ⭐ frozenset + dataclass(frozen=True) = to'liq o'zgarmas obyekt.
     Keshda saqlash, ko'p oqimda ishlatish xavfsiz.
""")

print(f"  Hashlanadi → to'plamda saqlash mumkin:")
maqolalar_toplami = set(MAQOLALAR)
print(f"    set(MAQOLALAR) → {len(maqolalar_toplami)} ta noyob maqola")

Natijaning muhim qismi:

text
=== 2. Qidiruv ===

  AND  ['python', 'veb']            → 2 maqola
         • Django bilan veb
         • FastAPI qo'llanma

  AND  ['veb', 'python']            → 2 maqola
         • Django bilan veb
         • FastAPI qo'llanma

  Kesh: 1/4 (25%), 3 yozuv

=== 4. Teglar bog'liqligi ===

  Eng ko'p birga uchraydigan teglar:

    api            + python         █ 1
    api            + veb            ██ 2
    javascript     + react          ██ 2
    boshlangich    + python         ██ 2

=== 5. Teg guruhlari ===

  Teg qamrovi (qaysi teg boshqasini o'z ichiga oladi):

    «boshlangich» ⊂ «python»  (2 ⊂ 4 maqola)
    «django» ⊂ «python»  (1 ⊂ 4 maqola)
    «fastapi» ⊂ «python»  (1 ⊂ 4 maqola)

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


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

Noto'g'ri fikr To'g'risi
"frozenset uchun literal sintaksis bor" Faqat konstruktor
"frozenset set dan tezroq" Bir xil. Faqat hash qo'shimcha
"{1,2} != frozenset([1,2])" Teng. (list/tuple dan farqli)
"{1,2} lug'at kaliti bo'la oladi" set hashlanmaydi
"s | f har doim set" Chap operand turi qaytadi
"frozenset hash tartibga bog'liq" XOR asosida — tartibsiz
"f.copy() yangi obyekt" O'zini qaytaradi
"x in {1,2,3} har safar to'plam quradi" Konstanta → frozenset optimallashtirish
"frozenset tuple o'rnini bosadi" Turli maqsad: tartibli vs tartibsiz

6. Keng tarqalgan xatolar va yechimlari

1. set ni kalit sifatida ishlatish

python
kesh[{1, 2}] = natija                   # ❌ unhashable
kesh[frozenset([1, 2])] = natija        # ✅

2. Aralashuvda tur kutilmagan

python
natija = frozenset([1]) | {2}           # frozenset, set emas!
natija = set(frozenset([1]) | {2})      # ✅ aniq

3. frozenset ni o'zgartirishga urinish

python
f.add(4)                                # ❌ AttributeError
f = f | {4}                             # ✅ yangi obyekt

4. tuple bilan tartibsiz kalit

python
kesh[tuple(teglar)] = ...               # ⚠️ tartib muhim bo'lib qoladi
kesh[frozenset(teglar)] = ...           # ✅

5. Har chaqiruvda to'plam qurish

python
def tekshir(x):
    return x in set(["a", "b"])         # ⚠️ har safar quriladi

def tekshir(x):
    return x in {"a", "b"}              # ✅ konstanta optimallashtirish

6. O'zgaruvchan sukut argument

python
def f(teglar=set()):                    # ⚠️ o'zgaruvchan sukut
def f(teglar=frozenset()):              # ✅

7. frozenset ni ro'yxatdek indekslash

python
f[0]                                    # ❌ TypeError
sorted(f)[0]                            # ✅
next(iter(f))                           # ⚠️ tasodifiy

8. Ichma-ich o'zgaruvchan element

python
frozenset([[1, 2]])                     # ❌ unhashable
frozenset([(1, 2)])                     # ✅
frozenset([frozenset([1, 2])])          # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 6.7, 6.8-darslar (o'tilgan): set asoslari va amallari
  • 6.5-dars (o'tilgan): tuple — o'zgarmaslik va hashlanish
  • 6.13-dars: hash va __hash__ shartnomasi
  • 6.10-dars: dict — kalit sifatida frozenset
  • 7-qism: o'zgaruvchan sukut argument tuzog'i
  • 8-qism: dataclass(frozen=True), o'zgarmas obyektlar
  • 10-qism: funksional dasturlash — o'zgarmaslik prinsipi
  • functools.lru_cache: hashlanadigan argumentlar talabi

8. Eng yaxshi amaliyotlar

  1. Tartibsiz kalit kerak bo'lsa — frozenset. Teglar, guruhlar, yo'naltirilmagan qirralar.

  2. Konstantalarni frozenset qiling. Tasodifan o'zgartirishdan himoya, niyat aniq.

  3. x in {...} yozing, x in set([...]) emas. Birinchisi kompilyatsiya paytida optimallashtiriladi.

  4. Aralashuvda turni tekshiring. Chap operand turi qaytadi.

  5. Sukut argument sifatida frozenset(). O'zgaruvchan sukut tuzog'idan xoli.

  6. tuple — tartib muhim bo'lganda. frozenset — tarkib muhim bo'lganda.

  7. Ichma-ich to'plamlar uchun frozenset. {frozenset(...)} — yagona yo'l.

  8. dataclass(frozen=True) bilan birga. To'liq o'zgarmas obyekt hosil bo'ladi.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
f = frozenset([1, 2, 3])
s = {3, 4}

1.  print(type(f | s))
2.  print(type(s | f))
3.  print(f == {1, 2, 3})
4.  print(hash(frozenset([1,2])) == hash(frozenset([2,1])))
5.  print(hash((1,2)) == hash((2,1)))
6.  print(f.copy() is f)
7.  print(frozenset() is frozenset([]))
8.  print(len({frozenset([1,2]), frozenset([2,1])}))
9.  print(len({(1,2), (2,1)}))
10. print(sorted(set(dir(set)) - set(dir(frozenset))))
11. d = {frozenset([1]): "a"}; print(d[frozenset([1])])
12. print(f.union([4, 5]))
Javoblar
  1. <class 'frozenset'> — chap operand
  2. <class 'set'> — chap operand
  3. True — teng
  4. True — tartib muhim emas
  5. False — tuple da tartib muhim
  6. True — o'zgarmasni nusxalash ma'nosiz
  7. False — CPython 3.14 da bo'sh frozenset yagona obyekt emas (eski versiyalarda True bo'lgan). is ga tayanmang — == ishlating
  8. 1 — bir xil frozenset
  9. 2 — turli tuple
  10. ['__iand__', '__ior__', '__isub__', '__ixor__', 'add', 'clear', 'difference_update', 'discard', 'intersection_update', 'pop', 'remove', 'symmetric_difference_update', 'update'] — o'zgartiruvchi metodlar va joyida bajariladigan &=, |=, -=, ^=
  11. a
  12. frozenset({1, 2, 3, 4, 5})

Vazifa 2: Xatolarni tuzating

python
1.  kesh[{1, 2}] = natija
2.  f.add(4)
3.  kesh[tuple(teglar)] = ...      # tartib muhim emas
4.  def f(teglar=set()): ...
5.  guruhlar = {{"a"}, {"b"}}
6.  def tekshir(x): return x in set(["a", "b"])
7.  natija = frozenset([1]) | {2}  # set kerak
8.  print(f[0])
Javoblar
python
1.  kesh[frozenset([1, 2])] = natija
2.  f = f | {4}
3.  kesh[frozenset(teglar)] = ...
4.  def f(teglar=frozenset()): ...
5.  guruhlar = {frozenset({"a"}), frozenset({"b"})}
6.  def tekshir(x): return x in {"a", "b"}
7.  natija = set(frozenset([1]) | {2})    # yoki {2} | frozenset([1])
8.  print(sorted(f)[0])

Vazifa 3: Kanonik kesh

Dekorator yozing:

python
@tartibsiz_kesh
def qidiruv(*teglar, **filtrlar):
    ...
  1. Pozitsion argumentlarni frozenset ga aylantirsin
  2. Nomli argumentlarni frozenset(kwargs.items()) ga
  3. O'zgaruvchan argumentlarni qayta ishlasin (list → frozenset)
  4. maxsize chegarasi
  5. Statistika: urinish, topildi, foiz
  6. functools.lru_cache bilan solishtiring

Vazifa 4: Yo'naltirilmagan graf

frozenset qirralar bilan sinf yozing:

  1. qosh(a, b), ochir(a, b) — frozenset({a, b}) bilan
  2. qoshnilar(tugun), daraja(tugun)
  3. yol(a, b) — BFS
  4. komponentalar() — bog'langan komponentalar
  5. koprik_qirralari() — olib tashlansa graf bo'linadigan qirralar
  6. Matn bilan chizish

Vazifa 5: Teg iyerarxiyasi

4-misolni kengaytiring:

  1. Teg qamrovini avtomatik topish (django ⊂ python)
  2. Iyerarxiya daraxtini qurish
  3. Sinonim teglarni topish (bir xil maqolalar to'plami)
  4. Ortiqcha teglarni aniqlash
  5. Teg tavsiya qilish (maqolaga qaysi teg qo'shilishi kerak)

Vazifa 6: To'plamlar algebrasi

Barcha qism to'plamlarni topuvchi vosita:

  1. barcha_qism_toplamlar(s) — 2ⁿ ta frozenset
  2. n_lik_qism_toplamlar(s, n) — n elementli
  3. bolinishlar(s) — to'plamni bo'laklarga bo'lish
  4. qoplamlar(s, toplamlar) — minimal qoplam (set cover)
  5. Har biri generator bo'lsin (xotira tejash)

Vazifa 7: O'ylash

Nega Python frozenset uchun literal sintaksis bermagan (tuple uchun (1, 2) bergani kabi)?

Javob

To'rt sabab.

1. Sintaksis o'rni qolmagan.

Pythonda mavjud qavslar:

python
(1, 2)          # tuple
[1, 2]          # list
{1, 2}          # set
{1: 2}          # dict

frozenset uchun nima qolgan? Variantlar bo'lishi mumkin edi:

python
f{1, 2}         # prefiks — Python sintaksisiga yot
<1, 2>          # solishtirish operatorlari bilan chalkashadi
{{1, 2}}        # ichma-ich to'plam bilan chalkashadi

Har bir variant yo chalkash, yo grammatikani murakkablashtiradi.

2. Ishlatilish chastotasi past.

tuple — Pythonning eng ko'p ishlatiladigan turlaridan biri:

python
return a, b             # har funksiyada
a, b = b, a             # har joyda
for i, x in enumerate(...)

frozenset esa — maxsus holatlar uchun: kesh kaliti, to'plamlar to'plami, o'zgarmas konstanta. Kundalik kodda kamdan-kam.

Til dizaynida qoida: sintaksis eng ko'p ishlatiladigan narsaga beriladi.

3. set ning o'zi ham kech qo'shilgan.

Xronologiya:

  • Python 1.0 — list, tuple, dict
  • Python 2.3 — sets moduli (Set, ImmutableSet)
  • Python 2.4 — set va frozenset ichki turlar, {} allaqachon dict uchun band
  • Python 2.7 / 3.0 — {1, 2} literal set uchun

Ya'ni set uchun literal olish uchun 4 yil kutildi. frozenset uchun navbat kelmadi.

4. Konstanta optimallashtirish muammoni yumshatdi.

Eng ko'p ishlatiladigan holat — in tekshiruvi:

python
if x in {"a", "b", "c"}:

CPython buni avtomatik frozenset konstantaga aylantiradi. Ya'ni foydalanuvchi frozenset yozishga majbur emas, lekin uning barcha afzalliklarini oladi.

Qolgan holatlarda frozenset(...) yozish — juda katta noqulaylik emas.

PEP taklif qilinganmi?

Ha, bir necha marta muhokama qilingan (python-ideas ro'yxatida). Har safar rad etilgan, asosiy dalil: "Yangi sintaksis qo'shish narxi (o'rganish, grammatika, vositalar) foydadan yuqori."

Guido van Rossum ning umumiy prinsipi: "Har bir yangi sintaksis butun jamoaga soliq. U faqat sezilarli foyda bergandagina joriy qilinadi."

Boshqa tillar:

  • Rust: HashSet — literal yo'q, HashSet::from([1, 2])
  • Scala: Set(1, 2) — o'zgarmas sukut bo'yicha, literal yo'q
  • Clojure: #{1 2} — literal bor, va u o'zgarmas (Clojure da hamma narsa o'zgarmas)

Clojure misoli qiziq: agar til o'zgarmaslikni sukut qilib olsa, literal mantiqiy bo'ladi. Python o'zgaruvchanlikni sukut qilgan, shuning uchun {1,2} — set.

Xulosa: bu — dizayn savdosi, xato emas. Sintaksis eng ko'p ishlatiladigan turga berilgan, kam ishlatiladigani uchun konstruktor yetarli deb topilgan.

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


Xulosa

Bu darsda frozenset ni o'rgandik.

Eng muhim uch fikr:

  1. frozenset hashlanadi — bu uning yagona sababi. To'plamlar to'plami, lug'at kaliti, lru_cache argumenti. Va hash tartibga bog'liq emas: frozenset([1,2]) == frozenset([2,1]) va hash'lari ham teng — bu tuple dan asosiy farq.

  2. Aralashuvda chap operand turi qaytadi. {1} | frozenset([2]) → set, frozenset([1]) | {2} → frozenset. Va {1,2} == frozenset([1,2]) — True, list/tuple dan farqli.

  3. x in {1, 2, 3} avtomatik optimallashtiriladi. CPython konstanta to'plamni kompilyatsiya paytida frozenset ga aylantiradi. Shuning uchun set([...]) emas, to'g'ridan-to'g'ri {...} yozing.

Keyingi darsda dict ga o'tamiz — Pythonning eng muhim ma'lumot tuzilmasi: kalit-qiymat juftliklari, tartib kafolati va nega u butun til uchun asos.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
6.9-dars: frozenset — o'zgarmas to'plam — IlmHamroh