Mundarija (20)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Uch xil mexanizm
- 2.2. Kichik butun sonlar
- 2.3. Satrlar interningi
- 2.4. Kompilyator: oldindan hisoblash va birlashtirish
- 2.5. Boshqa keshlar
- 2.6. sys.intern qachon foydali
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Kesh va interning qoidalarini tekshirish
- Misol 2 — Kompilyator: oldindan hisoblash va birlashtirish
- Misol 3 — sys.intern va lug'at qidiruvi
- Misol 4 — Amaliy: log tahlilida xotirani tejash
- 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
11.7-dars: Interning — kichik sonlar va satrlar
11-QISM — OBYEKT MODELI VA XOTIRA · 7-dars
1. Kirish va motivatsiya
3.1-darsda "kichik sonlar tuzog'i"ni ko'rdik: a = 256; b = 256; a is b → True, 257 bilan esa har doim ham emas. 11.2-darsda is ni qiymatlar uchun ishlatmaslik kerakligini, 999 + 1 is 1000 esa "tasodifan" True bo'lishini ko'rdik. Endi shu "tasodif" larning qoidalarini o'rganamiz.
Real vaziyat. Log tahlil servisi har kuni 50 million qator o'qiydi. Har qatorda shahar, qurilma, holat kabi maydonlar bor — ular faqat bir necha o'nlab xil qiymatga ega. Lekin xotirada 50 million alohida "Toshkent" satri yotibdi: har line.split(",") yangi satr obyekti yaratadi. Xizmat 18 GB xotira talab qiladi.
Bitta qator o'zgartirildi:
shahar = sys.intern(maydonlar[3])Xotira 3 GB ga tushdi: endi bitta "Toshkent" obyekti va 50 million havola.
CPython bir xil qiymatli obyektlarni ba'zi hollarda avtomatik bitta nusxaga keltiradi (interning, keshlash, konstantalarni birlashtirish), ba'zi hollarda esa yo'q. Qoidalarni bilish ikki narsa beradi: is bilan bog'liq "sirli" natijalarni tushuntirish va katta ma'lumotda xotirani ongli ravishda tejash.
Bu darsda:
- Kichik butun sonlar keshi:
-5 ... 256 - Satrlar interningi: kompilyatsiya konstantalari va ish vaqtidagi satrlar
- Kompilyator: konstantalarni oldindan hisoblash va birlashtirish (4096 chegarasi)
- Bitta belgili satrlar, bo'sh kortej, bitta baytli
byteskeshlari sys.intern: qachon foydali, qanday narxi bor- Atribut nomlari va lug'at kalitlari — kim avtomatik interning qilinadi
2. Nazariya — chuqur tushuntirish
2.1. Uch xil mexanizm
"Bir xil qiymat — bitta obyekt" natijasini uch xil mexanizm beradi:
| Mexanizm | Qachon | Misol |
|---|---|---|
| Kesh (oldindan yaratilgan obyektlar) | Interpretator ishga tushganda | Kichik sonlar, bitta belgili satrlar, (), b"a" |
| Interning (satrlar jadvali) | Kompilyatsiyada yoki sys.intern |
Identifikatorga o'xshash konstantalar, atribut nomlari |
| Konstantalarni birlashtirish | Bir faylni kompilyatsiya qilganda | Bir modulda ikki marta yozilgan "salom dunyo!" |
Bularning hech biri til kafolati emas — CPython implementatsiyasi va versiyasiga bog'liq optimallashtirishlar. Kodning to'g'riligi ularga tayanmasligi kerak.
2.2. Kichik butun sonlar
CPython -5 dan 256 gacha bo'lgan butun sonlarni ishga tushishda yaratib qo'yadi. Shu oraliqdagi har qanday int qiymati — literal, int("100"), 50 + 50, len(royxat) — o'sha tayyor obyektni qaytaradi. 3.12 dan ular o'lmas (11.1-dars).
| Qiymat | a is b (ish vaqtida hisoblangan) |
|---|---|
-5 ... 256 |
Har doim True |
257 va katta, -6 va kichik |
Odatda False |
| Bir faylda yozilgan bir xil literal | Ko'pincha True — konstantalar birlashtirilgan (2.4) |
Nega aynan shu oraliq: ular massiv indekslari, sanoq va bayt qiymatlari sifatida eng ko'p ishlatiladi.
2.3. Satrlar interningi
Interned satr — interpretatorning maxsus jadvalida saqlanadigan, qiymati bo'yicha yagona satr obyekti.
| Satr qayerdan | Interning qilinadimi |
|---|---|
Kodda yozilgan identifikatorga o'xshash konstanta ("salom", "ism_familiya") |
kompilyatsiyada |
| Nomlar: o'zgaruvchi, funksiya, atribut, parametr nomlari | |
setattr(obj, nom, ...), getattr dagi atribut nomi |
|
Bitta belgili latin-1 satrlar (chr(97)) |
kesh |
Ish vaqtida yaratilgan satr: input(), split(), join, f-string, fayl, JSON, tarmoq |
|
Oddiy lug'at kaliti ({"kalit": 1} ish vaqtida) |
|
sys.intern(s) natijasi |
sys.intern(s):
- jadvalda
sga teng satr bo'lsa — o'shani qaytaradi - bo'lmasa —
sni jadvalga qo'shib, uni qaytaradi - faqat
strqabul qiladi (bytesemas)
Interned satrlar Python 3.14 da o'lmas emas — hech kim ishlatmasa, jadvaldan tushib, xotiradan o'chiriladi.
2.4. Kompilyator: oldindan hisoblash va birlashtirish
Oldindan hisoblash (constant folding). Kompilyator konstantalardan iborat ifodalarni oldindan hisoblaydi:
"ab" + "cd" # → "abcd" konstanta
"-" * 20 # → 20 ta "-" konstanta
60 * 60 * 24 # → 86400Satr va ketma-ketliklar uchun natija 4096 elementdan oshmasa. "a" * 5000 — ish vaqtida hisoblanadi.
Birlashtirish. Bir faylni (modulni) kompilyatsiya qilganda bir xil konstantalar bitta obyektga keltiriladi — hatto turli funksiyalarda bo'lsa ham:
def f(): return "bir xil satr!"
def g(): return "bir xil satr!"
f() is g() # True — bitta faylda REPL da har buyruq alohida kompilyatsiya qilinadi — birlashtirish ishlamaydi. Shuning uchun bir kod faylda True, REPL da False berishi mumkin.
2.5. Boshqa keshlar
| Obyekt | Kesh |
|---|---|
() |
Bo'sh kortej — yagona |
"" |
Bo'sh satr — yagona |
| Bitta belgili latin-1 satr | chr(0) ... chr(255) |
b"", bitta baytli bytes |
Yagona nusxalar |
frozenset() |
yagona emas (6.9-dars) |
float, katta int, [], {} |
kesh yo'q |
2.6. sys.intern qachon foydali
Xotira. Ko'p takrorlanuvchi, ish vaqtida yaratiladigan satrlar: CSV/log ustunlari (shahar, holat, qurilma), JSON kalitlari, teglar, enum-ga o'xshash qiymatlar. Million nusxa o'rniga bitta obyekt.
Tezlik. Lug'at qidiruvi avval is ni tekshiradi (11.2-dars): kalitlar interned bo'lsa, xesh mos kelganda satrlarni belgima-belgi solishtirish shart emas. Bu atribut va global nomlar qidiruvini tez qiladi — lekin oddiy kodda farq odatda sezilmaydi.
Narxi:
sys.internchaqiruvining o'zi — jadval qidiruvi- Kam takrorlanuvchi, noyob satrlar uchun foyda yo'q, faqat narx
- Juda katta jadval ham xotira oladi
Qoida: qiymatlar soni kam, nusxalar soni ko'p bo'lganda interning qiling. Tezlik uchun emas, avvalo xotira uchun — va o'lchab.
3. Tez ma'lumotnoma
Kichik sonlar
-5 ... 256 → har doim bitta obyekt (o'lmas)
257+, -6- → ish vaqtida hisoblangan — alohidaSatrlar
kod konstantasi (identifikatorga o'xshash) → interned
nomlar, setattr/getattr nomi → interned
split, join, f-string, input, json, fayl → yo'q
sys.intern(s) → jadvaldagi yagona nusxa
3.14: interned satrlar o'lmas emasKompilyator
"ab" + "cd", "-" * 20 → oldindan hisoblangan konstanta (≤ 4096 element)
bir fayldagi bir xil konstantalar → bitta obyekt
REPL → har buyruq alohidaKeshlar
() "" chr(0..255) b"" bytes([n]) → yagona
frozenset(), float, [] {} → yo'qQoida
qiymat taqqoslash → == (hech qachon is)
sys.intern → kam noyob qiymat × ko'p nusxa → xotira4. Batafsil misollar
Misol 1 — Kesh va interning qoidalarini tekshirish
"""Kichik sonlar chegarasi, kompilyatsiya konstantalari, ish vaqtidagi satrlar, sys.intern va boshqa keshlar."""
import sys
def ish_vaqtida(qiymat):
"""Kompilyator oldindan ko'ra olmaydigan yo'l bilan yangi obyekt yasaydi."""
return type(qiymat)(str(qiymat)) if isinstance(qiymat, int) else "".join(list(qiymat))
print("=== 1. Kichik sonlar keshi ===")
chegaralar = [-7, -6, -5, 0, 100, 255, 256, 257, 1000]
qator = []
for n in chegaralar:
qator.append(f"{n}:{'✓' if ish_vaqtida(n) is ish_vaqtida(n) else '✗'}")
print(f" ish vaqtida ikki marta yaratilgan son bitta obyektmi: {' '.join(qator)}")
print(f" 257 o'lmasmi: {sys._is_immortal(ish_vaqtida(257))}, 256: {sys._is_immortal(256)}")
print("\n=== 2. Satrlar: konstanta, ish vaqtida, sys.intern ===")
KONSTANTA = "salom"
yasalgan = ish_vaqtida("salom")
print(f" kod konstantasi interned: {sys._is_interned(KONSTANTA)}")
print(f" join bilan yasalgan: teng {yasalgan == KONSTANTA}, bitta obyekt {yasalgan is KONSTANTA}, interned {sys._is_interned(yasalgan)}")
interned = sys.intern(yasalgan)
print(f" sys.intern(yasalgan): konstanta bilan bitta obyekt {interned is KONSTANTA}")
yangi = ish_vaqtida("mutlaqo yangi qiymat 42")
print(f" jadvalda yo'q satr: sys.intern o'sha obyektni qaytardi {sys.intern(yangi) is yangi}, endi interned {sys._is_interned(yangi)}")
print("\n=== 3. Qayerdan kelgan satr interned ===")
import json
class Obyekt:
pass
o = Obyekt()
setattr(o, ish_vaqtida("dinamik_atribut"), 1)
manbalar = {
"split natijasi": "Toshkent,Samarqand".split(",")[0],
"f-string": f"{KONSTANTA} dunyo",
"json kaliti": next(iter(json.loads('{"shahar": 1}'))),
"lug'at kaliti (ish vaqtida)": next(iter({ish_vaqtida("kalit"): 1})),
"setattr nomi": next(iter(vars(o))),
"chr(65)": chr(65),
"str(7)": str(7),
}
for tavsif, s in manbalar.items():
print(f" {tavsif:28} {s!r:22} interned: {sys._is_interned(s)}")
print("\n=== 4. Boshqa keshlar ===")
for tavsif, a, b in [("() va tuple([])", (), tuple([])), ("'' va ''.join([])", "", "".join([])),
("chr(97) va 'a'", chr(97), "a"), ("bytes([97]) va b'a'", bytes([97]), b"a"),
("frozenset() ikki marta", frozenset(), frozenset()),
("float('1.5') ikki marta", float("1.5"), float("1.5"))]:
print(f" {tavsif:26} bitta obyekt: {a is b}")Natijaning muhim qismi:
=== 1. Kichik sonlar keshi ===
ish vaqtida ikki marta yaratilgan son bitta obyektmi: -7:✗ -6:✗ -5:✓ 0:✓ 100:✓ 255:✓ 256:✓ 257:✗ 1000:✗
257 o'lmasmi: False, 256: True
=== 2. Satrlar: konstanta, ish vaqtida, sys.intern ===
kod konstantasi interned: True
join bilan yasalgan: teng True, bitta obyekt False, interned False
sys.intern(yasalgan): konstanta bilan bitta obyekt True
jadvalda yo'q satr: sys.intern o'sha obyektni qaytardi True, endi interned True
=== 3. Qayerdan kelgan satr interned ===
split natijasi 'Toshkent' interned: False
f-string 'salom dunyo' interned: False
json kaliti 'shahar' interned: False
lug'at kaliti (ish vaqtida) 'kalit' interned: False
setattr nomi 'dinamik_atribut' interned: True
chr(65) 'A' interned: True
str(7) '7' interned: False
=== 4. Boshqa keshlar ===
() va tuple([]) bitta obyekt: True
'' va ''.join([]) bitta obyekt: True
chr(97) va 'a' bitta obyekt: True
bytes([97]) va b'a' bitta obyekt: True
frozenset() ikki marta bitta obyekt: False
float('1.5') ikki marta bitta obyekt: FalseNima ko'rsatdi: 2.2, 2.3, 2.5-bo'limlar.
Misol 2 — Kompilyator: oldindan hisoblash va birlashtirish
"""co_consts da nima bor: ifodalar oldindan hisoblangan; 4096 chegarasi; bir fayldagi konstantalar birlashadi; REPL farqi."""
import dis
def konstantalar(funksiya):
return [c for c in funksiya.__code__.co_consts if not (c is None or isinstance(c, type(konstantalar.__code__)))]
def hisoblangan():
kun = 60 * 60 * 24
chiziq = "-" * 30
salom = "sal" + "om"
uzun = "a" * 5000
return kun, chiziq, salom, uzun
print("=== 1. co_consts: nima oldindan hisoblangan ===")
konst = konstantalar(hisoblangan)
for tavsif, qiymat in [("60 * 60 * 24", 86400), ('"-" * 30', "-" * 30), ('"sal" + "om"', "salom"), ('"a" * 5000', "a" * 5000)]:
print(f" {tavsif:14} natijasi konstantalar ichida: {qiymat in konst}")
amallar = [i.opname for i in dis.get_instructions(hisoblangan) if i.opname == "BINARY_OP"]
print(f" funksiyada qolgan BINARY_OP soni: {len(amallar)} — faqat 'a' * 5000 ish vaqtida hisoblanadi")
print("\n=== 2. Chegara: 4096 ===")
kichik1, kichik2 = "a" * 4096, "a" * 4096
katta1, katta2 = "a" * 4097, "a" * 4097
print(f" 'a' * 4096 ikki marta — bitta obyekt: {kichik1 is kichik2}")
print(f" 'a' * 4097 ikki marta — bitta obyekt: {katta1 is katta2}")
print("\n=== 3. Bir fayldagi konstantalar birlashadi ===")
def xabar_a():
return "Buyurtma qabul qilindi! Rahmat."
def xabar_b():
return "Buyurtma qabul qilindi! Rahmat."
print(f" ikki funksiyadagi bir xil (bo'shliqli) konstanta bitta obyekt: {xabar_a() is xabar_b()}")
narx_a, narx_b = 12_345_678, 12_345_678
print(f" katta son literali ikki marta: {narx_a is narx_b}")
print("\n=== 4. REPL — har buyruq alohida kompilyatsiya ===")
muhit = {}
exec(compile('x = "Buyurtma qabul qilindi! Rahmat."', "<repl-1>", "exec"), muhit)
exec(compile('y = "Buyurtma qabul qilindi! Rahmat."', "<repl-2>", "exec"), muhit)
print(f" ikki alohida kompilyatsiya — bitta obyekt: {muhit['x'] is muhit['y']}")
exec(compile('p = 12345678\nq = 12345678', "<bitta-blok>", "exec"), muhit)
print(f" bitta blokda son: {muhit['p'] is muhit['q']}")
exec(compile("r = 12345678", "<repl-3>", "exec"), muhit)
print(f" alohida blokda son: {muhit['p'] is muhit['r']}")
print(" ⚠️ is natijasi kod qanday kompilyatsiya qilinganiga bog'liq — qiymatlar uchun faqat ==")Natijaning muhim qismi:
=== 1. co_consts: nima oldindan hisoblangan ===
60 * 60 * 24 natijasi konstantalar ichida: True
"-" * 30 natijasi konstantalar ichida: True
"sal" + "om" natijasi konstantalar ichida: True
"a" * 5000 natijasi konstantalar ichida: False
funksiyada qolgan BINARY_OP soni: 1 — faqat 'a' * 5000 ish vaqtida hisoblanadi
=== 2. Chegara: 4096 ===
'a' * 4096 ikki marta — bitta obyekt: True
'a' * 4097 ikki marta — bitta obyekt: False
=== 3. Bir fayldagi konstantalar birlashadi ===
ikki funksiyadagi bir xil (bo'shliqli) konstanta bitta obyekt: True
katta son literali ikki marta: True
=== 4. REPL — har buyruq alohida kompilyatsiya ===
ikki alohida kompilyatsiya — bitta obyekt: False
bitta blokda son: True
alohida blokda son: False
⚠️ is natijasi kod qanday kompilyatsiya qilinganiga bog'liq — qiymatlar uchun faqat ==Nima ko'rsatdi: 2.4-bo'lim. 12_345_678 kabi katta son ham bir blokda ikki marta yozilganda bitta obyekt bo'ldi — bu kesh emas, konstantalarni birlashtirish.
Misol 3 — sys.intern va lug'at qidiruvi
"""Lug'at qidiruvi is ni avval tekshiradi: interned va interned bo'lmagan kalitlar; atribut nomlari; o'lchov."""
import sys
import time
class HisobchiSatr(str):
"""Taqqoslashlarni sanaydigan satr — lug'at qachon == ga murojaat qilishini ko'rsatadi."""
taqqoslashlar = 0
def __eq__(self, boshqa):
HisobchiSatr.taqqoslashlar += 1
return str.__eq__(self, boshqa)
__hash__ = str.__hash__
print("=== 1. Lug'at: avval is, keyin == ===")
kalit = HisobchiSatr("mahsulot_narxi")
lugat = {kalit: 12_000}
HisobchiSatr.taqqoslashlar = 0
lugat[kalit]
print(f" o'sha obyekt bilan qidiruv: __eq__ chaqiruvlari = {HisobchiSatr.taqqoslashlar}")
teng_nusxa = HisobchiSatr("".join(["mahsulot", "_narxi"]))
lugat[teng_nusxa]
print(f" teng, lekin boshqa obyekt: __eq__ chaqiruvlari = {HisobchiSatr.taqqoslashlar}")
print("\n=== 2. Uzun kalitlar: interned va interned emas ===")
QISM = "foydalanuvchi_profil_sozlamalari_bildirishnomalar_elektron_pochta_" * 40 # ~2700 belgi
kalitlar = [QISM + str(i) for i in range(2000)]
malumot = {k: i for i, k in enumerate(kalitlar)}
nusxalar = ["".join([QISM, str(i)]) for i in range(2000)]
internlar = [sys.intern(k) for k in kalitlar]
print(f" nusxalar kalit bilan bitta obyekt: {nusxalar[0] is kalitlar[0]}, internlar: {internlar[0] is kalitlar[0]}")
def olcha(soroq_kalitlari, takror=50):
eng_yaxshi = float("inf")
for _ in range(5):
bosh = time.perf_counter()
for _ in range(takror):
for k in soroq_kalitlari:
malumot[k]
eng_yaxshi = min(eng_yaxshi, time.perf_counter() - bosh)
return eng_yaxshi
asl = olcha(kalitlar)
nusxa = olcha(nusxalar)
print(f" teng nusxalar bilan qidiruv o'sha kalitlarga nisbatan sekinroq: {nusxa > asl}")
print("\n=== 3. Atribut nomlari avtomatik interned ===")
class Sozlama:
pass
s = Sozlama()
for nom in ["".join(["ma", "vzu"]), "".join(["til", "_kodi"])]:
setattr(s, nom, True)
print(f" setattr nomlari interned: {[sys._is_interned(n) for n in vars(s)]}")
print(f" getattr(s, join(...)) ishlaydi: {getattr(s, ''.join(['ma', 'vzu']))}")Natijaning muhim qismi:
=== 1. Lug'at: avval is, keyin == ===
o'sha obyekt bilan qidiruv: __eq__ chaqiruvlari = 0
teng, lekin boshqa obyekt: __eq__ chaqiruvlari = 1
=== 2. Uzun kalitlar: interned va interned emas ===
nusxalar kalit bilan bitta obyekt: False, internlar: True
teng nusxalar bilan qidiruv o'sha kalitlarga nisbatan sekinroq: True
=== 3. Atribut nomlari avtomatik interned ===
setattr nomlari interned: [True, True]
getattr(s, join(...)) ishlaydi: TrueNima ko'rsatdi: 2.3, 2.6-bo'limlar. O'sha kalit obyekti bilan qidirilganda __eq__ umuman chaqirilmadi; teng nusxa bilan esa bir marta chaqirildi.
Misol 4 — Amaliy: log tahlilida xotirani tejash
Veb-server loglaridan har qatorda shahar, qurilma, holat va URL yo'li bor. Ko'p qiymatlar takrorlanadi. Uch usulni tracemalloc bilan solishtiramiz: oddiy o'qish, sys.intern va o'z kichik lug'at-keshimiz.
"""100 000 log qatori: oddiy split, sys.intern, qo'lda kesh — xotira va noyob obyektlar soni; qaysi maydonni intern qilish kerak."""
import random
import sys
import tracemalloc
rng = random.Random(20260914)
SHAHARLAR = ["Toshkent", "Samarqand", "Buxoro", "Namangan", "Andijon", "Farg'ona", "Nukus", "Qarshi"]
QURILMALAR = ["android", "ios", "windows", "macos", "linux"]
HOLATLAR = ["200", "301", "404", "500"]
YOLLAR = [f"/mahsulot/{i}" for i in range(20_000)]
QATORLAR = [
f"{rng.choice(SHAHARLAR)},{rng.choice(QURILMALAR)},{rng.choice(HOLATLAR)},{rng.choice(YOLLAR)}"
for _ in range(100_000)
]
def oddiy(qator):
shahar, qurilma, holat, yol = qator.split(",")
return (shahar, qurilma, holat, yol)
def internli(qator):
shahar, qurilma, holat, yol = qator.split(",")
return (sys.intern(shahar), sys.intern(qurilma), sys.intern(holat), yol)
_kesh = {}
def keshli(qator):
shahar, qurilma, holat, yol = qator.split(",")
return (_kesh.setdefault(shahar, shahar), _kesh.setdefault(qurilma, qurilma),
_kesh.setdefault(holat, holat), yol)
def olchov(tahlilchi):
tracemalloc.start()
yozuvlar = [tahlilchi(q) for q in QATORLAR]
hajm, _ = tracemalloc.get_traced_memory()
tracemalloc.stop()
noyob_obyekt = {ustun: len({id(y[ustun]) for y in yozuvlar}) for ustun in range(4)}
return hajm, noyob_obyekt, yozuvlar
print("=== 1. Xotira va noyob obyektlar ===")
natijalar = {}
for nom, f in [("oddiy", oddiy), ("sys.intern", internli), ("qo'lda kesh", keshli)]:
hajm, noyob, yozuvlar = olchov(f)
natijalar[nom] = hajm
print(f" {nom:12} ≈ {hajm / 1_000_000:5.1f} MB noyob obyektlar (shahar, qurilma, holat, yo'l): "
f"{noyob[0]:>6}, {noyob[1]:>6}, {noyob[2]:>6}, {noyob[3]:>6}")
del yozuvlar
print(f" intern tejami: {1 - natijalar['sys.intern'] / natijalar['oddiy']:.0%}")
print("\n=== 2. Qaysi ustunni intern qilish kerak ===")
yozuvlar = [oddiy(q) for q in QATORLAR]
for i, nom in enumerate(["shahar", "qurilma", "holat", "yo'l"]):
noyob_qiymat = len({y[i] for y in yozuvlar})
nisbat = len(yozuvlar) / noyob_qiymat
tavsiya = "✅ intern" if nisbat >= 50 else "❌ foydasi kam"
print(f" {nom:8} noyob qiymatlar: {noyob_qiymat:>6}, har qiymat o'rtacha {nisbat:>8.1f} marta takrorlanadi → {tavsiya}")
del yozuvlar
print("\n=== 3. Natija to'g'riligi o'zgarmadi ===")
a, b = oddiy(QATORLAR[0]), internli(QATORLAR[0])
print(f" a == b: {a == b}, a[0] is b[0]: {a[0] is b[0]} — mantiq == ga tayanadi, interning faqat xotira uchun")Natijaning muhim qismi:
=== 1. Xotira va noyob obyektlar ===
oddiy ≈ 28.2 MB noyob obyektlar (shahar, qurilma, holat, yo'l): 100000, 100000, 100000, 100000
sys.intern ≈ 14.2 MB noyob obyektlar (shahar, qurilma, holat, yo'l): 8, 5, 4, 100000
qo'lda kesh ≈ 14.2 MB noyob obyektlar (shahar, qurilma, holat, yo'l): 8, 5, 4, 100000
intern tejami: 50%
=== 2. Qaysi ustunni intern qilish kerak ===
shahar noyob qiymatlar: 8, har qiymat o'rtacha 12500.0 marta takrorlanadi → ✅ intern
qurilma noyob qiymatlar: 5, har qiymat o'rtacha 20000.0 marta takrorlanadi → ✅ intern
holat noyob qiymatlar: 4, har qiymat o'rtacha 25000.0 marta takrorlanadi → ✅ intern
yo'l noyob qiymatlar: 19860, har qiymat o'rtacha 5.0 marta takrorlanadi → ❌ foydasi kam
=== 3. Natija to'g'riligi o'zgarmadi ===
a == b: True, a[0] is b[0]: False — mantiq == ga tayanadi, interning faqat xotira uchunNima ko'rsatdi: 2.6-bo'lim — kam noyob qiymatli ustunlarni intern qilish xotirani sezilarli kamaytirdi, 20 000 xil qiymatli yo'l ustuni esa bunga loyiq emas; mantiq esa == da o'zgarmay qoldi.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Bir xil satrlar har doim bitta obyekt" | Faqat konstantalar, nomlar va sys.intern qilinganlar |
"Kichik sonlar keshi — 0...255" |
CPython'da -5 ... 256 |
"257 is 257 har doim False" |
Bir blokdagi literallar birlashtiriladi — ko'pincha True |
"sys.intern kodni tezlashtiradi" |
Asosan xotira; tezlik farqi odatda kichik |
| "Interned satrlar abadiy yashaydi" | Python 3.14 da o'lmas emas |
| "Lug'at kalitlari avtomatik interned" | Ish vaqtida yaratilgan kalitlar — yo'q |
| "Kompilyator istalgan ifodani oldindan hisoblaydi" | Satr va ketma-ketliklar uchun natija ≤ 4096 element |
"Fayl va REPL da is bir xil natija beradi" |
Kompilyatsiya birligi farq qiladi |
6. Keng tarqalgan xatolar va yechimlari
1. Qiymatlarni is bilan solishtirish
if holat is "faol": ... # ❌ SyntaxWarning, fayldan kelgan satrda False
if holat == "faol": ... # ✅2. Interningga tayangan "tezlashtirish"
if sys.intern(a) is sys.intern(b): ... # ❌ har chaqiruvda jadval qidiruvi — == dan sekinroq
if a == b: ... # ✅3. Noyob qiymatlarni intern qilish
yozuv["uuid"] = sys.intern(qator[0]) # ❌ har qiymat noyob — foyda yo'q, faqat narxFaqat takrorlanuvchi ustunlar (shahar, holat, teg).
4. bytes ni intern qilishga urinish
sys.intern(b"kalit") # ❌ TypeError: intern() argument must be str Kerak bo'lsa dict.setdefault(k, k) asosidagi o'z keshingiz.
5. Kichik sonlar keshiga tayanadigan test
assert natija is 200 # ❌ 200 ↔ ishlaydi, 1000 ↔ yo'q
assert natija == 200 # ✅6. Katta ro'yxatda takroriy satrlarni saqlash
yozuvlar.append({"shahar": maydonlar[0], ...}) # ⚠️ 10 mln "Toshkent" nusxasi
yozuvlar.append({"shahar": sys.intern(maydonlar[0]), ...}) # ✅ yoki pandas "category" turi (24-qism)7. REPL da ko'rgan is natijasini kodga ko'chirish
>>> a = 1000; b = 1000; a is b # REPL: bir qatorda True Natija kompilyatsiya birligiga bog'liq — == ishlating.
8. id bo'yicha takrorlarni aniqlash
noyob = {id(s) for s in satrlar} # ❌ teng satrlar turli id ga ega bo'lishi mumkin
noyob = set(satrlar) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 3.1-dars (o'tilgan): kichik sonlar tuzog'i
- 4.12-dars (o'tilgan): satrlarning o'zgarmasligi — interning uning sharti
- 11.2-dars (o'tilgan):
isva==; lug'atdaisqisqa yo'li - 11.8-dars:
sys.getsizeof— satr va sonlarning haqiqiy hajmi - 12-qism:
co_consts, AST optimallashtiruvchi,LOAD_CONSTvaLOAD_SMALL_INT - 16-qism: katta fayllarni o'qishda xotira
- 24-qism: pandas
categoryturi — ustun darajasidagi "interning" - 29-qism: xotira optimizatsiyasi
8. Eng yaxshi amaliyotlar
Qiymatlar uchun faqat
==— interning va keshlar implementatsiya tafsiloti.Katta ma'lumotda takrorlanuvchi satr ustunlarini
sys.internqiling.Interningdan oldin o'lchang — noyob qiymatlar va nusxalar nisbati,
tracemalloc.Noyob qiymatlarni (ID, vaqt, URL) intern qilmang.
bytesva boshqa turlar uchundict.setdefault(x, x)keshi.Jadvalli ma'lumotda pandas
categoryyokienum— interningdan ham samaraliroq.Test va REPL dagi
isnatijalariga ishonmang.Kompilyator optimallashtirishini "oldindan hisoblangan konstanta" uchun ishlating —
60 * 60 * 24ni o'qiladigan qilib yozing, u baribir bir marta hisoblanadi.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
import sys
1. a = 256
b = int("256")
print(a is b)
2. a = 257
b = int("257")
print(a is b)
3. s = "".join(["o", "k"])
print(s == "ok", s is sys.intern(s))
4. OK = "ok"
print(sys.intern("".join(["o", "k"])) is OK)
5. print(chr(65) is chr(65), str(10) is str(10))
6. x = "a" * 10
y = "a" * 10
print(x is y)
7. x = "a" * 5000
y = "a" * 5000
print(x is y)
8. BOSH = ()
print(tuple([]) is BOSH, [] is [])
9. def f():
return "bir xil matn!"
def g():
return "bir xil matn!"
print(f() is g())
10. n = 5
s = f"{n}"
print(s == "5", s is str(5))
11. class K:
pass
k = K()
setattr(k, "".join(["ab", "cd"]), 1)
print(sys._is_interned(next(iter(vars(k)))))
12. d = {"".join(["ab", "cd"]): 1}
print(sys._is_interned(next(iter(d))))Javoblar
True— kichik sonlar keshiFalse— ish vaqtida yaratilgan 257True False—sys.internjadvaldagi"ok"konstantasini qaytardi,sni emasTrueTrue False— bitta belgili satrlar keshlangan,str(10)esa har safar yangiTrue— oldindan hisoblangan konstantaFalse— 4096 dan uzun, ish vaqtida hisoblanadiTrue False— bo'sh kortej yagona, ro'yxat yo'qTrue— bir fayldagi konstantalar birlashtirilganTrue FalseTrue— atribut nomlari internedFalse— ish vaqtidagi lug'at kaliti interned emas
Vazifa 2: Xatolarni tuzating
1. def holat_tekshir(javob):
return javob["status"] is "ok"
2. def tezkor_teng(a, b):
return sys.intern(a) is sys.intern(b)
3. for qator in fayl:
id_, vaqt, shahar = qator.split(",")
yozuvlar.append((sys.intern(id_), sys.intern(vaqt), shahar))
4. teglar = [sys.intern(t.encode()) for t in xom_teglar]
5. assert len(natija) is 1000Javoblar
1. def holat_tekshir(javob):
return javob["status"] == "ok" # JSON dan kelgan satr interned emas
2. def tezkor_teng(a, b):
return a == b # == o'zi avval is ni tekshiradi va arzon
3. for qator in fayl:
id_, vaqt, shahar = qator.split(",")
yozuvlar.append((id_, vaqt, sys.intern(shahar))) # noyob id va vaqt emas, takrorlanuvchi shahar
4. kesh = {}
teglar = [kesh.setdefault(b, b) for b in (t.encode() for t in xom_teglar)] # bytes uchun o'z keshi
5. assert len(natija) == 1000Vazifa 3: Kesh xaritasi
Skript yozing:
-20dan300gacha barcha butun sonlar uchun ish vaqtida ikki marta yaratilgan obyektlar bitta ekanini tekshirib, kesh chegaralarini avtomatik topsinchr(0)danchr(1000)gacha bitta belgili satrlar uchun ham xuddi shundaybytes([n])uchun- Natijani jadval qilib chiqarib, CPython manba kodidagi qiymatlar bilan solishtiring
Vazifa 4: Universal intern keshi
Interner klassini yozing:
str,bytes,tuple(ichidagi elementlar ham rekursiv intern qilinsin) vafrozensetni qo'llab-quvvatlasin- Obyektlar ishlatilmay qolganda keshdan tushsin — qaysi turlar
weakrefni qo'llab-quvvatlamasligini aniqlab, muqobil strategiya tanlang statistika()— so'rovlar, topilganlar, tejalgan taxminiy baytlar (sys.getsizeof)- Misol 4 dagi loglarga qo'llang
Vazifa 5: CSV yuklovchi
yukla(yol, intern_ustunlar="avto") funksiyasini yozing:
- Birinchi 10 000 qatorni namuna qilib, har ustun uchun noyob qiymatlar nisbatini hisoblasin
- Nisbati chegaradan past ustunlarni avtomatik
sys.internqilsin tracemallocbilan intern qilingan va qilinmagan yuklash xotirasini solishtirsin- 1 million qatorli sun'iy fayl bilan sinang va natijani jadvalda ko'rsating
Vazifa 6: Kompilyator tajribalari
dis va co_consts yordamida aniqlang:
"a" * nqaysingacha oldindan hisoblanadi (ikkilik qidiruv bilan)(1, 2) * nvab"x" * nuchun chegara bir xilmi2 ** nqaysingacha oldindan hisoblanadi (katta sonlar uchun alohida cheklov bor)"abc"[1],(1, 2, 3)[0],not Truekabi ifodalar oldindan hisoblanadimi- Natijalarni jadvalda yozing va nega bunday cheklovlar kerakligini tushuntiring
Vazifa 7: O'ylash
Java'da String.intern() va kompilyatsiya konstantalari uchun satrlar havzasi, Lua'da qisqa satrlarning avtomatik interningi, Ruby'da Symbol va Erlang'da atomlar bor. Bu yondashuvlar Python'dagi interningdan qanday farq qiladi va nega Python barcha satrlarni avtomatik intern qilmaydi?
Javob
Qisqa javob: interning — "bir xil qiymat uchun bitta obyekt" kelishuvi. U xotira va taqqoslashni tejaydi, lekin har satr yaratilganda jadval qidiruvi narxini, jadvalni tozalash murakkabligini va (ba'zi tillarda) xotira sizishi xavfini olib keladi. Tillar bu narxni turli joyga qo'yadi: Lua qisqa satrlar uchun uni har doim to'laydi, Ruby va Erlang alohida "belgi" turini ajratadi, Java va Python esa faqat konstantalar va aniq so'ralgan satrlar uchun to'laydi.
1. Java
- Kompilyatsiya vaqtidagi satr konstantalari satrlar havzasiga (string pool) tushadi
- Ish vaqtida yaratilgan satrlar — alohida obyektlar;
String.intern()ularni havzaga qo'shadi ==havolani taqqoslaydi — shuning uchun Java'da interning bilan bog'liq xatolar Python'dagidan ham ko'p (==vaequals(), 11.2-dars)- G1 axlat yig'uvchisida ixtiyoriy satrlar dublikatini yo'qotish (string deduplication) bor: yig'uvchi bir xil satrlarning ichki massivlarini fon rejimida birlashtiradi
2. Lua
- Qisqa satrlar (Lua 5.2+ da 40 belgigacha) har doim interning qilinadi
- Taqqoslash va jadval kalitlari — ko'rsatkichni solishtirish, juda tez
- Uzun satrlar interning qilinmaydi — yaratish narxini cheklash uchun
3. Ruby: Symbol
:holat.equal?(:holat) # true — bitta obyekt
"holat".equal?("holat") # falseSymbol— alohida tur, har doim yagona, identifikatorlar va xesh kalitlari uchun- Uzoq vaqt Symbol lar xotiradan o'chmagan; Ruby 2.2 dan dinamik yaratilganlari axlat yig'uvchi tomonidan tozalanadi
4. Erlang: atomlar
- Atom — nomlangan konstanta, taqqoslash juda tez
- Atomlar axlat yig'uvchi tomonidan tozalanmaydi, jadval o'lchami chegaralangan — foydalanuvchi kiritmasidan dinamik atom yaratish tizimni yiqitishi mumkin bo'lgan klassik xavfsizlik muammosi
5. Python yondashuvi
- Avtomatik: kod konstantalari, nomlar, atribut nomlari, bitta belgili satrlar
- Qo'lda:
sys.intern— faqatstr - Alohida "belgi" turi yo'q —
enumva satrlar bu vazifani bajaradi - 3.14 da ish vaqtida intern qilingan satrlar o'lmas emas — ishlatilmasa tozalanadi, Erlang atomlaridagi sizish muammosi yo'q
6. Nega hamma satrlar avtomatik intern qilinmaydi
| Sabab | Tafsilot |
|---|---|
| Yaratish narxi | Har split, join, f-string, fayl o'qishda jadval qidiruvi va xesh hisoblash |
| Noyob satrlar ko'pchilik | Matn, URL, JSON qiymatlari ko'pincha noyob — interning faqat narx |
| Jadval hajmi va tozalash | Million noyob satr jadvalni ham shishiradi; o'chirishni kuzatish kerak |
| Iplar | Global jadval — umumiy holat, qulf yoki murakkab sinxronizatsiya (erkin ipli yig'mada ayniqsa) |
== allaqachon tez |
Uzunlik va xesh farq qilsa darhol False, bir xil obyekt bo'lsa darhol True |
Python dizaynerlari sukut bo'yicha arzon yaratish va kerak joyda aniq interning ni tanlagan. Lua'da interning tabiiyroq, chunki til kichik va satrlar asosan identifikator va kalit sifatida ishlatiladi.
7. Amaliy xulosa
- Qiymatlarni doim
==bilan taqqoslang — qaysi tilda bo'lmasin interning tafsilotiga tayanmang - Takrorlanuvchi, kam noyob qiymatli satrlar uchun
sys.internyoki ustun darajasidagi yechim (pandascategory) - Identifikatorga o'xshash qiymatlar to'plami uchun
enum— Ruby Symbol va Erlang atomining Python'dagi xavfsiz o'rinbosari - Foydalanuvchi kiritmasidan cheksiz "belgi" yaratmang — Erlang saboqi
8. Xulosa
- Interning xotira va taqqoslashni tejaydi, lekin yaratishga narx qo'shadi
- Java va Python konstantalarni avtomatik, qolganini so'rov bo'yicha intern qiladi; Lua qisqa satrlarni har doim
- Ruby
Symbolva Erlang atomlari — alohida tur; atomlar tozalanmasligi xavfli - Python barcha satrlarni intern qilmaydi, chunki ko'p satrlar noyob,
==allaqachon tez va global jadval iplar uchun muammo - Amalda:
==, kerak joydasys.intern, belgilar uchunenum
Nimani mustahkamlaydi: 2.1–2.6-bo'limlar.
Xulosa
Bu darsda "bir xil qiymat — bitta obyekt" natijasini beruvchi mexanizmlarni o'rgandik.
Eng muhim uch fikr:
Uch mexanizm, hech biri kafolat emas. Kichik butun sonlar (
-5 ... 256), bitta belgili satrlar,()va bitta baytlibytes— oldindan yaratilgan keshlar. Kod konstantalari, nomlar va atribut nomlari — interned. Bir faylda yozilgan bir xil konstantalar — kompilyator tomonidan birlashtirilgan (satr va ketma-ketliklar ≤ 4096 element bo'lsa oldindan hisoblangan ham). REPL da har buyruq alohida kompilyatsiya qilinadi — shuning uchunisnatijalari kod qanday ishga tushirilganiga bog'liq.Ish vaqtida yaratilgan satrlar interned emas.
split,join, f-string, JSON, fayl va tarmoqdan kelgan satrlar va oddiy lug'at kalitlari — alohida obyektlar;setattrnomlari esa interned.sys.internjadvaldagi yagona nusxani qaytaradi, Python 3.14 da interned satrlar o'lmas emas va ishlatilmasa tozalanadi.sys.intern— xotira vositasi,==— mantiq vositasi. Kam noyob qiymatli, ko'p takrorlanuvchi ustunlarni (shahar, holat, qurilma) intern qilish xotirani bir necha barobar kamaytiradi; noyob qiymatlarni intern qilish esa faqat narx. Lug'at qidiruviisni avval tekshirgani uchun interning tezlikka ham biroz yordam beradi, lekin kodning to'g'riligi hech qachon unga tayanmasligi kerak.
Keyingi darsda obyektlarning haqiqiy hajmini o'rganamiz: sys.getsizeof nimani o'lchaydi va nimani o'lchamaydi, konteyner va uning ichidagi obyektlar, chuqur hajmni hisoblash va tracemalloc bilan to'g'ri o'lchash.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!