IlmHamroh
Python kursi/Satrlar chuqur1/12-dars25 daqiqa
Mundarija (23)

4.1-dars: Satrning ichki tuzilishi va indekslash

4-QISM — SATRLAR CHUQUR · 1-dars


1. Kirish va motivatsiya

3.7-darsda satrlar bilan tanishdik: yaratish, indekslash, asosiy metodlar. Endi ichkariga kiramiz.

Nega bu kerak? Chunki satrlar bilan bog'liq eng chalkash savollar aynan ichki tuzilishdan kelib chiqadi:

python
matn = "salom"
print(len(matn))                        # 5

matn = "салом"
print(len(matn))                        # 5
print(len(matn.encode("utf-8")))        # 10 — nega ikki barobar?
python
import sys
print(sys.getsizeof("a"))               # 50
print(sys.getsizeof("ы"))               # 76 — nega ko'proq?
print(sys.getsizeof("😊"))              # 80
python
a = "salom"
b = "salom"
print(a is b)                           # True

a = "salom dunyo!"
b = "salom dunyo!"
print(a is b)                           # False — nega endi yo'q?

Bu darsda:

  • CPython satrni qanday saqlaydi (kompakt Unicode ko'rinishi)
  • Kod nuqtasi, belgi va grafema farqi
  • ord(), chr() va Unicode jadvali
  • Internlash — nega ba'zi satrlar bitta obyekt
  • Indekslash tezligi va nima uchun str list ga o'xshamaydi

2. Nazariya — chuqur tushuntirish

2.1. Satr — Unicode kod nuqtalari ketma-ketligi

3.7-darsda aytgandik: Python 3 da satr — belgilar ketma-ketligi, baytlar emas.

Aniqrog'i: satr — Unicode kod nuqtalari (code points) ketma-ketligi.

python
matn = "salom"

for belgi in matn:
    print(f"  {belgi!r}  →  U+{ord(belgi):04X}  ({ord(belgi)})")
text
  's'  →  U+0073  (115)
  'a'  →  U+0061  (97)
  'l'  →  U+006C  (108)
  'o'  →  U+006F  (111)
  'm'  →  U+006D  (109)

U+0073 — Unicode standartidagi rasmiy yozuv. 0x73 = 115.

Unicode nima? Dunyodagi barcha yozuv tizimining barcha belgisiga noyob raqam beruvchi standart. 2026-yil holatiga ko'ra 149 000 dan ortiq belgi.

text
U+0000 – U+007F     ASCII (lotin, raqam, tinish)
U+0080 – U+07FF     Kirillitsa, arab, ibroniy, yunon
U+0800 – U+FFFF     Xitoy, yapon, koreys va boshqalar
U+10000 – U+10FFFF  Emoji, qadimiy yozuvlar, matematik belgilar

8-darsda Unicode ni to'liq o'rganamiz. Hozircha: har bir belgi — bitta raqam.

2.2. ord() va chr()

python
print(ord("A"))                 # 65
print(ord("a"))                 # 97
print(ord("0"))                 # 48
print(ord("я"))                 # 1103
print(ord("中"))                # 20013
print(ord("😊"))                # 128522

print(chr(65))                  # 'A'
print(chr(1103))                # 'я'
print(chr(128522))              # '😊'

Foydali qonuniyatlar:

python
# Lotin harflari ketma-ket
print(ord("a"), ord("b"), ord("z"))         # 97 98 122
print(ord("A"), ord("Z"))                   # 65 90

# Katta va kichik harf farqi — doim 32
print(ord("a") - ord("A"))                  # 32

# Raqamlar ham ketma-ket
print(ord("0"), ord("9"))                   # 48 57
print(ord("7") - ord("0"))                  # 7  ← raqamni songa aylantirish

Amaliy foydalanish:

python
# Sezar shifri (eng oddiy shifrlash)
def sezar(matn: str, siljish: int) -> str:
    natija = []
    for h in matn:
        if "a" <= h <= "z":
            natija.append(chr((ord(h) - ord("a") + siljish) % 26 + ord("a")))
        elif "A" <= h <= "Z":
            natija.append(chr((ord(h) - ord("A") + siljish) % 26 + ord("A")))
        else:
            natija.append(h)
    return "".join(natija)


print(sezar("salom dunyo", 3))          # 'vdorp gxqbr'
print(sezar("vdorp gxqbr", -3))         # 'salom dunyo'

% 26 — 3.3-darsdagi aylanma indeks naqshi.

Alifboni hosil qilish:

python
alifbo = "".join(chr(ord("a") + i) for i in range(26))
print(alifbo)                   # abcdefghijklmnopqrstuvwxyz

# Yoki tayyor:
import string
print(string.ascii_lowercase)   # abcdefghijklmnopqrstuvwxyz
print(string.ascii_uppercase)
print(string.digits)            # 0123456789
print(string.punctuation)

2.3. CPython satrni qanday saqlaydi

Bu — PEP 393 "Flexible String Representation" (Python 3.3+).

Muammo: har bir belgini 4 baytda saqlash xotira isrofi. "salom" uchun 20 bayt kerak bo'lardi, aslida 5 yetadi.

Yechim: CPython satrdagi eng katta kod nuqtasiga qarab saqlash usulini tanlaydi:

Eng katta kod nuqtasi Har belgi uchun Nomi
≤ U+00FF (255) 1 bayt Latin-1
≤ U+FFFF (65535) 2 bayt UCS-2
≤ U+10FFFF 4 bayt UCS-4
python
import sys

namunalar = [
    "",                 # bo'sh
    "a",                # ASCII
    "abc",
    "abcdefghij",
    "é",                # Latin-1 chegarasida
    "ы",                # kirillitsa → 2 bayt
    "abcы",             # BITTA kirillitsa hammasini 2 baytga o'tkazadi
    "中",               # xitoy → 2 bayt
    "😊",               # emoji → 4 bayt
    "abc😊",            # BITTA emoji hammasini 4 baytga
]

print(f"{'Satr':<12} {'Uzunlik':>8} {'Xotira':>8} {'Belgi/bayt':>12}")
print("─" * 44)
for s in namunalar:
    hajm = sys.getsizeof(s)
    bayt = (hajm - sys.getsizeof("")) / len(s) if s else 0
    print(f"{s!r:<12} {len(s):>8} {hajm:>8} {bayt:>12.1f}")
text
Satr             Uzunlik   Xotira   Belgi/bayt
────────────────────────────────────────────
''                     0       49          0.0
'a'                    1       50          1.0
'abc'                  3       52          1.0
'abcdefghij'          10       59          1.0
'é'                    1       75         26.0
'ы'                    1       76         27.0
'abcы'                 4       82          8.2
'中'                   1       76         27.0
'😊'                   1       80         31.0
'abc😊'                4       92         10.8

Muhim kuzatish: "abcы" — to'rt belgi, lekin bitta kirillitsa harfi butun satrni 2 baytli rejimga o'tkazdi.

Amaliy natija:

python
# Katta matnda bitta emoji xotirani 4 barobar oshiradi
oddiy = "a" * 1_000_000
emojili = "a" * 999_999 + "😊"

print(f"Oddiy:   {sys.getsizeof(oddiy):>12,} bayt")
print(f"Emojili: {sys.getsizeof(emojili):>12,} bayt")
text
Oddiy:      1,000,049 bayt
Emojili:    4,000,056 bayt

Bu — katta matn qayta ishlashda ahamiyatli bo'lishi mumkin.

Nega bunday qilingan? Chunki indekslash O(1) bo'lishi kerak. Agar belgilar turli uzunlikda saqlansa (UTF-8 kabi), matn[1000] ni topish uchun boshidan sanash kerak bo'lardi — O(n).

Sobit uzunlik → to'g'ridan-to'g'ri hisoblash:

text
manzil = boshlanish + indeks × bayt_uzunligi

2.4. Indekslash — batafsil

python
matn = "Python"

#         P   y   t   h   o   n
#         0   1   2   3   4   5
#        -6  -5  -4  -3  -2  -1

print(matn[0])              # 'P'
print(matn[5])              # 'n'
print(matn[-1])             # 'n'
print(matn[-6])             # 'P'

Manfiy indeks formulasi:

text
matn[-i]  ≡  matn[len(matn) - i]
python
print(matn[-2] == matn[len(matn) - 2])      # True

Chegaradan chiqish:

python
print(matn[6])              # ❌ IndexError: string index out of range
print(matn[-7])             # ❌ IndexError

Kesimlar esa xato bermaydi (2-darsda batafsil):

python
print(matn[6:])             # '' — bo'sh satr
print(matn[10:20])          # ''

Xavfsiz indekslash:

python
def xavfsiz_olish(matn: str, indeks: int, sukut: str = "") -> str:
    """Indeks bo'yicha belgini xavfsiz qaytaradi."""
    if -len(matn) <= indeks < len(matn):
        return matn[indeks]
    return sukut


print(xavfsiz_olish("abc", 1))          # 'b'
print(xavfsiz_olish("abc", 10))         # ''
print(xavfsiz_olish("abc", 10, "?"))    # '?'

Yoki kesim bilan (soddaroq):

python
print("abc"[10:11] or "?")              # '?'

Belgi ham satr:

python
belgi = matn[0]
print(type(belgi))          # <class 'str'>
print(len(belgi))           # 1
print(belgi[0])             # 'P' — o'zi
print(belgi[0][0][0])       # 'P' — cheksiz

Pythonda alohida char turi yo'q. Bu — soddalik, lekin ba'zan chalkashtiradi.

2.5. Iteratsiya usullari

python
matn = "salom"

# 1. To'g'ridan-to'g'ri (eng oddiy va tez)
for harf in matn:
    print(harf, end=" ")
print()

# 2. Indeks bilan
for i in range(len(matn)):
    print(f"{i}:{matn[i]}", end=" ")
print()

# 3. enumerate — ikkalasi (TAVSIYA ETILADI)
for i, harf in enumerate(matn):
    print(f"{i}:{harf}", end=" ")
print()

# 4. Teskari
for harf in reversed(matn):
    print(harf, end=" ")
print()

# 5. Indeks va teskari
for i, harf in enumerate(reversed(matn)):
    print(f"{i}:{harf}", end=" ")
print()
text
s a l o m
0:s 1:a 2:l 3:o 4:m
0:s 1:a 2:l 3:o 4:m
m o l a s
0:m 1:o 2:l 3:a 4:s

enumerate boshlang'ich qiymat bilan:

python
for raqam, harf in enumerate(matn, start=1):
    print(f"{raqam}-belgi: {harf}")

5-qismda iteratsiyani to'liq o'rganamiz.

Tezlik:

python
import time

matn = "a" * 1_000_000

boshlandi = time.perf_counter()
for h in matn:
    pass
vaqt_toGridan = time.perf_counter() - boshlandi

boshlandi = time.perf_counter()
for i in range(len(matn)):
    matn[i]
vaqt_indeks = time.perf_counter() - boshlandi

print(f"To'g'ridan: {vaqt_toGridan * 1000:>7.1f} ms")
print(f"Indeks:     {vaqt_indeks * 1000:>7.1f} ms")

To'g'ridan-to'g'ri iteratsiya odatda 2-3 barobar tezroq — chunki range obyekti va indekslash amali qo'shimcha xarajat.

2.6. Internlash — nega ba'zi satrlar bitta obyekt

3.1-darsda tegib o'tgandik. Endi batafsil.

python
a = "salom"
b = "salom"
print(a is b)               # True

a = "salom dunyo!"
b = "salom dunyo!"
print(a is b)               # False

Internlash (interning) — bir xil satrni xotirada bir marta saqlash optimizatsiyasi.

CPython qanday satrlarni avtomatik internlaydi:

  1. Kompilyatsiya paytidagi doimiylar — kodda yozilgan satrlar
  2. Identifikatorga o'xshash satrlar — faqat harf, raqam va _ dan iborat
  3. Bo'sh satr va bitta belgili satrlar
python
# ✅ Internlanadi (identifikatorga o'xshash)
a = "salom"
b = "salom"
print(a is b)               # True

a = "foydalanuvchi_ismi"
b = "foydalanuvchi_ismi"
print(a is b)               # True

# ❌ Internlanmaydi (bo'sh joy va tinish belgisi bor)
a = "salom dunyo!"
b = "salom dunyo!"
print(a is b)               # False — interaktiv rejimda (REPL)

Nozik joy: oxirgi misolni .py faylga yozib ishga tushirsangiz, True chiqadi! Kompilyator bitta kod obyektidagi bir xil satr doimiylarini birlashtiradi — bu internlash emas, doimiylarni takrorlamaslik. REPL'da esa har qator alohida kompilyatsiya qilinadi, shuning uchun False. Xulosa bir xil: is natijasi kontekstga bog'liq, unga tayanib bo'lmaydi.

Ishlash paytida yaratilgan satrlar internlanmaydi:

python
a = "salom"
b = "sal" + "om"            # kompilyatsiya paytida birlashadi
print(a is b)               # True

qism = "sal"
c = qism + "om"             # ishlash paytida
print(a is c)               # False
print(a == c)               # True

Qo'lda internlash:

python
import sys

a = "salom dunyo!"
b = sys.intern("salom dunyo!")
c = sys.intern("salom dunyo!")

print(a is b)               # False (REPL'da; bitta faylda True bo'lishi mumkin)
print(b is c)               # True — har doim

Qachon foydali? Ko'p takrorlanadigan satrlar bilan ishlaganda:

python
# Katta ma'lumotda ustun nomlari millionlab marta takrorlanadi
import sys

ustunlar = [sys.intern(nom) for nom in ["ism", "yosh", "shahar"]]

# Endi taqqoslash `is` bilan tez ishlaydi va xotira tejaydi

Amaliy qoida:

Internlash — optimizatsiya tafsiloti, kafolat emas. is ni satrlar bilan hech qachon ishlatmang.

python
if matn is "salom":         # ❌ ishonchsiz, SyntaxWarning
if matn == "salom":         # ✅

2.7. Satr va ro'yxat — farqi

Satr ro'yxatga o'xshaydi, lekin muhim farqlar bor:

python
matn = "salom"
royxat = list(matn)         # ['s', 'a', 'l', 'o', 'm']

# ✅ Ikkalasida ishlaydi
print(matn[0], royxat[0])
print(len(matn), len(royxat))
print(matn[1:3], royxat[1:3])
print("a" in matn, "a" in royxat)

# ❌ Faqat ro'yxatda
royxat[0] = "S"             # ✅
# matn[0] = "S"             # ❌ TypeError

royxat.append("!")          # ✅
# matn.append("!")          # ❌ AttributeError

Asosiy farqlar:

str list
O'zgaruvchanlik O'zgarmas O'zgaruvchan
Elementlar Faqat belgilar Har qanday obyekt
in Pastki satr qidiradi Elementni qidiradi
Xeshlanadimi Ha (kalit bo'la oladi) Yo'q
Xotira Ixcham Ko'proq

in ning farqi — muhim:

python
print("lo" in "salom")              # True  — pastki satr
print("lo" in ["s","a","l","o","m"]) # False — bunday element yo'q
print("l" in ["s","a","l","o","m"])  # True

Xeshlanish:

python
lugat = {"salom": 1}                # ✅ satr kalit bo'la oladi
# lugat = {["a"]: 1}                # ❌ TypeError: unhashable type

print(hash("salom"))                # har seansda boshqa (xavfsizlik uchun)

Xesh har dastur ishga tushganda o'zgaradi — bu xesh to'qnashuvi hujumlaridan himoya (PYTHONHASHSEED).

Satrni "o'zgartirish" uchun ro'yxatga o'tish:

python
matn = "salom"

# ❌ Ishlamaydi
# matn[0] = "S"

# ✅ Ro'yxat orqali
harflar = list(matn)
harflar[0] = "S"
matn = "".join(harflar)
print(matn)                         # 'Salom'

# ✅ Yoki kesim bilan (soddaroq)
matn = "salom"
matn = "S" + matn[1:]
print(matn)                         # 'Salom'

2.8. Grafema — ko'rinadigan belgi

Bu — nozik, lekin muhim mavzu.

Bir "belgi" bir kod nuqtasi bo'lmasligi mumkin:

python
# Bir xil ko'rinadi, turli tuzilma
a = "é"                     # U+00E9 — tayyor belgi
b = "é"               # U+0065 + U+0301 (e + urg'u)

print(a, b)                 # é é — bir xil ko'rinadi
print(a == b)               # False!
print(len(a), len(b))       # 1 2
print([hex(ord(c)) for c in a])     # ['0xe9']
print([hex(ord(c)) for c in b])     # ['0x65', '0x301']

Normalizatsiya bu muammoni hal qiladi:

python
import unicodedata

a = "é"
b = "é"

print(unicodedata.normalize("NFC", a) == unicodedata.normalize("NFC", b))   # True
print(len(unicodedata.normalize("NFC", b)))     # 1
print(len(unicodedata.normalize("NFD", a)))     # 2
Shakl Nima qiladi
NFC Birlashtiradi (tayyor belgi)
NFD Ajratadi (asos + belgi)
NFKC Birlashtiradi + moslik almashtirish
NFKD Ajratadi + moslik almashtirish

Emoji bilan yanada murakkab:

python
oila = "👨‍👩‍👧‍👦"
print(len(oila))                    # 7! — bitta emoji ko'rinadi
print([hex(ord(c)) for c in oila])
text
7
['0x1f468', '0x200d', '0x1f469', '0x200d', '0x1f467', '0x200d', '0x1f466']

0x200d — "Zero Width Joiner", belgilarni birlashtiradi.

Amaliy natija:

python
matn = "Salom 👨‍👩‍👧‍👦"
print(len(matn))                    # 13, lekin ko'zga 7 ta ko'rinadi
print(matn[:7])                     # 'Salom 👨' — emoji buzildi!

Yechim: grafemalar bilan ishlash uchun tashqi kutubxona kerak:

bash
pip install grapheme
python
import grapheme
print(grapheme.length("👨‍👩‍👧‍👦"))     # 1

Amaliy maslahat: oddiy matn bilan ishlaganda bu haqda o'ylash shart emas. Lekin foydalanuvchi kiritmasi, ism, izoh bilan ishlaganda (ayniqsa uzunlikni cheklashda) e'tiborga oling.

2.9. Foydali funksiyalar

python
import string
import unicodedata

matn = "Salom, Dunyo! 123"

# Tekshirish (3.7-darsdan)
print("abc".isalpha())              # True
print("123".isdigit())              # True
print("abc123".isalnum())           # True
print("  ".isspace())               # True
print("Salom".istitle())            # True

# Unicode ma'lumoti
for belgi in "Aя中😊":
    print(f"  {belgi}  U+{ord(belgi):05X}  "
          f"{unicodedata.category(belgi):<3} "
          f"{unicodedata.name(belgi, '?')}")
text
  A  U+00041  Lu  LATIN CAPITAL LETTER A
  я  U+0044F  Ll  CYRILLIC SMALL LETTER YA
  中  U+04E2D  Lo  CJK UNIFIED IDEOGRAPH-4E2D
  😊  U+1F60A  So  SMILING FACE WITH SMILING EYES

Kategoriyalar:

Kod Ma'nosi
Lu Katta harf
Ll Kichik harf
Lo Boshqa harf (xitoy, arab)
Nd O'nlik raqam
Zs Bo'sh joy
So Boshqa belgi (emoji)
Po Tinish belgisi

string moduli doimiylari:

python
print(string.ascii_letters)         # a-zA-Z
print(string.ascii_lowercase)       # a-z
print(string.digits)                # 0-9
print(string.punctuation)           # !"#$%&'()*+,-./...
print(string.whitespace)            # ' \t\n\r\x0b\x0c'
print(string.printable)             # hammasi

3. Tez ma'lumotnoma

Kod nuqtalari

python
ord("A")            → 65
chr(65)             → 'A'
f"U+{ord(c):04X}"   → 'U+0041'

Ichki saqlash (PEP 393)

text
≤ U+00FF   → 1 bayt/belgi
≤ U+FFFF   → 2 bayt/belgi
> U+FFFF   → 4 bayt/belgi

Bitta emoji butun satrni 4 baytli rejimga o'tkazadi

Indekslash

python
matn[0]         birinchi
matn[-1]        oxirgi
matn[-i] ≡ matn[len(matn) - i]
matn[10]        IndexError
matn[10:11]     '' — xavfsiz

Iteratsiya

python
for h in matn:                  eng tez
for i, h in enumerate(matn):    indeks kerak bo'lsa
for h in reversed(matn):        teskari

Internlash

text
Identifikatorga o'xshash satrlar avtomatik internlanadi
sys.intern(s) — qo'lda
⚠️ `is` ni satrlar bilan ISHLATMANG

Unicode

python
unicodedata.name(c)         belgi nomi
unicodedata.category(c)     kategoriya
unicodedata.normalize("NFC", s)   normalizatsiya

4. Batafsil misollar

Misol 1 — Satrning ichki tuzilishini o'rganish

python
"""CPython satrni qanday saqlaydi."""

import sys
import unicodedata


def tahlil(matn: str) -> None:
    """Satrning ichki tuzilishini ko'rsatadi."""
    hajm = sys.getsizeof(matn)
    bosh_hajm = sys.getsizeof("")
    belgi_hajmi = (hajm - bosh_hajm) / len(matn) if matn else 0

    # Saqlash rejimini aniqlash
    eng_katta = max((ord(c) for c in matn), default=0)
    if eng_katta <= 0xFF:
        rejim = "Latin-1 (1 bayt)"
    elif eng_katta <= 0xFFFF:
        rejim = "UCS-2 (2 bayt)"
    else:
        rejim = "UCS-4 (4 bayt)"

    print(f"  Satr:          {matn!r}")
    print(f"  Uzunlik:       {len(matn)} belgi")
    print(f"  Xotira:        {hajm} bayt")
    print(f"  Belgi hajmi:   ~{belgi_hajmi:.1f} bayt")
    print(f"  Saqlash rejimi: {rejim}")
    print(f"  Eng katta kod:  U+{eng_katta:04X}")
    print(f"  UTF-8 da:      {len(matn.encode('utf-8'))} bayt")
    print()


print("=== Turli satrlar ===\n")
for m in ["abcdefghij", "abcdefghiы", "abcdefghi😊", "салом", "中文"]:
    tahlil(m)

print("=== Bitta belgi hammasini o'zgartiradi ===")
asos = "a" * 100
print(f"  100 ta 'a':        {sys.getsizeof(asos):>8,} bayt")
print(f"  99 ta 'a' + 'ы':   {sys.getsizeof('a' * 99 + 'ы'):>8,} bayt")
print(f"  99 ta 'a' + '😊':  {sys.getsizeof('a' * 99 + '😊'):>8,} bayt")

print("\n=== Katta matnda farq ===")
N = 1_000_000
oddiy = "a" * N
emojili = "a" * (N - 1) + "😊"
print(f"  {N:,} ta ASCII:      {sys.getsizeof(oddiy):>12,} bayt")
print(f"  {N:,} + 1 emoji:     {sys.getsizeof(emojili):>12,} bayt")
print(f"  Farq:               {sys.getsizeof(emojili) / sys.getsizeof(oddiy):>12.1f}x")
text
=== Turli satrlar ===

  Satr:          'abcdefghij'
  Uzunlik:       10 belgi
  Xotira:        59 bayt
  Belgi hajmi:   ~1.0 bayt
  Saqlash rejimi: Latin-1 (1 bayt)
  Eng katta kod:  U+006A
  UTF-8 da:      10 bayt

  Satr:          'abcdefghiы'
  Uzunlik:       10 belgi
  Xotira:        94 bayt
  Belgi hajmi:   ~4.5 bayt
  Saqlash rejimi: UCS-2 (2 bayt)
  Eng katta kod:  U+044B
  UTF-8 da:      11 bayt

  Satr:          'abcdefghi😊'
  Uzunlik:       10 belgi
  Xotira:        96 bayt
  Belgi hajmi:   ~4.7 bayt
  Saqlash rejimi: UCS-4 (4 bayt)
  Eng katta kod:  U+1F60A
  UTF-8 da:      13 bayt

=== Bitta belgi hammasini o'zgartiradi ===
  100 ta 'a':             149 bayt
  99 ta 'a' + 'ы':        274 bayt
  99 ta 'a' + '😊':       476 bayt

=== Katta matnda farq ===
  1,000,000 ta ASCII:      1,000,049 bayt
  1,000,000 + 1 emoji:     4,000,056 bayt
  Farq:                            4.0x

Nima ko'rsatdi: 2.3-bo'lim. Bitta belgi butun satrning xotira sarfini 4 barobar oshirishi mumkin.

Misol 2 — ord/chr bilan amaliy masalalar

python
"""Kod nuqtalari bilan ishlash."""

import string


def sezar(matn: str, siljish: int) -> str:
    """Sezar shifri — harflarni siljitadi."""
    natija = []
    for h in matn:
        if "a" <= h <= "z":
            yangi = (ord(h) - ord("a") + siljish) % 26 + ord("a")
            natija.append(chr(yangi))
        elif "A" <= h <= "Z":
            yangi = (ord(h) - ord("A") + siljish) % 26 + ord("A")
            natija.append(chr(yangi))
        else:
            natija.append(h)
    return "".join(natija)


def harf_pozitsiyasi(harf: str) -> int:
    """Harfning alifbodagi o'rnini qaytaradi (a=1)."""
    return ord(harf.lower()) - ord("a") + 1


def raqamga(belgi: str) -> int:
    """Raqam belgisini songa aylantiradi."""
    return ord(belgi) - ord("0")


def registr_almashtir(matn: str) -> str:
    """Katta-kichik harfni almashtiradi (32 farqidan foydalanib)."""
    natija = []
    for h in matn:
        if "a" <= h <= "z":
            natija.append(chr(ord(h) - 32))
        elif "A" <= h <= "Z":
            natija.append(chr(ord(h) + 32))
        else:
            natija.append(h)
    return "".join(natija)


print("=== Sezar shifri ===")
asl = "Salom, dunyo!"
shifr = sezar(asl, 3)
print(f"  Asl:      {asl}")
print(f"  Shifr:    {shifr}")
print(f"  Ochilgan: {sezar(shifr, -3)}")

print("\n=== Alifbodagi o'rin ===")
for h in "abzABZ":
    print(f"  {h!r} → {harf_pozitsiyasi(h):>2}")

print("\n=== Raqam belgisini songa ===")
raqamlar = "12345"
print(f"  {raqamlar!r} → {[raqamga(r) for r in raqamlar]}")
print(f"  Yig'indi: {sum(raqamga(r) for r in raqamlar)}")

print("\n=== Registr almashtirish ===")
print(f"  {registr_almashtir('Salom Dunyo 123')}")

print("\n=== Alifbo hosil qilish ===")
print(f"  chr bilan:  {''.join(chr(ord('a') + i) for i in range(26))}")
print(f"  string:     {string.ascii_lowercase}")

print("\n=== Belgi kodlari jadvali ===")
print(f"  {'Belgi':<8} {'Dec':>6} {'Hex':>8} {'Unicode':>10}")
print("  " + "─" * 34)
for h in "aA0 !ы中😊":
    print(f"  {h!r:<8} {ord(h):>6} {ord(h):>8X} {'U+' + format(ord(h), '04X'):>10}")
text
=== Sezar shifri ===
  Asl:      Salom, dunyo!
  Shifr:    Vdorp, gxqbr!
  Ochilgan: Salom, dunyo!

=== Alifbodagi o'rin ===
  'a' →  1
  'b' →  2
  'z' → 26
  'A' →  1
  'B' →  2
  'Z' → 26

=== Raqam belgisini songa ===
  '12345' → [1, 2, 3, 4, 5]
  Yig'indi: 15

=== Registr almashtirish ===
  sALOM dUNYO 123

=== Alifbo hosil qilish ===
  chr bilan:  abcdefghijklmnopqrstuvwxyz
  string:     abcdefghijklmnopqrstuvwxyz

=== Belgi kodlari jadvali ===
  Belgi       Dec      Hex    Unicode
  ──────────────────────────────────
  'a'          97       61     U+0061
  'A'          65       41     U+0041
  '0'          48       30     U+0030
  ' '          32       20     U+0020
  '!'          33       21     U+0021
  'ы'        1099      44B     U+044B
  '中'       20013     4E2D     U+4E2D
  '😊'      128522    1F60A     U+1F60A

Nima ko'rsatdi: 2.2-bo'lim.

Misol 3 — Internlash tajribasi

python
"""Satr internlashini kuzatish."""

import sys


def tekshir(nom: str, a: str, b: str) -> None:
    belgi = "✅ bitta obyekt" if a is b else "❌ ikkita obyekt"
    print(f"  {nom:<38} {belgi}")


print("=== 1. Kodda yozilgan satrlar ===")
a = "salom"
b = "salom"
tekshir("'salom' — identifikatorga o'xshash", a, b)

a = "foydalanuvchi_ismi"
b = "foydalanuvchi_ismi"
tekshir("'foydalanuvchi_ismi'", a, b)

a = "salom dunyo!"
b = "salom dunyo!"
tekshir("'salom dunyo!' (bitta faylda)", a, b)


def alohida(kod: str) -> str:
    """Satrni ALOHIDA kompilyatsiya qiladi — REPL'dagi kabi."""
    return eval(compile(kod, "<alohida>", "eval"))


tekshir("'salom dunyo!' (alohida kompilyatsiya)",
        alohida('"salom dunyo!"'), alohida('"salom dunyo!"'))
tekshir("'salom' (alohida kompilyatsiya)",
        alohida('"salom"'), alohida('"salom"'))

a = ""
b = ""
tekshir("bo'sh satr", a, b)

a = "x"
b = "x"
tekshir("bitta belgi", a, b)

print("\n=== 2. Kompilyatsiya vs ishlash paytida ===")
a = "salom"
b = "sal" + "om"                # kompilyator birlashtiradi
tekshir("'sal' + 'om' (doimiylar)", a, b)

qism = "sal"
c = qism + "om"                 # ishlash paytida
tekshir("qism + 'om' (o'zgaruvchi)", a, c)
print(f"  {'Lekin == bilan:':<38} {a == c}")

print("\n=== 3. sys.intern bilan ===")
a = alohida('"salom dunyo!"')
b = alohida('"salom dunyo!"')
c = sys.intern(alohida('"salom dunyo!"'))
d = sys.intern(alohida('"salom dunyo!"'))
tekshir("internlanmagan a va b", a, b)
tekshir("internlangan c va d", c, d)

print("\n=== 4. Xotira tejash namoyishi ===")
N = 100_000

# Har biri alohida obyekt
alohida = [f"ustun_{i % 10}" for i in range(N)]

# Internlangan
internlangan = [sys.intern(f"ustun_{i % 10}") for i in range(N)]

noyob_alohida = len({id(s) for s in alohida})
noyob_intern = len({id(s) for s in internlangan})

print(f"  {N:,} ta satr, 10 xil qiymat")
print(f"  Internlashsiz noyob obyektlar: {noyob_alohida:>8,}")
print(f"  Internlash bilan:              {noyob_intern:>8,}")

print("\n=== 5. ⚠️ NEGA `is` ISHLATMASLIK KERAK ===")
kutilgan = "ha"
harf = "h"
kirish = harf + "a"                 # ishlash paytida — foydalanuvchi kiritgandek
print(f"  kirish = harf + 'a' = {kirish!r}")
print(f"  kirish is kutilgan  →  {kirish is kutilgan}")
print(f"  kirish == kutilgan  →  {kirish == kutilgan}")
print("\n  Xulosa: satrlar bilan DOIM == ishlating.")

Natijaning muhim qismi:

text
=== 1. Kodda yozilgan satrlar ===
  'salom' — identifikatorga o'xshash     ✅ bitta obyekt
  'foydalanuvchi_ismi'                   ✅ bitta obyekt
  'salom dunyo!' (bitta faylda)          ✅ bitta obyekt
  'salom dunyo!' (alohida kompilyatsiya) ❌ ikkita obyekt
  'salom' (alohida kompilyatsiya)        ✅ bitta obyekt
  bo'sh satr                             ✅ bitta obyekt
  bitta belgi                            ✅ bitta obyekt

=== 2. Kompilyatsiya vs ishlash paytida ===
  'sal' + 'om' (doimiylar)               ✅ bitta obyekt
  qism + 'om' (o'zgaruvchi)              ❌ ikkita obyekt
  Lekin == bilan:                        True

=== 3. sys.intern bilan ===
  internlanmagan a va b                  ❌ ikkita obyekt
  internlangan c va d                    ✅ bitta obyekt

=== 4. Xotira tejash namoyishi ===
  100,000 ta satr, 10 xil qiymat
  Internlashsiz noyob obyektlar:  100,000
  Internlash bilan:                    10

Eslatma: is natijalari CPython'ning amalga oshiruv tafsiloti — versiya va kontekstga (fayl yoki REPL) qarab o'zgaradi. Satrlarni doim == bilan solishtiring.

Nima ko'rsatdi: 2.6-bo'lim.

Misol 4 — Grafema muammosi

python
"""Ko'rinadigan belgi va kod nuqtasi farqi."""

import unicodedata


def tahlil(matn: str, nom: str) -> None:
    print(f"\n  {nom}: {matn!r}")
    print(f"    Ko'rinishi:    {matn}")
    print(f"    len():         {len(matn)}")
    print(f"    Kod nuqtalari:")
    for c in matn:
        ism = unicodedata.name(c, "NOMA'LUM")
        print(f"      U+{ord(c):05X}  {unicodedata.category(c)}  {ism}")


print("=== 1. Bir xil ko'rinish, turli tuzilma ===")
tayyor = "é"                    # U+00E9
qismli = "é"              # e + urg'u

tahlil(tayyor, "Tayyor belgi (NFC)")
tahlil(qismli, "Qismlardan (NFD)")

print(f"\n  Teng?          {tayyor == qismli}")
print(f"  NFC dan keyin: "
      f"{unicodedata.normalize('NFC', tayyor) == unicodedata.normalize('NFC', qismli)}")

print("\n\n=== 2. Emoji — bir belgi, ko'p kod nuqtasi ===")
oila = "👨‍👩‍👧‍👦"
tahlil(oila, "Oila emojisi")

print("\n  Kesish muammosi:")
matn = "Salom 👨‍👩‍👧‍👦 dunyo"
print(f"    To'liq:      {matn}")
print(f"    len():       {len(matn)}")
print(f"    matn[:8]:    {matn[:8]}  ← emoji buzildi")
print(f"    matn[:13]:   {matn[:13]}")

print("\n\n=== 3. Amaliy: qidiruv muammosi ===")
foydalanuvchilar = ["José", "José", "Jose"]
qidiruv = "José"

print(f"  Qidiruv: {qidiruv!r}")
for f in foydalanuvchilar:
    oddiy = f == qidiruv
    normal = (unicodedata.normalize("NFC", f)
              == unicodedata.normalize("NFC", qidiruv))
    print(f"    {f!r:<16} oddiy={oddiy!s:<6} normalizatsiya bilan={normal}")

print("\n\n=== 4. Yechim: doim normalizatsiya qiling ===")


def normalize(matn: str) -> str:
    """Qidiruv va solishtirish uchun matnni normallashtiradi."""
    return unicodedata.normalize("NFC", matn.strip())


print("  Ma'lumotlar bazasiga yozishdan oldin va qidirishda")
print("  normalize() ni qo'llang — muammo yo'qoladi.")
text
=== 1. Bir xil ko'rinish, turli tuzilma ===

  Tayyor belgi (NFC): 'é'
    Ko'rinishi:    é
    len():         1
    Kod nuqtalari:
      U+000E9  Ll  LATIN SMALL LETTER E WITH ACUTE

  Qismlardan (NFD): 'é'
    Ko'rinishi:    é
    len():         2
    Kod nuqtalari:
      U+00065  Ll  LATIN SMALL LETTER E
      U+00301  Mn  COMBINING ACUTE ACCENT

  Teng?          False
  NFC dan keyin: True

=== 2. Emoji — bir belgi, ko'p kod nuqtasi ===

  Oila emojisi: '👨‍👩‍👧‍👦'
    len():         7
    ...

  Kesish muammosi:
    To'liq:      Salom 👨‍👩‍👧‍👦 dunyo
    len():       19
    matn[:8]:    Salom 👨‍  ← emoji buzildi

=== 3. Amaliy: qidiruv muammosi ===
  Qidiruv: 'José'
    'José'           oddiy=True   normalizatsiya bilan=True
    'José'           oddiy=False  normalizatsiya bilan=True
    'Jose'           oddiy=False  normalizatsiya bilan=False

Nima ko'rsatdi: 2.8-bo'lim. Bu — foydalanuvchi ismlari va qidiruv bilan ishlaganda haqiqiy muammo.


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

Noto'g'ri fikr To'g'risi
"len(matn) baytlar sonini beradi" Kod nuqtalari sonini. Baytlar uchun .encode()
"Har bir belgi 4 bayt" CPython eng katta kod nuqtasiga qarab 1, 2 yoki 4 bayt tanlaydi
"a is b satrlar uchun ishlaydi" Ba'zan. Internlash — optimizatsiya, kafolat emas
"Bitta belgi = bitta kod nuqtasi" Emoji va urg'uli harflar bir necha kod nuqtasi bo'lishi mumkin
"matn[0] str emas" str. Pythonda alohida char turi yo'q
"Satr — belgilar ro'yxati" O'xshash, lekin o'zgarmas va in pastki satr qidiradi
"Emoji qo'shish zararsiz" Butun satrni 4 baytli rejimga o'tkazadi
""é" == "é" doim True" Turli normalizatsiya shakllarida False bo'lishi mumkin

6. Keng tarqalgan xatolar va yechimlari

1. IndexError: string index out of range

python
if matn:                        # ✅ bo'sh emasligini tekshiring
    birinchi = matn[0]

birinchi = matn[:1]             # ✅ kesim — xavfsiz

2. is bilan satr solishtirish

python
if kirish is "ha":              # ❌ SyntaxWarning, ishonchsiz
if kirish == "ha":              # ✅

3. TypeError: 'str' object does not support item assignment

python
matn[0] = "S"                   # ❌
matn = "S" + matn[1:]           # ✅

4. Unicode normalizatsiya muammosi

python
if ism == qidiruv:                                          # ❌
if normalize("NFC", ism) == normalize("NFC", qidiruv):      # ✅

5. Emoji bilan uzunlik cheklash

python
qisqa = izoh[:100]              # ❌ emoji buzilishi mumkin

# Xavfsizroq — baytlarda cheklash
qisqa = izoh.encode("utf-8")[:400].decode("utf-8", errors="ignore")

6. ord() ko'p belgili satrga

python
ord("ab")                       # ❌ TypeError: expected a character
ord("a")                        # ✅
[ord(c) for c in "ab"]          # ✅ [97, 98]

7. chr() chegaradan tashqarida

python
chr(1_114_112)                  # ❌ ValueError
chr(0x10FFFF)                   # ✅ eng katta

8. Katta matnda emoji xotira sarfi

python
# Millionlab qatorli logda bitta emoji butun buferni 4x qiladi
# Yechim: emoji bo'lgan qatorlarni alohida saqlash yoki
# baytlarda ishlash (16-qism)

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 4.2-dars: kesimlar — indekslashning davomi
  • 4.8-dars: Unicode to'liq — kod nuqtalari, tekisliklar, kategoriyalar
  • 4.9-dars: encode/decode — kod nuqtasi ↔ bayt
  • 4.12-dars: o'zgarmaslik va internlash chuqur
  • 5-qism: iteratsiya, enumerate
  • 6-qism: satr va ro'yxat o'rtasidagi aylantirish, xeshlash
  • 16-qism: fayllar, kodlash, baytlar
  • 15-qism: muntazam ifodalar — Unicode kategoriyalari
  • 23-qism: ma'lumotlar bazasida matn saqlash (bayt va belgi farqi)

8. Eng yaxshi amaliyotlar

  1. is ni satrlar bilan hech qachon ishlatmang. Internlash — optimizatsiya tafsiloti.

  2. Foydalanuvchi kiritmasini normallashtiring. unicodedata.normalize("NFC", matn) — qidiruv va solishtirishda muammoni oldini oladi.

  3. len() belgilarni sanashini eslang. Ma'lumotlar bazasi cheklovi baytlarda bo'lishi mumkin.

  4. Indeks o'rniga kesim ishlating. matn[:1] matn[0] dan xavfsizroq.

  5. for h in matn: — eng tez iteratsiya. range(len(matn)) kerak emas.

  6. enumerate indeks kerak bo'lganda. for i, h in enumerate(matn).

  7. Emoji va maxsus belgi bilan uzunlik cheklashda ehtiyot bo'ling. Kesish belgini buzishi mumkin.

  8. string modulidagi doimiylardan foydalaning. string.ascii_lowercase — qo'lda yozishdan yaxshiroq.


9. Amaliy topshiriq

Vazifa 1: Kod nuqtalari

python
1.  ord("A")
2.  ord("a") - ord("A")
3.  chr(ord("a") + 25)
4.  len("салом")
5.  len("салом".encode("utf-8"))
6.  ord("😊")
7.  len("👨‍👩‍👧‍👦")
Javoblar
  1. 65
  2. 32
  3. 'z'
  4. 5 — belgilar
  5. 10 — kirillitsa UTF-8 da 2 bayt
  6. 128522
  7. 7 — ZWJ bilan birlashtirilgan

Vazifa 2: Xotira tahlili

1-misoldagi tahlil() funksiyasini ishlatib, quyidagilarni solishtiring:

python
["hello", "héllo", "hеllo"]     # 3-satrda kirilcha 'е'!

Nega uchinchisi ko'proq joy egallaydi? (Diqqat: е kirilcha, e lotincha)

Vazifa 3: Sezar shifri

2-misoldagi sezar() ni kengaytiring:

  1. Kirillitsa harflarini ham qo'llab-quvvatlasin
  2. sindir(shifr) funksiyasini yozing — barcha 26 siljishni sinab ko'rsin
  3. Eng ehtimolli siljishni toping (eng ko'p uchraydigan harf a yoki e bo'lishi kerak)

Vazifa 4: Internlash

python
a = "test_nom"
b = "test_nom"
c = "test" + "_nom"
qism = "test"
d = qism + "_nom"
e = sys.intern(qism + "_nom")
f = sys.intern("test_nom")

Qaysi juftliklar is bo'yicha teng? Oldin bashorat qiling.

Javoblar
  • a is b → True (ikkalasi ham kompilyatsiya doimiysi, internlangan)
  • a is c → True (kompilyator "test" + "_nom" ni birlashtiradi)
  • a is d → False (ishlash paytida yaratildi)
  • e is f → True (ikkalasi internlangan)
  • a is e → True (a ham internlangan)

Vazifa 5: Belgi statistikasi

Funksiya yozing: matndagi har bir belgi kategoriyasi bo'yicha statistika chiqarsin.

python
belgi_statistikasi("Salom, Dunyo! 123 😊")
text
  Lu (katta harf):    2
  Ll (kichik harf):   8
  Nd (raqam):         3
  Zs (bo'sh joy):     3
  Po (tinish):        2
  So (belgi):         1
Yechim
python
import unicodedata
from collections import Counter

NOMLAR = {
    "Lu": "katta harf", "Ll": "kichik harf", "Nd": "raqam",
    "Zs": "bo'sh joy", "Po": "tinish", "So": "belgi",
    "Lo": "boshqa harf", "Sc": "valyuta",
}


def belgi_statistikasi(matn: str) -> None:
    hisob = Counter(unicodedata.category(c) for c in matn)
    for kod, soni in hisob.most_common():
        nom = NOMLAR.get(kod, "boshqa")
        print(f"  {kod} ({nom}): {soni}")

Counter — 15-qismda o'rganamiz.

Vazifa 6: Normalizatsiya

xavfsiz_solishtir(a, b) funksiyasini yozing:

  • Bo'sh joylarni tozalasin
  • Kichik harfga o'tkazsin
  • NFC normalizatsiya qilsin
  • Keyin solishtirsin

Sinang: "José", " josé ", "JOSÉ".

Vazifa 7: O'ylash

Nega CPython satrlarni sobit uzunlikda saqlaydi (1/2/4 bayt), UTF-8 da emas? UTF-8 ixchamroq-ku.

Javob

Indekslash tezligi uchun.

UTF-8 da har bir belgi 1–4 bayt egallaydi. matn[1000] ni topish uchun boshidan sanash kerak bo'lardi — O(n).

Sobit uzunlikda esa oddiy hisob:

text
manzil = boshlanish + 1000 × bayt_uzunligi

Bu — O(1).

Solishtiring: Go va Rust satrlarni UTF-8 da saqlaydi va shuning uchun ularda s[i] bayt qaytaradi, belgi emas. Belgi bo'yicha yurish uchun maxsus iterator kerak.

Python soddalikni tanladi: matn[i] doim belgi qaytaradi va tez ishlaydi. Narxi — ba'zan ko'proq xotira.

Bu — 1.5-darsdagi "Soddalik murakkablikdan yaxshi" qoidasining amaliy namunasi.

Nimani mustahkamlaydi: 2.2, 2.3, 2.6, 2.8, 2.9-bo'limlar.


Xulosa

Bu darsda satrning ichki tuzilishini o'rgandik.

Eng muhim uch fikr:

  1. Satr — kod nuqtalari ketma-ketligi. len() belgilarni sanaydi, baytlarni emas. CPython eng katta kod nuqtasiga qarab 1, 2 yoki 4 bayt tanlaydi — bitta emoji butun satrni 4 barobar kattalashtiradi.

  2. Internlash — optimizatsiya, kafolat emas. Identifikatorga o'xshash satrlar avtomatik internlanadi, boshqalari yo'q. Shuning uchun satrlar bilan doim ==, hech qachon is.

  3. Ko'rinadigan belgi ≠ kod nuqtasi. "é" bir yoki ikki kod nuqtasi bo'lishi mumkin. Foydalanuvchi kiritmasini unicodedata.normalize("NFC", ...) bilan normallashtiring.

Keyingi darsda kesimlarni (slicing) chuqur o'rganamiz: slice obyekti, salbiy qadam, kesim bilan o'zlashtirish va eng foydali idiomalar.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
4.1-dars: Satrning ichki tuzilishi va indekslash — IlmHamroh