IlmHamroh
Python kursi/Obyekt modeli va xotira7/10-dars24 daqiqa
Mundarija (20)

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:

python
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 bytes keshlari
  • 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 s ga teng satr bo'lsa — o'shani qaytaradi
  • bo'lmasa — s ni jadvalga qo'shib, uni qaytaradi
  • faqat str qabul qiladi (bytes emas)

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:

python
"ab" + "cd"        # → "abcd" konstanta
"-" * 20           # → 20 ta "-" konstanta
60 * 60 * 24       # → 86400

Satr 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:

python
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.intern chaqiruvining 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 — alohida

Satrlar

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 emas

Kompilyator

"ab" + "cd", "-" * 20        → oldindan hisoblangan konstanta (≤ 4096 element)
bir fayldagi bir xil konstantalar → bitta obyekt
REPL → har buyruq alohida

Keshlar

() "" chr(0..255) b"" bytes([n])  → yagona
frozenset(), float, [] {}         → yo'q

Qoida

qiymat taqqoslash → ==    (hech qachon is)
sys.intern → kam noyob qiymat × ko'p nusxa → xotira

4. Batafsil misollar

Misol 1 — Kesh va interning qoidalarini tekshirish

python
"""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:

text
=== 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: False

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

Misol 2 — Kompilyator: oldindan hisoblash va birlashtirish

python
"""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:

text
=== 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

python
"""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:

text
=== 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: True

Nima 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.

python
"""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:

text
=== 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 uchun

Nima 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

python
if holat is "faol": ...                # ❌ SyntaxWarning, fayldan kelgan satrda False
if holat == "faol": ...                # ✅

2. Interningga tayangan "tezlashtirish"

python
if sys.intern(a) is sys.intern(b): ... # ❌ har chaqiruvda jadval qidiruvi — == dan sekinroq
if a == b: ...                         # ✅

3. Noyob qiymatlarni intern qilish

python
yozuv["uuid"] = sys.intern(qator[0])   # ❌ har qiymat noyob — foyda yo'q, faqat narx

Faqat takrorlanuvchi ustunlar (shahar, holat, teg).

4. bytes ni intern qilishga urinish

python
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

python
assert natija is 200                   # ❌ 200 ↔ ishlaydi, 1000 ↔ yo'q
assert natija == 200                   # ✅

6. Katta ro'yxatda takroriy satrlarni saqlash

python
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

python
>>> a = 1000; b = 1000; a is b         # REPL: bir qatorda True

Natija kompilyatsiya birligiga bog'liq — == ishlating.

8. id bo'yicha takrorlarni aniqlash

python
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): is va ==; lug'atda is qisqa yo'li
  • 11.8-dars: sys.getsizeof — satr va sonlarning haqiqiy hajmi
  • 12-qism: co_consts, AST optimallashtiruvchi, LOAD_CONST va LOAD_SMALL_INT
  • 16-qism: katta fayllarni o'qishda xotira
  • 24-qism: pandas category turi — ustun darajasidagi "interning"
  • 29-qism: xotira optimizatsiyasi

8. Eng yaxshi amaliyotlar

  1. Qiymatlar uchun faqat == — interning va keshlar implementatsiya tafsiloti.

  2. Katta ma'lumotda takrorlanuvchi satr ustunlarini sys.intern qiling.

  3. Interningdan oldin o'lchang — noyob qiymatlar va nusxalar nisbati, tracemalloc.

  4. Noyob qiymatlarni (ID, vaqt, URL) intern qilmang.

  5. bytes va boshqa turlar uchun dict.setdefault(x, x) keshi.

  6. Jadvalli ma'lumotda pandas category yoki enum — interningdan ham samaraliroq.

  7. Test va REPL dagi is natijalariga ishonmang.

  8. Kompilyator optimallashtirishini "oldindan hisoblangan konstanta" uchun ishlating — 60 * 60 * 24 ni o'qiladigan qilib yozing, u baribir bir marta hisoblanadi.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
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
  1. True — kichik sonlar keshi
  2. False — ish vaqtida yaratilgan 257
  3. True False — sys.intern jadvaldagi "ok" konstantasini qaytardi, s ni emas
  4. True
  5. True False — bitta belgili satrlar keshlangan, str(10) esa har safar yangi
  6. True — oldindan hisoblangan konstanta
  7. False — 4096 dan uzun, ish vaqtida hisoblanadi
  8. True False — bo'sh kortej yagona, ro'yxat yo'q
  9. True — bir fayldagi konstantalar birlashtirilgan
  10. True False
  11. True — atribut nomlari interned
  12. False — ish vaqtidagi lug'at kaliti interned emas

