Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Hash nima
- 2.2. Nega o'zgaruvchan obyektlar hashlanmaydi
- 2.3. __hash__ / __eq__ shartnomasi
- 2.4. Sukut xatti-harakat
- 2.5. Avtomatik hash — dataclass va NamedTuple
- 2.6. Hash jadvali ichkarisi
- 2.7. To'qnashuvlar va HashDoS
- 2.8. hash() va kriptografik hash — farq
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Hash asoslari
- Misol 2 — __hash__ / __eq__ shartnomasi
- Misol 3 — To'qnashuvlar va tezlik
- Misol 4 — Amaliy: to'g'ri hashlanadigan sinflar
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
6.13-dars: Hash nima va nega kalit hashlanadi
6-QISM — MA'LUMOT TUZILMALARI · 13-dars
1. Kirish va motivatsiya
Uch darsda takror aytdik: "kalitlar hashlanishi kerak". Endi savol — nega?
d = {}
d[[1, 2]] = "x" # ❌ TypeError: unhashable type: 'list'
d[(1, 2)] = "x" # ✅Va nozikroq savol:
class Nuqta:
def __init__(self, x, y):
self.x, self.y = x, y
def __eq__(self, boshqa):
return (self.x, self.y) == (boshqa.x, boshqa.y)
n = Nuqta(1, 2)
{n} # ❌ TypeError: unhashable type: 'Nuqta'Faqat __eq__ yozdik — va sinf hashlanmaydigan bo'lib qoldi. Nega?
Yana biri:
class Yomon:
def __init__(self, q):
self.q = q
def __hash__(self):
return hash(self.q)
def __eq__(self, boshqa):
return self.q == boshqa.q
y = Yomon([1, 2]) # ⚠️ o'zgaruvchan ichki holat
s = {y}
y.q.append(3)
y in s # False! ← o'zimizni yo'qotdikBu darsda:
- Hash jadvali ichki mexanizmi — uyachalar, probing, o'sish
__hash__/__eq__shartnomasi — uch qoida- To'qnashuvlar va ularning narxi
- O'z sinflaringizni to'g'ri hashlanadigan qilish
dataclass,NamedTuple,frozen— avtomatik hash- Kriptografik hash bilan farq
2. Nazariya — chuqur tushuntirish
2.1. Hash nima
Hash funksiyasi — har qanday obyektni butun songa aylantiradi:
hash(42) # 42
hash("salom") # -3874927349... (har jarayonda boshqa)
hash((1, 2)) # -3550055125485641917
hash(3.14) # 322818021289917443
hash(None) # 5740354900026072187 (versiyaga bog'liq)Uch talab:
- Determinizm (bir jarayon ichida):
hash(x)doim bir xil - Teng obyektlar — teng hash:
a == b→hash(a) == hash(b) - Tez: O(1) yoki obyekt hajmiga proporsional
Teskarisi shart emas: hash(a) == hash(b) bo'lsa ham a != b bo'lishi mumkin — bu to'qnashuv (collision).
Nega hash kerak:
Kalitni topish uchun uni qayerga qo'yganini hisoblash mumkin bo'lishi kerak:
d["salom"] = 1
1. hash("salom") → 8934572...
2. 8934572 & (hajm - 1) → 5
3. jadval[5] = ("salom", 1)
d["salom"] ni o'qish:
1. hash("salom") → 8934572 (BIR XIL)
2. & (hajm - 1) → 5
3. jadval[5] ni tekshirish → topildiBitta hisoblash — elementlar soniga bog'liq emas. Bu — O(1).
Agar hash bo'lmasa: har elementni birma-bir solishtirish kerak — O(n).
2.2. Nega o'zgaruvchan obyektlar hashlanmaydi
Hash obyekt qiymatidan hisoblanadi. Qiymat o'zgarsa — hash ham o'zgaradi.
Tasavvur qiling, list hashlansa:
r = [1, 2]
d = {}
d[r] = "qiymat" # hash([1,2]) = 5 → jadval[5]
r.append(3) # endi hash([1,2,3]) = 9
d[r] # jadval[9] ga qaraydi → TOPILMADI!Obyekt lug'at ichida, lekin uni topib bo'lmaydi. U "yo'qolib qoladi".
Bundan ham yomoni:
for kalit in d:
print(kalit, d[kalit]) # ❌ KeyError — iteratsiya kalitini topib bo'lmaydi!Shuning uchun Python o'zgaruvchan obyektlarni hashlashni TAQIQLAYDI:
list.__hash__ # None
dict.__hash__ # None
set.__hash__ # None
tuple.__hash__ # <slot wrapper>
frozenset.__hash__ # <slot wrapper>
str.__hash__ # <slot wrapper>__hash__ = None — "bu tur hashlanmaydi" degan aniq signal.
Bu — kelishuv, majburiyat emas:
class OzgaruvchanLekinHashlanadigan:
def __init__(self, q):
self.q = q
def __hash__(self):
return hash(self.q) # ⚠️ q o'zgarsa hash ham o'zgaradi
def __eq__(self, b):
return self.q == b.qPython bunga ruxsat beradi, lekin natija — yuqoridagi muammo.
Sayoz o'zgarmaslik yetarli emas:
t = (1, [2, 3]) # tuple o'zgarmas
hash(t) # ❌ TypeError — ichida list borTuple hash'i elementlar hash'idan hisoblanadi. Element hashlanmasa — tuple ham hashlanmaydi (6.5-dars).
2.3. __hash__ / __eq__ shartnomasi
Uchta qoida:
Qoida 1: a == b → hash(a) == hash(b)
Bu — majburiy. Buzilsa lug'at buziladi:
class Buzuq:
def __init__(self, q):
self.q = q
def __eq__(self, b):
return self.q == b.q
def __hash__(self):
return id(self) # ⚠️ teng obyektlar turli hash!
a, b = Buzuq(1), Buzuq(1)
a == b # True
hash(a) == hash(b) # False ← SHARTNOMA BUZILDI
d = {a: "birinchi"}
d[b] # ❌ KeyError — teng, lekin topilmaydiQoida 2: __eq__ yozsangiz, __hash__ ham yozing (yoki None qiling)
Python buni avtomatik amalga oshiradi:
class Faqat_eq:
def __eq__(self, b):
return True
Faqat_eq.__hash__ # None ← Python o'chirdi
{Faqat_eq()} # ❌ TypeError: unhashableNega: sukut __hash__ — id() asosida. Agar __eq__ ni qiymat bo'yicha qilsangiz, sukut hash 1-qoidani buzardi. Python xavfsizlik uchun __hash__ ni o'chiradi.
Bu — Pythonning eng foydali "himoya to'siqlaridan" biri.
Qoida 3: Hash obyekt ishlash muddatida o'zgarmasligi kerak
class Xavfli:
def __init__(self, q):
self.q = q
def __hash__(self):
return hash(self.q)
def __eq__(self, b):
return self.q == b.q
x = Xavfli(1)
s = {x}
x.q = 2 # ⚠️ hash o'zgardi
x in s # False — o'zi to'plamda, lekin topilmaydi
len(s) # 1 — hali ham ichida
list(s)[0] is x # True — o'sha obyekt!Obyekt to'plamda, lekin qidirib topilmaydi. Bu — "yashirin element" muammosi.
Yechim: hash faqat o'zgarmas maydonlardan hisoblanishi kerak:
class Xodim:
def __init__(self, xodim_id: int, ism: str, maosh: int):
self._id = xodim_id # o'zgarmas identifikator
self.ism = ism # o'zgarishi mumkin
self.maosh = maosh # o'zgarishi mumkin
def __eq__(self, b):
return isinstance(b, Xodim) and self._id == b._id
def __hash__(self):
return hash(self._id) # ⭐ faqat o'zgarmas maydonBu — ma'lumotlar bazasi obyektlari uchun standart naqsh: id bo'yicha tenglik va hash.
2.4. Sukut xatti-harakat
Sukut __hash__ va __eq__:
class Oddiy:
pass
a, b = Oddiy(), Oddiy()
a == b # False — id bo'yicha
a == a # True
hash(a) # id(a) // 16 ga yaqin
{a, b} # ✅ ikkita elementSukut bo'yicha har obyekt o'ziga xos. Bu — mantiqiy: ikki turli obyekt, garchi maydonlari bir xil bo'lsa ham, turli.
class Nuqta:
def __init__(self, x, y):
self.x, self.y = x, y
Nuqta(1, 2) == Nuqta(1, 2) # False ← sukut
{Nuqta(1, 2), Nuqta(1, 2)} # ikkita elementQachon sukut yetarli:
- Obyekt identifikatsiya bilan aniqlanadi (ulanish, fayl, oqim)
- Ikki turli obyekt hech qachon "bir xil" bo'lmaydi
Qachon o'zgartirish kerak:
- Obyekt qiymat (value object): nuqta, pul, sana, rang
- Lug'at kaliti yoki to'plam elementi bo'lishi kerak
2.5. Avtomatik hash — dataclass va NamedTuple
dataclass uchta rejim:
from dataclasses import dataclass
@dataclass # eq=True, frozen=False (sukut)
class A:
x: int
# __eq__ ✅, __hash__ = None → hashlanmaydi
@dataclass(frozen=True) # ⭐ eng ko'p ishlatiladigan
class B:
x: int
# __eq__ ✅, __hash__ ✅ → hashlanadi
@dataclass(eq=False)
class C:
x: int
# __eq__ ❌ (sukut id), __hash__ ✅ (sukut id)
@dataclass(eq=True, unsafe_hash=True) # ⚠️ ehtiyot bo'ling
class D:
x: int
# __eq__ ✅, __hash__ ✅, lekin O'ZGARUVCHANJadval:
eq |
frozen |
unsafe_hash |
__hash__ |
|---|---|---|---|
True |
False |
False |
None (sukut) |
True |
True |
— | Yaratiladi |
False |
— | — | Meros (id) |
True |
False |
True |
Yaratiladi (xavfli) |
unsafe_hash=True nomi ogohlantiruvchi:
@dataclass(unsafe_hash=True)
class Xavfli:
x: int
o = Xavfli(1)
s = {o}
o.x = 2 # hash o'zgardi
o in s # False ← yashirin elementUni faqat maydonlar amalda o'zgarmasligini kafolatlay olsangiz ishlating.
NamedTuple — doim hashlanadi:
from typing import NamedTuple
class Nuqta(NamedTuple):
x: int
y: int
hash(Nuqta(1, 2)) # ✅ tuple hash'i
Nuqta(1, 2) == (1, 2) # ⚠️ True (6.6-dars)Solishtirish:
__eq__ |
__hash__ |
O'zgaruvchan | |
|---|---|---|---|
| Oddiy sinf | id |
id |
|
@dataclass |
Qiymat | None |
|
@dataclass(frozen=True) |
Qiymat | Qiymat | |
NamedTuple |
Qiymat (tuple) | Qiymat | |
@dataclass(unsafe_hash=True) |
Qiymat | Qiymat |
Tavsiya: kalit bo'ladigan qiymat obyektlari uchun @dataclass(frozen=True, slots=True) yoki NamedTuple.
2.6. Hash jadvali ichkarisi
Uyacha topish:
i = hash & (jadval_hajmi - 1) // hajm — 2 ning darajasiBitli AND — % dan ~10x tez.
Faqat pastki bitlar ishlatiladi:
hash(1) & 7 # 1
hash(9) & 7 # 1 ← to'qnashuv (8 uyachali jadvalda)
hash(17) & 7 # 1Shuning uchun hash funksiyasi bitlarni yaxshi aralashtirishi kerak.
To'qnashuv hal qilish — ochiq adreslash (open addressing):
CPython zanjir ishlatmaydi (bog'langan ro'yxat), balki keyingi uyachani qidiradi:
perturb = hash;
while (band) {
i = (i * 5 + 1 + perturb) & mask;
perturb >>= 5;
}Bu — tasodifiy probing: keyingi uyacha oldingi hash'ning yuqori bitlaridan hisoblanadi. Klasterlanishni kamaytiradi.
Nega zanjir emas:
- Bog'langan ro'yxat — har element uchun qo'shimcha xotira
- Kesh do'stligi yomon (xotirada sochilgan)
- Ochiq adreslash — ketma-ket xotira, protsessor keshi yaxshi ishlaydi
O'sish:
Yuklanish (elementlar / hajm) chegaradan oshsa, jadval kattalashadi va barcha elementlar qayta joylashtiriladi (rehashing).
| Tuzilma | Yuklanish chegarasi | O'sish |
|---|---|---|
dict |
2/3 | ×2 yoki ×4 |
set |
3/5 | ×4 (kichik), ×2 (katta) |
Rehashing — O(n). Lekin amortizatsiyalangan holda O(1) (6.2-dars mantiqi).
Hash keshlanadi:
s = "juda uzun satr" * 1000
hash(s) # birinchi marta — O(n)
hash(s) # keyin — O(1), keshdanstr obyekti hash'ni ichida saqlaydi. frozenset ham. tuple esa Python 3.14 dan boshlab saqlaydi — 3.13 gacha har safar qayta hisoblanardi (katta tuple'ni lug'at kaliti qilganda bu sezilardi).
Solishtirish tartibi:
Uyachada element topilganda:
if (yozuv->hash == qidirilayotgan_hash) // 1. hash solishtirish (arzon)
if (yozuv->key == key) // 2. identiklik (arzon)
return topildi;
if (PyObject_RichCompare(...)) // 3. __eq__ (qimmat)
return topildi;Uch bosqichli optimallashtirish. Shuning uchun:
- Hash to'qnashuvi kam bo'lsa —
__eq__deyarli chaqirilmaydi - Interning (4.11-dars) 2-bosqichda tezlik beradi
2.7. To'qnashuvlar va HashDoS
To'qnashuv — ikki turli obyekt bir xil hash.
hash(1) == hash(1.0) == hash(True) # True — ATAYLAB (teng qiymatlar)
# Tasodifiy to'qnashuvlar:
hash(-1) == hash(-2) # True — CPython nozikligi hash(-1) == -2: CPython da -1 xato belgisi sifatida band, shuning uchun hash(-1) -2 qaytaradi.
To'qnashuvlar narxi:
| Yuklanish | O'rtacha probing |
|---|---|
| 25% | ~1.15 |
| 50% | ~1.5 |
| 66% | ~2.0 |
| 90% | ~5.5 |
| 99% | ~50 |
Yuklanish 2/3 dan past bo'lsa — deyarli har doim birinchi urinishda topiladi.
HashDoS hujumi:
Hujumchi barchasi bir xil hash beradigan kalitlar yuboradi:
POST /api {"aaa": 1, "bbX": 2, "cYc": 3, ...} # 10000 ta to'qnashuvchi kalitServer ularni lug'atga qo'yadi → har qo'shish O(n) → jami O(n²) → server osiladi.
Himoya — hash randomizatsiyasi (PEP 456, Python 3.3+):
Har jarayon boshlanganda tasodifiy urug' (seed) tanlanadi. str va bytes hash'i shu urug'ga bog'liq:
$ python -c "print(hash('a'))"
-7028830851905903200
$ python -c "print(hash('a'))"
2543271982834501283Hujumchi kalitlarni oldindan hisoblay olmaydi.
Sonlar randomizatsiya qilinmaydi:
hash(42) == 42 # doimSabab: sonlar uchun hash qiymatning o'zi — bu tez va range kabi ketma-ket kalitlarni yaxshi taqsimlaydi. Son kalitlar bilan HashDoS ham mumkin, lekin ancha qiyin.
SipHash algoritmi:
Python 3.4+ da str/bytes uchun SipHash-1-3 ishlatiladi — kriptografik tarzda kuchli, lekin tez.
2.8. hash() va kriptografik hash — farq
Butunlay turli maqsadlar:
hash() |
hashlib.sha256() |
|
|---|---|---|
| Maqsad | Tez qidirish | Xavfsizlik, butunlik |
| Natija | 64-bit son | 256-bit bayt |
| Tezlik | ~50 ns | ~1 µs |
| Jarayonlar aro barqaror | (str) |
|
| Teskari qilib bo'lmaydi | Qisman | |
| To'qnashuvga chidamli |
hash() ni fayl imzosi, parol yoki ID sifatida ishlatmang:
# ❌ Har jarayonda boshqa
kesh_kaliti = hash(foydalanuvchi_email)
# ❌ Parol
saqlangan = hash(parol)
# ✅ Barqaror identifikator
import hashlib
kesh_kaliti = hashlib.sha256(email.encode()).hexdigest()
# ✅ Parol (20-qism)
import bcrypt
saqlangan = bcrypt.hashpw(parol.encode(), bcrypt.gensalt())hash() faqat bir jarayon ichida, xotirada ishlaydi.
3. Tez ma'lumotnoma
Hash talablari
1. Determinizm (bir jarayonda)
2. a == b → hash(a) == hash(b) ⭐ MAJBURIY
3. TezShartnoma
__eq__ yozdingiz → __hash__ AVTOMATIK None bo'ladi
(Python o'zi o'chiradi — himoya)
__hash__ faqat O'ZGARMAS maydonlardan hisoblansin
hash obyekt umri davomida O'ZGARMASINSinf turlari
class A: ... eq=id, hash=id ✅ hashlanadi
@dataclass eq=qiymat, hash=None ❌
@dataclass(frozen=True) eq=qiymat, hash=qiymat ✅ ⭐
@dataclass(eq=False) eq=id, hash=id ✅
@dataclass(unsafe_hash=True) eq=qiymat, hash=qiymat ⚠️ o'zgaruvchan
class X(NamedTuple) eq=tuple, hash=tuple ✅Hash jadvali
i = hash & (hajm - 1) hajm — 2 ning darajasi
To'qnashuv → ochiq adreslash (tasodifiy probing)
Yuklanish: dict 2/3, set 3/5 → o'sish + rehashing O(n)
Solishtirish: hash → is → __eq__ (arzondan qimmatga)
Hash keshlanadi: str ✅, frozenset ✅, tuple ✅ (3.14+; oldin ❌)Tuzoqlar
hash(-1) == -2 CPython nozikligi
hash(1)==hash(1.0)==hash(True) ataylab
hash((1,[2])) ❌ ichida list
str hash — har jarayonda BOSHQA (PEP 456, HashDoS)
hash(42) == 42 sonlar randomizatsiya QILINMAYDI
hash() ≠ hashlib xotira uchun, imzo uchun emas4. Batafsil misollar
Misol 1 — Hash asoslari
"""hash() nima qiladi va nima uchun kerak."""
import sys
print("=== 1. Turli turlar hash'i ===")
NAMUNALAR = [
("42", 42),
("-1", -1),
("0", 0),
("1", 1),
("1.0", 1.0),
("True", True),
("False", False),
("3.14", 3.14),
("'a'", "a"),
("(1, 2)", (1, 2)),
("frozenset([1, 2])", frozenset([1, 2])),
("None", None),
("b'ab'", b"ab"),
("[1, 2]", [1, 2]),
("{1, 2}", {1, 2}),
("{'a': 1}", {"a": 1}),
("(1, [2])", (1, [2])),
]
print(f" {'Obyekt':<22} {'hash()'}")
print(" " + "─" * 50)
for nom, o in NAMUNALAR:
try:
h = str(hash(o))
except TypeError as x:
h = f"❌ {x}"
print(f" {nom:<22} {h}")
print("""
⚠️ hash(-1) == -2 — CPython da -1 xato belgisi sifatida band
⚠️ hash(1) == hash(1.0) == hash(True) — ATAYLAB (teng qiymatlar)
⚠️ (1, [2]) — tuple o'zgarmas, lekin ICHIDA list bor
""")
print("\n=== 2. __hash__ = None ===")
TURLAR = [int, float, str, bytes, tuple, frozenset, type(None),
list, dict, set, bytearray]
print(f" {'Tur':<14} {'__hash__':<30} {'Hashlanadi'}")
print(" " + "─" * 58)
for t in TURLAR:
h = t.__hash__
holat = "✅" if h is not None else "❌"
korinish = "None" if h is None else str(h).split()[0].strip("<")
print(f" {t.__name__:<14} {korinish:<30} {holat}")
print("""
⭐ __hash__ = None — "bu tur hashlanmaydi" degan ANIQ signal.
Bu tasodif emas, ataylab qo'yilgan.
""")
print("\n=== 3. Nega o'zgaruvchan hashlanmaydi ===")
class SohtaHashlanadiganRoyxat(list):
"""⚠️ FAQAT NAMOYISH UCHUN — hech qachon yozmang!"""
def __hash__(self):
return hash(tuple(self))
r = SohtaHashlanadiganRoyxat([1, 2])
d = {r: "qiymat"}
print(f" r = {list(r)}, d = {{r: 'qiymat'}}")
print(f" hash(r) = {hash(r)}")
print(f" d[r] = {d[r]!r} ✅\n")
r.append(4)
print(f" r.append(4):")
print(f" r = {list(r)}")
print(f" hash(r) = {hash(r)} ← O'ZGARDI")
try:
print(f" d[r] = {d[r]!r}")
except KeyError:
print(f" d[r] → ❌ KeyError — obyekt lug'atda, lekin TOPILMAYDI")
print(f"\n len(d) = {len(d)} ← hali ham ichida!")
print(f" Iteratsiya bilan:")
for kalit, qiymat in d.items():
print(f" {list(kalit)} → {qiymat!r}")
try:
d[kalit]
except KeyError:
print(f" ⚠️ d[kalit] → KeyError — o'z kalitini topa olmaydi!")
print(f"""
⚠️ NOZIK HOLAT: r.append(3) qilsak, kalit TOPILIB qolardi!
8 uyali jadvalda hash((1, 2)) & 7 == hash((1, 2, 3)) & 7 == 3 —
yangi hash tasodifan ESKI uyaga tushadi, Python esa uyada avval
`is` bilan solishtiradi. Ya'ni xato har doim ham ko'rinmaydi —
aynan shuning uchun u juda xavfli.""")
print("""
⭐ Shuning uchun Python o'zgaruvchan obyektlarni
hashlashni TAQIQLAYDI. Bu — himoya, cheklov emas.
""")
print("\n=== 4. Hash uyachani belgilaydi ===")
def uyacha(x, hajm=8):
return hash(x) & (hajm - 1)
MEVALAR = ["olma", "anor", "behi", "uzum", "nok", "shaftoli"]
print(f" 8 uyachali jadvalda:\n")
print(f" {'Element':<12} {'hash()':>22} {'& 7':>5}")
print(" " + "─" * 44)
for m in MEVALAR:
print(f" {m:<12} {hash(m):>22} {uyacha(m):>5}")
jadval = [[] for _ in range(8)]
for m in MEVALAR:
jadval[uyacha(m)].append(m)
print(f"\n Jadval:")
for i, u in enumerate(jadval):
belgi = ", ".join(u) if u else "─"
ogoh = " ⚠️ TO'QNASHUV" if len(u) > 1 else ""
print(f" [{i}] {belgi}{ogoh}")
print("""
⭐ hash & (hajm-1) — faqat PASTKI bitlar ishlatiladi.
Shuning uchun hash funksiyasi bitlarni yaxshi
aralashtirishi kerak.
⚠️ Bu natija HAR ISHGA TUSHIRISHDA boshqa —
satrlar uchun hash randomizatsiyasi bor.
""")
print("\n=== 5. Sonlar uchun hash ===")
print(f" Kichik butun sonlar — hash = qiymatning o'zi:")
for x in [0, 1, 42, 1000, -2, -100]:
print(f" hash({x:>6}) = {hash(x):>6} {'✅ teng' if hash(x) == x else '⚠️ farqli'}")
print(f"\n Katta sonlar (modul olinadi):")
for x in [2**61 - 1, 2**61, 2**64, 10**20]:
print(f" hash({x:>24}) = {hash(x)}")
print(f"\n Float:")
for x in [1.0, 1.5, 2.0, 0.5, -1.5, float('inf')]:
print(f" hash({x:>6}) = {hash(x)}")
print(f"\n ⭐ hash(n) == hash(float(n)) butun float lar uchun:")
for x in [1, 2, 100]:
print(f" hash({x}) == hash({float(x)}): {hash(x) == hash(float(x))}")
print(f"\n Decimal va Fraction ham mos:")
from decimal import Decimal
from fractions import Fraction
print(f" hash(1) == hash(Decimal('1')): {hash(1) == hash(Decimal('1'))}")
print(f" hash(0.5) == hash(Fraction(1,2)): {hash(0.5) == hash(Fraction(1, 2))}")
print("""
⭐ Python sonli turlarni MOS hashlaydi:
1 == 1.0 == Decimal('1') == Fraction(1,1)
→ hash'lari ham teng → bitta lug'at kaliti
""")Natijaning muhim qismi:
=== 1. Turli turlar hash'i ===
Obyekt hash()
──────────────────────────────────────────────────
-1 -2
1 1
1.0 1
True 1
[1, 2] ❌ unhashable type: 'list'
(1, [2]) ❌ unhashable type: 'list'
=== 3. Nega o'zgaruvchan hashlanmaydi ===
r.append(4):
hash(r) = -4363729961677198915 ← O'ZGARDI
d[r] → ❌ KeyError — obyekt lug'atda, lekin TOPILMAYDI
len(d) = 1 ← hali ham ichida!
[1, 2, 4] → 'qiymat'
⚠️ d[kalit] → KeyError — o'z kalitini topa olmaydi!Nima ko'rsatdi: 2.1, 2.2, 2.6-bo'limlar.
Misol 2 — __hash__ / __eq__ shartnomasi
"""Uch qoida va ularni buzish oqibatlari."""
from dataclasses import dataclass
print("=== 1. Qoida 1: teng → teng hash ===")
class Buzuq:
"""❌ __eq__ qiymat bo'yicha, __hash__ id bo'yicha."""
def __init__(self, q):
self.q = q
def __eq__(self, b):
return isinstance(b, Buzuq) and self.q == b.q
def __hash__(self):
return id(self) # ⚠️ SHARTNOMA BUZILDI
class Togri:
"""✅ Ikkalasi ham bir xil maydondan."""
def __init__(self, q):
self.q = q
def __eq__(self, b):
return isinstance(b, Togri) and self.q == b.q
def __hash__(self):
return hash(self.q)
for sinf, nom in [(Buzuq, "Buzuq"), (Togri, "To'g'ri")]:
a, b = sinf(1), sinf(1)
print(f"\n {nom}:")
print(f" a == b: {a == b}")
print(f" hash(a) == hash(b): {hash(a) == hash(b)}")
d = {a: "birinchi"}
try:
natija = d[b]
print(f" d[b] (a bilan teng): {natija!r} ✅")
except KeyError:
print(f" d[b] (a bilan teng): ❌ KeyError")
s = {a, b}
print(f" len({{a, b}}): {len(s)} "
f"{'✅' if len(s) == 1 else '⚠️ teng obyektlar ikki marta!'}")
print("\n\n=== 2. Qoida 2: __eq__ → __hash__ = None ===")
class FaqatEq:
def __init__(self, q):
self.q = q
def __eq__(self, b):
return isinstance(b, FaqatEq) and self.q == b.q
print(f" class FaqatEq: __eq__ yozildi, __hash__ yozilmadi\n")
print(f" FaqatEq.__hash__ = {FaqatEq.__hash__}")
try:
{FaqatEq(1)}
except TypeError as x:
print(f" {{FaqatEq(1)}} → ❌ TypeError: {x}")
print("""
⭐ Python __hash__ ni AVTOMATIK None qildi.
Nega: sukut __hash__ id() asosida ishlaydi.
Agar __eq__ qiymat bo'yicha bo'lsa, sukut hash
1-QOIDANI BUZARDI — teng obyektlar turli hash olardi.
Python xavfsizlik uchun hashlashni O'CHIRADI.
Bu — eng foydali "himoya to'sig'i".
""")
print(" Qayta yoqish uchun:")
class EqVaHash:
def __init__(self, q):
self.q = q
def __eq__(self, b):
return isinstance(b, EqVaHash) and self.q == b.q
__hash__ = object.__hash__ # ⚠️ id bo'yicha — shartnoma buziladi!
class TogriEqVaHash:
def __init__(self, q):
self.q = q
def __eq__(self, b):
return isinstance(b, TogriEqVaHash) and self.q == b.q
def __hash__(self):
return hash(self.q) # ✅ bir xil maydondan
print(f" __hash__ = object.__hash__ ⚠️ shartnoma buziladi")
print(f" def __hash__: hash(self.q) ✅ to'g'ri")
a, b = EqVaHash(1), EqVaHash(1)
print(f"\n EqVaHash: a==b: {a == b}, len({{a,b}}): {len({a, b})} ⚠️")
c, e = TogriEqVaHash(1), TogriEqVaHash(1)
print(f" TogriEqVaHash: a==b: {c == e}, len({{a,b}}): {len({c, e})} ✅")
print("\n\n=== 3. Qoida 3: hash o'zgarmasin ===")
class Ozgaruvchan:
def __init__(self, q):
self.q = q
def __eq__(self, b):
return isinstance(b, Ozgaruvchan) and self.q == b.q
def __hash__(self):
return hash(self.q)
o = Ozgaruvchan(1)
s = {o}
print(f" o = Ozgaruvchan(1); s = {{o}}")
print(f" o in s: {o in s} ✅")
print(f" len(s): {len(s)}")
o.q = 2
print(f"\n o.q = 2 (hash o'zgardi):")
print(f" o in s: {o in s} ⚠️ FALSE!")
print(f" len(s): {len(s)} ← hali ham ichida")
print(f" list(s)[0] is o: {list(s)[0] is o} ← O'SHA obyekt!")
print(f"\n ⚠️ 'Yashirin element' — to'plamda bor, lekin topilmaydi.")
print("\n ✅ Yechim — faqat o'zgarmas maydondan hash:")
class Xodim:
"""ID bo'yicha tenglik — DB obyektlari uchun standart naqsh."""
def __init__(self, xodim_id: int, ism: str, maosh: int):
self._id = xodim_id # o'zgarmas
self.ism = ism # o'zgarishi mumkin
self.maosh = maosh # o'zgarishi mumkin
@property
def id(self):
return self._id
def __eq__(self, b):
return isinstance(b, Xodim) and self._id == b._id
def __hash__(self):
return hash(self._id)
def __repr__(self):
return f"Xodim({self._id}, {self.ism!r}, {self.maosh:,})"
x = Xodim(1, "Aziz", 7_000_000)
s = {x}
print(f"\n x = {x}")
print(f" x in s: {x in s}")
x.maosh = 8_000_000
x.ism = "Aziz Karimov"
print(f"\n x.maosh va x.ism o'zgardi:")
print(f" x = {x}")
print(f" x in s: {x in s} ✅ hali ham topiladi")
print(f"\n Bir xil ID — teng:")
x2 = Xodim(1, "Boshqa ism", 999)
print(f" x2 = {x2}")
print(f" x == x2: {x == x2}, x2 in s: {x2 in s} ✅")
print("\n\n=== 4. dataclass rejimlari ===")
@dataclass
class Sukut:
x: int
@dataclass(frozen=True)
class Muzlatilgan:
x: int
@dataclass(eq=False)
class EqSiz:
x: int
@dataclass(unsafe_hash=True)
class Xavfli:
x: int
from typing import NamedTuple
class NT(NamedTuple):
x: int
class Oddiy:
def __init__(self, x):
self.x = x
SINFLAR = [
("class Oddiy", Oddiy),
("@dataclass", Sukut),
("@dataclass(frozen=True)", Muzlatilgan),
("@dataclass(eq=False)", EqSiz),
("@dataclass(unsafe_hash=True)", Xavfli),
("NamedTuple", NT),
]
print(f" {'Sinf':<32} {'a==b':>7} {'hash':>8} {'o`zg.':>7}")
print(" " + "─" * 58)
for nom, sinf in SINFLAR:
a, b = sinf(1), sinf(1)
teng = a == b
try:
hash(a)
hashlanadi = "✅"
except TypeError:
hashlanadi = "❌"
try:
a.x = 2
ozgaruvchan = "✅"
except (AttributeError, TypeError):
ozgaruvchan = "❌"
print(f" {nom:<32} {str(teng):>7} {hashlanadi:>7} {ozgaruvchan:>7}")
print("""
⭐ @dataclass(frozen=True) — kalit bo'ladigan
qiymat obyektlari uchun ENG YAXSHI tanlov.
⚠️ unsafe_hash=True — nomi ogohlantiradi.
Faqat maydonlar amalda o'zgarmasligini
kafolatlay olsangiz ishlating.
""")
print(" unsafe_hash muammosi namoyishda:")
xv = Xavfli(1)
s = {xv}
print(f" xv = Xavfli(1); s = {{xv}}; xv in s: {xv in s}")
xv.x = 2
print(f" xv.x = 2; xv in s: {xv in s} ⚠️")
print("\n=== 5. Solishtirish tartibi ===")
class Kuzatuvchi:
"""__eq__ chaqirilishini kuzatadi."""
eq_chaqiruvlari = 0
def __init__(self, q):
self.q = q
def __eq__(self, b):
Kuzatuvchi.eq_chaqiruvlari += 1
return isinstance(b, Kuzatuvchi) and self.q == b.q
def __hash__(self):
return hash(self.q)
obyektlar = [Kuzatuvchi(i) for i in range(1000)]
s = set(obyektlar)
Kuzatuvchi.eq_chaqiruvlari = 0
for o in obyektlar:
_ = o in s
print(f" 1000 ta MAVJUD obyektni qidirish:")
print(f" __eq__ chaqirilishlari: {Kuzatuvchi.eq_chaqiruvlari}")
print(f" ⭐ 0 — chunki `is` tekshiruvi yetarli bo'ldi")
Kuzatuvchi.eq_chaqiruvlari = 0
for i in range(1000):
_ = Kuzatuvchi(i) in s # YANGI obyektlar
print(f"\n 1000 ta YANGI (lekin teng) obyektni qidirish:")
print(f" __eq__ chaqirilishlari: {Kuzatuvchi.eq_chaqiruvlari}")
print(f" ⭐ ~1000 — hash mos keldi, `is` mos kelmadi → __eq__")
Kuzatuvchi.eq_chaqiruvlari = 0
for i in range(2000, 3000):
_ = Kuzatuvchi(i) in s # YO'Q obyektlar
print(f"\n 1000 ta YO'Q obyektni qidirish:")
print(f" __eq__ chaqirilishlari: {Kuzatuvchi.eq_chaqiruvlari}")
print(f" ⭐ ~0 — hash mos kelmadi, __eq__ CHAQIRILMADI")
print("""
⭐ Uch bosqichli optimallashtirish:
1. hash solishtirish (arzon)
2. `is` tekshiruvi (arzon)
3. __eq__ chaqirish (qimmat)
Shuning uchun __eq__ sekin bo'lsa ham,
amalda kam chaqiriladi.
""")Natijaning muhim qismi:
=== 1. Qoida 1: teng → teng hash ===
Buzuq:
a == b: True
hash(a) == hash(b): False
d[b] (a bilan teng): ❌ KeyError
len({a, b}): 2 ⚠️ teng obyektlar ikki marta!
To'g'ri:
a == b: True
hash(a) == hash(b): True
d[b] (a bilan teng): 'birinchi' ✅
len({a, b}): 1 ✅
=== 4. dataclass rejimlari ===
Sinf a==b hash o`zg.
──────────────────────────────────────────────────────────
class Oddiy False ✅ ✅
@dataclass True ❌ ✅
@dataclass(frozen=True) True ✅ ❌
@dataclass(eq=False) False ✅ ✅
@dataclass(unsafe_hash=True) True ✅ ✅
NamedTuple True ✅ ❌
=== 5. Solishtirish tartibi ===
1000 ta MAVJUD obyektni qidirish:
__eq__ chaqirilishlari: 0
1000 ta YANGI (lekin teng) obyektni qidirish:
__eq__ chaqirilishlari: 1000
1000 ta YO'Q obyektni qidirish:
__eq__ chaqirilishlari: 0Nima ko'rsatdi: 2.3, 2.4, 2.5, 2.6-bo'limlar.
Misol 3 — To'qnashuvlar va tezlik
"""Hash sifati qanday qilib tezlikka ta'sir qiladi."""
import time
import sys
from collections import Counter
print("=== 1. Yomon hash narxi ===")
class YaxshiHash:
def __init__(self, q):
self.q = q
def __hash__(self):
return hash(self.q)
def __eq__(self, b):
return isinstance(b, YaxshiHash) and self.q == b.q
class OrtachaHash:
"""Faqat 100 xil hash qiymati."""
def __init__(self, q):
self.q = q
def __hash__(self):
return self.q % 100
def __eq__(self, b):
return isinstance(b, OrtachaHash) and self.q == b.q
class YomonHash:
"""Barcha obyekt uchun bir xil hash."""
def __init__(self, q):
self.q = q
def __hash__(self):
return 42
def __eq__(self, b):
return isinstance(b, YomonHash) and self.q == b.q
N = 3_000
print(f" {N:,} obyekt bilan:\n")
print(f" {'Hash sifati':<24} {'Qurish':>12} {'Qidirish':>12} {'Nisbat':>9}")
print(" " + "─" * 60)
natijalar = []
for sinf, nom in [(YaxshiHash, "Yaxshi (to'liq)"),
(OrtachaHash, "O'rtacha (100 xil)"),
(YomonHash, "Yomon (hammasi 42)")]:
obyektlar = [sinf(i) for i in range(N)]
boshlandi = time.perf_counter()
s = set(obyektlar)
qurish = time.perf_counter() - boshlandi
qidiruvlar = obyektlar[::10]
boshlandi = time.perf_counter()
for o in qidiruvlar:
_ = o in s
qidirish = time.perf_counter() - boshlandi
natijalar.append((nom, qurish, qidirish))
eng_tez = min(q for _, q, _ in natijalar)
for nom, qurish, qidirish in natijalar:
print(f" {nom:<24} {qurish * 1000:>9.1f} ms {qidirish * 1000:>9.1f} ms "
f"{qurish / eng_tez:>8.0f}x")
print("""
⭐ Yomon hash to'plamni RO'YXATGA aylantiradi: O(1) → O(n)
Qurish esa O(n) → O(n²)
""")
print("\n=== 2. Hash taqsimoti ===")
def taqsimot(elementlar, hajm=64):
uyachalar = Counter(hash(x) & (hajm - 1) for x in elementlar)
return uyachalar
TOPLAMLAR = [
("Ketma-ket sonlar", list(range(64))),
("Tasodifiy sonlar", [i * 7919 % 100003 for i in range(64)]),
("Qisqa satrlar", [f"k{i}" for i in range(64)]),
("Tuple lar", [(i, i * 2) for i in range(64)]),
("64 ning karralari", [i * 64 for i in range(64)]),
]
print(f" 64 uyachali jadvalda 64 element:\n")
print(f" {'Ma`lumot turi':<22} {'Band uyacha':>12} {'Max/uyacha':>12} {'Sifat'}")
print(" " + "─" * 60)
for nom, elementlar in TOPLAMLAR:
t = taqsimot(elementlar)
band = len(t)
eng_kop = max(t.values())
sifat = "✅" if band > 40 else ("⚠️" if band > 20 else "❌")
print(f" {nom:<22} {band:>12} {eng_kop:>12} {sifat:>6}")
print(f"\n '64 ning karralari' taqsimoti:")
t = taqsimot([i * 64 for i in range(64)])
for uyacha in sorted(t)[:8]:
print(f" [{uyacha:>2}] {'█' * t[uyacha]} {t[uyacha]}")
print("""
⚠️ i * 64 → hash = i * 64 → & 63 = 0 (hammasi!)
Sonlar uchun hash = qiymatning o'zi.
Agar kalitlar jadval hajmiga karrali bo'lsa —
hammasi bitta uyachaga tushadi.
⭐ CPython buni "perturbation" bilan yumshatadi:
probing yuqori bitlarni ham hisobga oladi.
""")
print("\n=== 3. Yuklanish va probing ===")
print(""" Nazariy o'rtacha probing soni (ochiq adreslash):
Yuklanish O'rtacha probing
─────────────────────────────
25% 1.2
50% 1.5
66% 2.0 ← dict chegarasi
75% 2.5
90% 5.5
95% 10.5
99% 50.5
⭐ CPython yuklanishni 2/3 dan past ushlab turadi (dict),
3/5 (set) — shuning uchun amalda ~1.5 probing.
""")
print(" Amaliy o'lchov — jadval o'sishi:")
d = {}
oldingi_hajm = sys.getsizeof(d)
osishlar = []
for i in range(200):
d[i] = i
hajm = sys.getsizeof(d)
if hajm != oldingi_hajm:
osishlar.append((len(d), hajm))
oldingi_hajm = hajm
print(f"\n {'Elementlar':>11} {'Hajm':>9} {'O`sish':>9}")
print(" " + "─" * 32)
oldingi = 0
for n, h in osishlar[:10]:
osish = f"{h / oldingi:.2f}x" if oldingi else "—"
print(f" {n:>11} {h:>9,} {osish:>9}")
oldingi = h
print("\n=== 4. Hash keshlash ===")
import timeit
UZUN_SATR = "x" * 100_000
UZUN_TUPLE = tuple(range(10_000))
UZUN_FROZENSET = frozenset(range(10_000))
# Birinchi hash
h1 = hash(UZUN_SATR)
h2 = hash(UZUN_TUPLE)
h3 = hash(UZUN_FROZENSET)
SINOVLAR = [
("str (100k belgi)", "hash(s)", {"s": UZUN_SATR}),
("tuple (10k element)", "hash(t)", {"t": UZUN_TUPLE}),
("frozenset (10k)", "hash(f)", {"f": UZUN_FROZENSET}),
]
print(f" Takroriy hash chaqiruvi:\n")
print(f" {'Tur':<24} {'ns/chaqiruv':>14} {'Keshlanadi'}")
print(" " + "─" * 52)
for nom, kod, muhit in SINOVLAR:
vaqt = timeit.timeit(kod, globals=muhit, number=100_000)
ns = vaqt / 100_000 * 1e9
keshlanadi = "✅" if ns < 100 else "❌"
print(f" {nom:<24} {ns:>12.1f} ns {keshlanadi:>10}")
print("""
⭐ str va frozenset hash'ni obyekt ichida SAQLAYDI.
tuple — Python 3.14 dan saqlaydi (3.13 gacha har safar qayta
hisoblardi; natijaga qarab o'z versiyangizni tekshiring).
⚠️ Katta tuple ni lug'at kaliti sifatida ko'p ishlatsangiz,
frozenset yoki oldindan hisoblangan kalit tejamliroq.
""")
print("\n=== 5. HashDoS namoyishi ===")
print(" Bir xil hash beradigan kalitlar bilan hujum:\n")
class HujumKaliti:
"""Barchasi bir xil hash — HashDoS simulyatsiyasi."""
def __init__(self, i):
self.i = i
def __hash__(self):
return 0
def __eq__(self, b):
return isinstance(b, HujumKaliti) and self.i == b.i
print(f" {'Kalitlar soni':>15} {'Normal':>12} {'Hujum':>12} {'Sekinlashuv':>13}")
print(" " + "─" * 56)
for n in [100, 500, 1000, 2000]:
normal = [f"kalit{i}" for i in range(n)]
hujum = [HujumKaliti(i) for i in range(n)]
boshlandi = time.perf_counter()
d1 = {k: 1 for k in normal}
vaqt_normal = time.perf_counter() - boshlandi
boshlandi = time.perf_counter()
d2 = {k: 1 for k in hujum}
vaqt_hujum = time.perf_counter() - boshlandi
print(f" {n:>15,} {vaqt_normal * 1000:>10.2f}ms "
f"{vaqt_hujum * 1000:>10.2f}ms {vaqt_hujum / vaqt_normal:>12.0f}x")
print("""
⭐ Kalitlar soni 2x oshsa, hujum vaqti 4x oshadi — O(n²).
Haqiqiy hujumda: JSON so'rovda 10 000 to'qnashuvchi kalit
→ server bir so'rovda bir necha soniya band bo'ladi
→ xizmatni rad etish (DoS).
⭐ Himoya: hash randomizatsiyasi (PEP 456).
Hujumchi str hash'ini oldindan hisoblay olmaydi.
""")
print(" Satr hash'i har jarayonda boshqa:")
import subprocess
for i in range(3):
try:
h = subprocess.run(
[sys.executable, "-c", "print(hash('hujum'))"],
capture_output=True, text=True, timeout=10
).stdout.strip()
print(f" {i + 1}. hash('hujum') = {h}")
except Exception:
print(f" {i + 1}. (o'lchab bo'lmadi)")
print(f"\n ⚠️ Sonlar randomizatsiya QILINMAYDI:")
print(f" hash(42) = {hash(42)} — har doim")Natijaning muhim qismi:
=== 1. Yomon hash narxi ===
3,000 obyekt bilan:
Hash sifati Qurish Qidirish Nisbat
────────────────────────────────────────────────────────────
Yaxshi (to'liq) 1.2 ms 0.3 ms 1x
O'rtacha (100 xil) 18.4 ms 4.1 ms 15x
Yomon (hammasi 42) 2841.2 ms 284.3 ms 2368x
=== 4. Hash keshlash ===
Tur ns/chaqiruv Keshlanadi
────────────────────────────────────────────────────
str (100k belgi) 41.2 ns ✅
tuple (10k element) 41.0 ns ✅
frozenset (10k) 38.1 ns ✅
=== 5. HashDoS namoyishi ===
Kalitlar soni Normal Hujum Sekinlashuv
────────────────────────────────────────────────────────
100 0.01ms 0.42ms 42x
500 0.04ms 8.91ms 223x
1,000 0.08ms 35.12ms 439x
2,000 0.16ms 141.83ms 886xNima ko'rsatdi: 2.6, 2.7-bo'limlar.
Misol 4 — Amaliy: to'g'ri hashlanadigan sinflar
"""Turli vazifalar uchun to'g'ri hash strategiyasi."""
from dataclasses import dataclass, field
from typing import NamedTuple
from decimal import Decimal
import hashlib
import json
print("=== 1. Qiymat obyekti (value object) ===")
@dataclass(frozen=True, slots=True)
class Pul:
"""Miqdor + valyuta. To'liq o'zgarmas."""
miqdor: Decimal
valyuta: str = "UZS"
def __post_init__(self):
if not isinstance(self.miqdor, Decimal):
object.__setattr__(self, "miqdor", Decimal(str(self.miqdor)))
object.__setattr__(self, "valyuta", self.valyuta.upper())
def __add__(self, b: "Pul") -> "Pul":
if self.valyuta != b.valyuta:
raise ValueError(f"Turli valyuta: {self.valyuta} + {b.valyuta}")
return Pul(self.miqdor + b.miqdor, self.valyuta)
def __str__(self):
return f"{self.miqdor:,.2f} {self.valyuta}"
NARXLAR = {
Pul(5000): "non",
Pul(12000): "sut",
Pul(5000, "uzs"): "non (takror)", # ⭐ bir xil kalit
Pul(100, "USD"): "import",
}
print(f" Lug'at ({len(NARXLAR)} kalit — 4 ta qo'shildi):")
for pul, nom in NARXLAR.items():
print(f" {str(pul):>18} {nom}")
print(f"\n ⭐ Pul(5000) va Pul(5000, 'uzs') — BIR XIL kalit")
print(f" hash teng: {hash(Pul(5000)) == hash(Pul(5000, 'uzs'))}")
print(f" teng: {Pul(5000) == Pul(5000, 'uzs')}")
s = {Pul(5000), Pul(5000), Pul(12000)}
print(f"\n To'plam: {len(s)} element (3 ta qo'shildi)")
try:
p = Pul(5000)
p.miqdor = Decimal(9999)
except Exception as x:
print(f"\n O'zgartirishga urinish: ❌ {type(x).__name__}")
print("\n\n=== 2. Identifikatsiya obyekti (entity) ===")
class Foydalanuvchi:
"""ID bo'yicha tenglik — maydonlar o'zgarishi mumkin."""
def __init__(self, foydalanuvchi_id: int, ism: str, email: str):
self._id = foydalanuvchi_id
self.ism = ism
self.email = email
self.oxirgi_kirish = None
@property
def id(self) -> int:
return self._id
def __eq__(self, b):
return isinstance(b, Foydalanuvchi) and self._id == b._id
def __hash__(self):
return hash(self._id) # ⭐ faqat o'zgarmas maydon
def __repr__(self):
return f"Foydalanuvchi({self._id}, {self.ism!r})"
f1 = Foydalanuvchi(1, "Aziz", "aziz@mail.uz")
f2 = Foydalanuvchi(1, "Aziz Karimov", "yangi@mail.uz") # bir xil ID
f3 = Foydalanuvchi(2, "Bobur", "bobur@mail.uz")
FAOL = {f1, f3}
print(f" f1 = {f1}")
print(f" f2 = {f2} ← bir xil ID, boshqa maydonlar")
print(f" f3 = {f3}\n")
print(f" f1 == f2: {f1 == f2} ✅ ID bir xil")
print(f" f2 in FAOL: {f2 in FAOL} ✅")
print(f" len(FAOL): {len(FAOL)}")
f1.ism = "Aziz Yangi"
f1.email = "boshqa@mail.uz"
print(f"\n f1 maydonlari o'zgardi: {f1}")
print(f" f1 in FAOL: {f1 in FAOL} ✅ hali ham topiladi")
print("""
⭐ Bu — ORM va DB obyektlari uchun STANDART naqsh:
• Tenglik va hash — faqat birlamchi kalit (id)
• Boshqa maydonlar erkin o'zgaradi
""")
print("\n\n=== 3. Kompozit kalit ===")
class KeshKaliti(NamedTuple):
"""Ko'p mezonli kesh kaliti."""
foydalanuvchi_id: int
resurs: str
versiya: int = 1
parametrlar: frozenset = frozenset() # ⭐ frozenset, dict emas
KESH: dict[KeshKaliti, str] = {}
def olish(foydalanuvchi_id, resurs, versiya=1, **parametrlar):
kalit = KeshKaliti(
foydalanuvchi_id, resurs, versiya,
frozenset(parametrlar.items()) # ⭐ tartibsiz, hashlanadi
)
if kalit in KESH:
return f"KESHDAN: {KESH[kalit]}"
natija = f"{resurs}#{foydalanuvchi_id}v{versiya}"
KESH[kalit] = natija
return f"HISOBLANDI: {natija}"
SOROVLAR = [
(1, "profil"),
(1, "profil"), # keshdan
(1, "profil", 2),
(2, "profil"),
(1, "sozlamalar", 1, {"til": "uz", "tema": "qora"}),
(1, "sozlamalar", 1, {"tema": "qora", "til": "uz"}), # ⭐ boshqa tartib → keshdan
]
print(f" {'So`rov':<44} Natija")
print(" " + "─" * 74)
for sorov in SOROVLAR:
if len(sorov) == 4:
fid, res, ver, par = sorov
natija = olish(fid, res, ver, **par)
korinish = f"olish({fid}, {res!r}, {ver}, **{par})"
else:
natija = olish(*sorov)
korinish = f"olish{sorov}"
print(f" {korinish:<44} {natija}")
print(f"\n Kesh: {len(KESH)} yozuv (6 so'rov)")
print("""
⭐ frozenset(kwargs.items()) — nomli argumentlar tartibidan
mustaqil kanonik kalit (6.9-dars).
""")
print("\n\n=== 4. Hash strategiyasi tanlash ===")
print("""
┌──────────────────────────┬─────────────────────────────────┐
│ Obyekt turi │ Strategiya │
├──────────────────────────┼─────────────────────────────────┤
│ Qiymat (nuqta, pul, rang)│ @dataclass(frozen=True) ⭐ │
│ Kichik qiymat │ NamedTuple ⭐ │
│ DB yozuvi (entity) │ hash(self.id) ⭐ │
│ Ulanish, fayl, oqim │ Sukut (id) — o'zgartirmang │
│ Konfiguratsiya │ @dataclass(frozen=True) │
│ Kesh kaliti │ NamedTuple + frozenset ⭐ │
│ O'zgaruvchan konteyner │ Hashlanmasin (__hash__ = None) │
└──────────────────────────┴─────────────────────────────────┘
""")
print("\n=== 5. hash() vs hashlib ===")
MALUMOT = {"foydalanuvchi": "aziz", "amal": "kirish", "vaqt": 1725782400}
print(f" Ma'lumot: {MALUMOT}\n")
print(f" hash() — xotira uchun:")
print(f" hash(tuple(sorted(MALUMOT.items()))) = "
f"{hash(tuple(sorted(MALUMOT.items())))}")
print(f" ⚠️ Har jarayonda BOSHQA (satrlar bor)")
matn = json.dumps(MALUMOT, sort_keys=True)
sha = hashlib.sha256(matn.encode()).hexdigest()
print(f"\n hashlib — barqaror imzo:")
print(f" sha256(...) = {sha[:32]}...")
print(f" ✅ Har doim, har joyda BIR XIL")
import time
N = 100_000
kalit = tuple(sorted(MALUMOT.items()))
boshlandi = time.perf_counter()
for _ in range(N):
hash(kalit)
vaqt_hash = time.perf_counter() - boshlandi
boshlandi = time.perf_counter()
for _ in range(N):
hashlib.sha256(matn.encode()).hexdigest()
vaqt_sha = time.perf_counter() - boshlandi
print(f"\n Tezlik ({N:,} chaqiruv):")
print(f" hash(): {vaqt_hash * 1000:>8.1f} ms "
f"({vaqt_hash / N * 1e9:.0f} ns/chaqiruv)")
print(f" hashlib.sha256(): {vaqt_sha * 1000:>8.1f} ms "
f"({vaqt_sha / N * 1e9:.0f} ns/chaqiruv)")
print(f" Farq: {vaqt_sha / vaqt_hash:>8.0f}x")
print("""
⭐ hash() → lug'at/to'plam, bir jarayon, xotirada
hashlib → fayl imzosi, ETag, kesh kaliti (diskda),
versiya identifikatori, butunlik tekshiruvi
⚠️ Parol uchun ikkalasi ham EMAS — bcrypt/argon2 (20-qism)
""")
print("\n=== 6. Xatolarni topish vositasi ===")
def hash_shartnomasini_tekshir(sinf, *namunalar):
"""Sinf hash shartnomasiga rioya qiladimi."""
muammolar = []
# Hashlanadimi
try:
hash(namunalar[0])
except TypeError:
return ["❌ Hashlanmaydi (__hash__ = None yoki yo'q)"]
# Qoida 1: teng → teng hash
for i, a in enumerate(namunalar):
for b in namunalar[i + 1:]:
if a == b and hash(a) != hash(b):
muammolar.append(
f"❌ QOIDA 1: {a!r} == {b!r}, lekin hash farqli"
)
# Qoida 3: hash barqarormi (o'zgaruvchan maydonlarni sinash)
o = namunalar[0]
eski_hash = hash(o)
ozgartirildi = False
for maydon in list(vars(o)) if hasattr(o, "__dict__") else []:
try:
eski_qiymat = getattr(o, maydon)
setattr(o, maydon, "O'ZGARTIRILDI")
ozgartirildi = True
if hash(o) != eski_hash:
muammolar.append(
f"⚠️ QOIDA 3: '{maydon}' o'zgarganda hash o'zgardi"
)
setattr(o, maydon, eski_qiymat)
except (AttributeError, TypeError):
pass
if not ozgartirildi and not muammolar:
muammolar.append("✅ O'zgarmas — hash barqaror")
return muammolar or ["✅ Shartnomaga rioya qilinadi"]
print(f" Tekshiruv natijalari:\n")
SINOVLAR = [
("Pul (frozen dataclass)",
Pul, [Pul(100), Pul(100), Pul(200)]),
("Foydalanuvchi (id hash)",
Foydalanuvchi, [Foydalanuvchi(1, "a", "a@x"), Foydalanuvchi(1, "b", "b@x")]),
("KeshKaliti (NamedTuple)",
KeshKaliti, [KeshKaliti(1, "a"), KeshKaliti(1, "a")]),
]
for nom, sinf, namunalar in SINOVLAR:
print(f" {nom}:")
for xabar in hash_shartnomasini_tekshir(sinf, *namunalar):
print(f" {xabar}")
print()
class BuzuqSinf:
def __init__(self, q):
self.q = q
def __eq__(self, b):
return isinstance(b, BuzuqSinf) and self.q == b.q
def __hash__(self):
return hash(self.q) # ⚠️ q o'zgaruvchan
print(f" BuzuqSinf (o'zgaruvchan maydondan hash):")
for xabar in hash_shartnomasini_tekshir(
BuzuqSinf, BuzuqSinf(1), BuzuqSinf(1)):
print(f" {xabar}")Natijaning muhim qismi:
=== 1. Qiymat obyekti (value object) ===
Lug'at (3 kalit — 4 ta qo'shildi):
5,000.00 UZS non (takror)
12,000.00 UZS sut
100.00 USD import
⭐ Pul(5000) va Pul(5000, 'uzs') — BIR XIL kalit
=== 3. Kompozit kalit ===
So`rov Natija
──────────────────────────────────────────────────────────────────────────
olish(1, 'profil') HISOBLANDI: profil#1v1
olish(1, 'profil') KESHDAN: profil#1v1
olish(1, 'sozlamalar', 1, **{'til': ...}) HISOBLANDI: sozlamalar#1v1
olish(1, 'sozlamalar', 1, **{'tema': ...}) KESHDAN: sozlamalar#1v1
=== 6. Xatolarni topish vositasi ===
BuzuqSinf (o'zgaruvchan maydondan hash):
⚠️ QOIDA 3: 'q' o'zgarganda hash o'zgardiNima ko'rsatdi: 2.3, 2.5, 2.8-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
"hash(a) == hash(b) → a == b" |
To'qnashuv mumkin |
"__eq__ yozsam yetarli" |
__hash__ avtomatik None bo'ladi |
"Sukut __hash__ har doim to'g'ri" |
__eq__ o'zgartirilsa — shartnoma buziladi |
"@dataclass hashlanadi" |
frozen=True kerak |
| "O'zgarmas tuple doim hashlanadi" | Ichida list bo'lsa yo'q |
"hash() xavfsiz identifikator" |
Har jarayonda boshqa (str uchun) |
"hash(-1) == -1" |
-2 — CPython nozikligi |
| "To'qnashuv — nazariy muammo" | HashDoS — real hujum vektori |
"tuple hash keshlanadi" |
str va frozenset — ha, tuple — yo'q |
6. Keng tarqalgan xatolar va yechimlari
1. Faqat __eq__ yozish
class A:
def __eq__(self, b): ... # ❌ hashlanmaydi
class A:
def __eq__(self, b): ...
def __hash__(self): # ✅
return hash(self.kalit)2. __hash__ va __eq__ turli maydonlardan
def __eq__(self, b): return self.x == b.x
def __hash__(self): return hash(self.y) # ❌ SHARTNOMA BUZILDI
def __hash__(self): return hash(self.x) # ✅ bir xil maydon3. O'zgaruvchan maydondan hash
def __hash__(self):
return hash(self.maosh) # ⚠️ o'zgarsa yashirin element
def __hash__(self):
return hash(self._id) # ✅ o'zgarmas4. @dataclass ni kalit sifatida
@dataclass # ❌ hashlanmaydi
class Nuqta: x: int
@dataclass(frozen=True) # ✅
class Nuqta: x: int5. unsafe_hash=True ni o'ylamasdan
@dataclass(unsafe_hash=True) # ⚠️ nomi ogohlantiradi
class A: x: int
@dataclass(frozen=True) # ✅
class A: x: int6. hash() ni barqaror ID sifatida
kesh_kaliti = hash(email) # ❌ jarayonlar aro barqaror emas
kesh_kaliti = hashlib.sha256(email.encode()).hexdigest() # ✅7. Tuple ichida o'zgaruvchan element
kalit = (1, [2, 3]) # ❌ unhashable
kalit = (1, (2, 3)) # ✅
kalit = (1, frozenset([2, 3])) # ✅8. __eq__ da isinstance tekshirmaslik
def __eq__(self, b):
return self.x == b.x # ❌ AttributeError boshqa tur bilan
def __eq__(self, b):
return isinstance(b, Nuqta) and self.x == b.x # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 6.7, 6.10-darslar (o'tilgan):
setvadict— hash jadvali - 6.5, 6.9-darslar (o'tilgan):
tuple,frozensethashlanishi - 4.11-dars (o'tilgan): satr interning va hash keshi
- 8-qism:
__eq__,__hash__,dataclass,__slots__ - 10-qism:
functools.lru_cache— hashlanadigan argumentlar - 15, 20-qismlar: kriptografiya,
hashlib, parollar - 23-qism: ORM, birlamchi kalit, obyekt identifikatsiyasi
8. Eng yaxshi amaliyotlar
__eq__va__hash__— birga va bir xil maydonlardan. Bu — buzilmaydigan qoida.Hash faqat o'zgarmas maydonlardan. DB obyektlari uchun
hash(self.id).Qiymat obyektlari —
@dataclass(frozen=True, slots=True). YokiNamedTuple.unsafe_hash=Truedan qoching. Nomi bejiz emas.__eq__daisinstancetekshiring. Aks holda boshqa tur bilanAttributeError.Barqaror identifikator uchun
hashlib.hash()— faqat xotirada, bir jarayonda.O'zgaruvchan konteyner hashlanmasin. Sukut xatti-harakat to'g'ri — buzmang.
Kompozit kalit —
NamedTupleyokifrozenset. Tartib muhim bo'lsa birinchisi, muhim bo'lmasa ikkinchisi.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
1. print(hash(-1))
2. print(hash(1) == hash(1.0) == hash(True))
3. print(list.__hash__)
4. print(hash((1, (2, 3))))
5. hash((1, [2]))
6. class A:
def __eq__(self, b): return True
print(A.__hash__)
7. from dataclasses import dataclass
@dataclass
class B: x: int
hash(B(1))
8. @dataclass(frozen=True)
class C: x: int
print(hash(C(1)) == hash(C(1)))
9. print(hash(frozenset([1,2])) == hash(frozenset([2,1])))
10. print(hash((1,2)) == hash((2,1)))
11. class D:
def __init__(s, x): s.x = x
print(D(1) == D(1))
12. print(hash(42) == 42)Javoblar
-2— CPython nozikligiTrue— teng qiymatlarNone— hashlanmaydi- Biror son — ichma-ich tuple hashlanadi
-
TypeError— ichida list None— Python o'chirdi-
TypeError—@dataclass__hash__ = None True—frozen=Truehash yaratadiTrue— tartib muhim emasFalse— tuple da tartib muhimFalse— sukut__eq__idbo'yichaTrue— kichik butun sonlar
Vazifa 2: Xatolarni tuzating
1. class A:
def __eq__(self, b): return self.x == b.x
2. def __eq__(self, b): return self.id == b.id
def __hash__(self): return hash(self.ism)
3. @dataclass
class Nuqta: x: int
d = {Nuqta(1): "a"}
4. def __hash__(self): return hash(self.maosh) # maosh o'zgaradi
5. kalit = (foydalanuvchi_id, ruxsatlar_royxati)
6. kesh_id = hash(email) # DB da saqlanadi
7. def __eq__(self, b): return self.x == b.x # b boshqa tur bo'lishi mumkin
8. @dataclass(unsafe_hash=True)
class Buyurtma: mahsulotlar: listJavoblar
1. def __hash__(self): return hash(self.x) # qo'shing
2. def __hash__(self): return hash(self.id) # bir xil maydon
3. @dataclass(frozen=True)
4. def __hash__(self): return hash(self._id)
5. kalit = (foydalanuvchi_id, frozenset(ruxsatlar_royxati))
6. kesh_id = hashlib.sha256(email.encode()).hexdigest()
7. return isinstance(b, Nuqta) and self.x == b.x
8. @dataclass(frozen=True)
class Buyurtma: mahsulotlar: tupleVazifa 3: Hash tekshiruvchi
hash_shartnomasini_tekshir ni kengaytiring:
- Uchala qoidani ham avtomatik tekshirsin
dataclassmaydonlarini aniqlasin- Ichma-ich obyektlarni tekshirsin
- Hash taqsimotini baholasin (100 namuna bilan)
- Tavsiyalar bersin (
frozen=Trueqo'shing va h.k.) pytesttesti sifatida ishlasin
Vazifa 4: Hash jadvali
Noldan hash jadvali yozing:
qoy(k, v),ol(k),ochir(k),__len__,__contains__- Ochiq adreslash (chiziqli va tasodifiy probing)
- Yuklanish 2/3 dan oshsa o'sish
TOMBSTONEbilan o'chirish- Statistika: to'qnashuvlar, o'rtacha probing, yuklanish
- Zanjir usuli bilan solishtiring
Vazifa 5: Qiymat obyektlari kutubxonasi
@dataclass(frozen=True) bilan yozing:
Pul(miqdor, valyuta)—+,-,*, taqqoslashOraliq(boshi, oxiri)— kesishish, birlashma,inVersiya(major, minor, patch)— taqqoslash,>, parsingRang(r, g, b)— hex, aralashtirish, masofa- Har biri hashlanadigan va lug'at kaliti bo'la olsin
- Shartnoma testlari
Vazifa 6: HashDoS tadqiqoti
- Berilgan Python versiyasi uchun to'qnashuvchi satrlar toping (
PYTHONHASHSEED=0bilan) - n = 100…5000 uchun sekinlashuvni o'lchang
- Grafik chizing (O(n) vs O(n²))
dict,set,Counteruchun taqqoslang- Himoya usullarini sinang (kalitlar sonini cheklash,
frozensetkaliti)
Vazifa 7: O'ylash
Nega Python __eq__ aniqlanganda __hash__ ni avtomatik None qiladi, __eq__ bilan mos hash avtomatik yaratmaydi?
Javob
Chunki Python __eq__ nimaga qarashini bilmaydi.
Avtomatik hash yaratish uchun nima kerak:
def __eq__(self, b):
return self.x == b.x # faqat xPython bu kodni tahlil qilib, "hash self.x dan hisoblanishi kerak" degan xulosaga kelishi kerak edi. Bu — statik tahlil, va u umumiy holda imkonsiz:
def __eq__(self, b):
if self.turi == "A":
return self.x == b.x
return self.y == b.y and self.z != b.z # qaysi maydondan hash?
def __eq__(self, b):
return self.hisobla() == b.hisobla() # metod natijasi
def __eq__(self, b):
return abs(self.x - b.x) < 0.001 # ⚠️ tranzitiv EMASOxirgi misol ayniqsa muhim: taxminiy tenglik uchun to'g'ri hash mavjud emas. a ≈ b va b ≈ c bo'lsa ham a ≉ c bo'lishi mumkin — hash bunday munosabatni ifodalay olmaydi.
Nega None qilish to'g'ri:
Uchta variant bor edi:
1. Hech narsa qilmaslik (sukut hash qolsin) —
class A:
def __eq__(self, b): return self.x == b.x
# __hash__ meros — id() asosida
a, b = A(1), A(1)
a == b # True
{a, b} # ⚠️ IKKI element — jimgina noto'g'ri
d = {a: 1}; d[b] # ⚠️ KeyError — jimgina noto'g'riBu — jim buzilish. Kod ishlaydi, testlar o'tadi, keyin ishlab chiqarishda g'alati xatolar chiqadi. Eng yomon variant.
2. Avtomatik hash yaratish — imkonsiz (yuqorida).
3. __hash__ = None —
{a, b} # ❌ TypeError: unhashable type: 'A'Darhol, aniq xato. Dasturchi qaror qabul qilishi kerak:
- Hashlanmasin (o'zgaruvchan konteyner) → hech narsa qilmang
- Hashlansin →
__hash__yozing, va__eq__bilan mos qilishga majbursiz
Bu — "aniq yashirinlikdan yaxshi" (Zen) ning namunasi.
Python 2 da qanday edi:
Python 2 da __hash__ avtomatik o'chirilmasdi:
# Python 2
class A(object):
def __eq__(self, b): return self.x == b.x
a, b = A(1), A(1)
a == b # True
{a, b} # ⚠️ 2 element — jim xatoBu — real xatolar manbai edi. Python 3 da ataylab o'zgartirildi (bu — orqaga moslikni buzuvchi o'zgarishlardan biri).
dataclass qanday hal qiladi:
dataclass maydonlarni biladi — chunki ular e'lon qilingan:
@dataclass(frozen=True)
class A:
x: int
y: int
# __eq__: (self.x, self.y) == (b.x, b.y)
# __hash__: hash((self.x, self.y)) ← bir xil maydonlardanShuning uchun dataclass avtomatik hash yarata oladi — u qo'lda yozilgan __eq__ ni tahlil qilmaydi, balki ikkalasini ham o'zi yaratadi.
Va frozen=True talab qilinadi, chunki 3-qoida (hash barqarorligi) faqat o'zgarmas obyektda kafolatlanadi.
Xulosa: Python "sehr qilishdan" bosh tortdi va o'rniga noto'g'ri holatni imkonsiz qildi. Bu — API dizaynining yaxshi namunasi: agar to'g'ri javobni bilib bo'lmasa, xato ber, taxmin qilma.
Nimani mustahkamlaydi: 2.2, 2.3, 2.5-bo'limlar.
Xulosa
Bu darsda hashni chuqur o'rgandik.
Eng muhim uch fikr:
Hash — obyektning xotiradagi "manzili".
hash(x) & (hajm-1)uyachani beradi, shuning uchun qidirish O(1). Obyekt o'zgarsa hash o'zgaradi va u "yo'qolib qoladi" — shuning uchun o'zgaruvchan obyektlar hashlanmaydi.__eq__va__hash__— bir shartnomaning ikki qismi.a == b→hash(a) == hash(b), ikkalasi bir xil maydonlardan, va hash obyekt umri davomida o'zgarmasin.__eq__yozsangiz Python__hash__ni avtomatikNoneqiladi — bu himoya, cheklov emas.To'g'ri tanlov:
@dataclass(frozen=True)yokihash(self.id). Qiymat obyektlari uchun birinchisi, ma'lumotlar bazasi obyektlari uchun ikkinchisi.unsafe_hash=True— nomi bejiz emas.
Keyingi darsda ichma-ich tuzilmalar ni ko'ramiz: ro'yxat ichida lug'at, lug'at ichida ro'yxat — JSON kabi murakkab ma'lumotlar bilan xavfsiz ishlash.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!