Vazifa 2: Xatolarni tuzating

python
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 1000
Javoblar
python
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) == 1000

Vazifa 3: Kesh xaritasi

Skript yozing:

  1. -20 dan 300 gacha barcha butun sonlar uchun ish vaqtida ikki marta yaratilgan obyektlar bitta ekanini tekshirib, kesh chegaralarini avtomatik topsin
  2. chr(0) dan chr(1000) gacha bitta belgili satrlar uchun ham xuddi shunday
  3. bytes([n]) uchun
  4. Natijani jadval qilib chiqarib, CPython manba kodidagi qiymatlar bilan solishtiring

Vazifa 4: Universal intern keshi

Interner klassini yozing:

  1. str, bytes, tuple (ichidagi elementlar ham rekursiv intern qilinsin) va frozenset ni qo'llab-quvvatlasin
  2. Obyektlar ishlatilmay qolganda keshdan tushsin — qaysi turlar weakref ni qo'llab-quvvatlamasligini aniqlab, muqobil strategiya tanlang
  3. statistika() — so'rovlar, topilganlar, tejalgan taxminiy baytlar (sys.getsizeof)
  4. Misol 4 dagi loglarga qo'llang

Vazifa 5: CSV yuklovchi

yukla(yol, intern_ustunlar="avto") funksiyasini yozing:

  1. Birinchi 10 000 qatorni namuna qilib, har ustun uchun noyob qiymatlar nisbatini hisoblasin
  2. Nisbati chegaradan past ustunlarni avtomatik sys.intern qilsin
  3. tracemalloc bilan intern qilingan va qilinmagan yuklash xotirasini solishtirsin
  4. 1 million qatorli sun'iy fayl bilan sinang va natijani jadvalda ko'rsating

Vazifa 6: Kompilyator tajribalari

dis va co_consts yordamida aniqlang:

  1. "a" * n qaysi n gacha oldindan hisoblanadi (ikkilik qidiruv bilan)
  2. (1, 2) * n va b"x" * n uchun chegara bir xilmi
  3. 2 ** n qaysi n gacha oldindan hisoblanadi (katta sonlar uchun alohida cheklov bor)
  4. "abc"[1], (1, 2, 3)[0], not True kabi ifodalar oldindan hisoblanadimi
  5. 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 (== va equals(), 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

ruby
:holat.equal?(:holat)          # true — bitta obyekt
"holat".equal?("holat")        # false
  • Symbol — 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 — faqat str
  • Alohida "belgi" turi yo'q — enum va 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

  1. Qiymatlarni doim == bilan taqqoslang — qaysi tilda bo'lmasin interning tafsilotiga tayanmang
  2. Takrorlanuvchi, kam noyob qiymatli satrlar uchun sys.intern yoki ustun darajasidagi yechim (pandas category)
  3. Identifikatorga o'xshash qiymatlar to'plami uchun enum — Ruby Symbol va Erlang atomining Python'dagi xavfsiz o'rinbosari
  4. Foydalanuvchi kiritmasidan cheksiz "belgi" yaratmang — Erlang saboqi

8. Xulosa

  1. Interning xotira va taqqoslashni tejaydi, lekin yaratishga narx qo'shadi
  2. Java va Python konstantalarni avtomatik, qolganini so'rov bo'yicha intern qiladi; Lua qisqa satrlarni har doim
  3. Ruby Symbol va Erlang atomlari — alohida tur; atomlar tozalanmasligi xavfli
  4. Python barcha satrlarni intern qilmaydi, chunki ko'p satrlar noyob, == allaqachon tez va global jadval iplar uchun muammo
  5. Amalda: ==, kerak joyda sys.intern, belgilar uchun enum

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:

  1. Uch mexanizm, hech biri kafolat emas. Kichik butun sonlar (-5 ... 256), bitta belgili satrlar, () va bitta baytli bytes — 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 uchun is natijalari kod qanday ishga tushirilganiga bog'liq.

  2. Ish vaqtida yaratilgan satrlar interned emas. split, join, f-string, JSON, fayl va tarmoqdan kelgan satrlar va oddiy lug'at kalitlari — alohida obyektlar; setattr nomlari esa interned. sys.intern jadvaldagi yagona nusxani qaytaradi, Python 3.14 da interned satrlar o'lmas emas va ishlatilmasa tozalanadi.

  3. 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 qidiruvi is ni 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.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
11.7-dars: Interning — kichik sonlar va satrlar — IlmHamroh