Mundarija (24)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. O'zgarmaslik nima
- 2.2. Nega o'zgarmas — to'rt sabab
- 2.3. Xeshlash — chuqur
- 2.4. Internlash — chuqur
- 2.5. Satr qurish — barcha usul
- 2.6. io.StringIO — bufer
- 2.7. bytearray — o'zgaruvchan baytlar
- 2.8. Xotira va nusxalash
- 2.9. sys.getsizeof va haqiqiy xotira
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — O'zgarmaslikni ko'rish
- Misol 2 — Satr qurish usullari va tezligi
- Misol 3 — Internlash va xotira
- Misol 4 — Amaliy: HTML generatori
- 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
- 4-QISM YAKUNLANDI
4.12-dars: Immutability — satr nega o'zgarmaydi
4-QISM — SATRLAR CHUQUR · 12-dars · 4-qismning yakuni
1. Kirish va motivatsiya
3.7-darsda o'rgandik: satrlar o'zgarmas. Endi savol — nega?
Bu shunchaki cheklov emas. Bu — Python dizaynidagi asosiy qarorlardan biri va u ko'p narsani belgilaydi:
matn = "salom"
matn[0] = "S" # ❌ TypeErrorlugat = {"salom": 1} # ✅ satr kalit bo'la oladi
lugat = {["a"]: 1} # ❌ ro'yxat bo'la olmaydia = "salom"
b = "salom"
print(a is b) # True — bitta obyekt# Va bu — eng amaliy oqibat:
natija = ""
for i in range(100_000):
natija += str(i) # ⚠️ har safar YANGI satrBu darsda:
- O'zgarmaslikning to'rt sababi
- Xeshlanish — nega bu o'zgarmaslikni talab qiladi
- Internlash — chuqur (4.1-darsning davomi)
- Satr qurishning barcha usuli va ularning tezligi
StringIO,bytearray,array— muqobil vositalar
2. Nazariya — chuqur tushuntirish
2.1. O'zgarmaslik nima
O'zgarmas (immutable) obyekt yaratilgandan keyin o'zgartirilmaydi.
matn = "salom"
# matn[0] = "S" # ❌ TypeError
# matn.append("!") # ❌ AttributeError
# del matn[0] # ❌ TypeError
matn = "Salom" # ✅ lekin bu — YANGI obyektPythondagi o'zgarmas turlar:
int float complex bool
str bytes tuple frozenset
range NoneTypeO'zgaruvchan turlar:
list dict set bytearray
sinf namunalari (odatda)Tekshirish — id() bilan (3.1-dars):
matn = "salom"
print(id(matn)) # 140234...
matn = matn.upper()
print(id(matn)) # 140235... ← BOSHQA obyekt
royxat = [1, 2]
print(id(royxat)) # 140236...
royxat.append(3)
print(id(royxat)) # 140236... ← BIR XIL obyektBarcha satr metodi yangi satr qaytaradi:
matn = " salom "
print(id(matn))
print(id(matn.strip())) # boshqa
print(id(matn.upper())) # boshqa
print(id(matn.replace("a", "A"))) # boshqa
print(matn) # ' salom ' — o'zgarmadiIstisno — o'zgarish bo'lmasa:
matn = "salom"
print(matn is matn.lower()) # True — o'zgarish yo'q, o'zini qaytardi
print(matn is matn.replace("z", "x")) # True — topilmadi
print(matn is matn[:]) # True — butun kesim
print(matn is matn.upper()) # False — o'zgardiCPython optimizatsiyasi: o'zgarish bo'lmasa yangi obyekt yaratmaydi. Bu — kafolat emas, amalga oshiruv tafsiloti.
2.2. Nega o'zgarmas — to'rt sabab
1. Xeshlanish — lug'at kaliti bo'la olish
lugat = {"salom": 1} # ✅
tuplam = {"a", "b"} # ✅
# lugat = {["a"]: 1} # ❌ TypeError: unhashable type: 'list'Nega ro'yxat kalit bo'la olmaydi?
kalit = [1, 2]
lugat = {kalit: "qiymat"} # tasavvur qiling, ruxsat berilgan
kalit.append(3) # xesh o'zgardi!
lugat[[1, 2, 3]] # qaysi katakda qidirish kerak?Xesh jadval element joyini xesh qiymati bo'yicha topadi. Kalit o'zgarsa — element yo'qoladi.
O'zgarmas obyektning xeshi hech qachon o'zgarmaydi:
matn = "salom"
print(hash(matn)) # har safar bir xil (shu seansda)
# hash([1, 2]) # ❌ TypeError
print(hash((1, 2))) # ✅ juftlik o'zgarmas2. Xavfsizlik — funksiyaga uzatishda
def qayta_ishla(matn: str) -> str:
return matn.upper()
asl = "salom"
natija = qayta_ishla(asl)
print(asl) # 'salom' — KAFOLATLANGAN o'zgarmaydiRo'yxat bilan bunday kafolat yo'q:
def qayta_ishla(royxat: list) -> list:
royxat.append("qo'shildi") # tashqaridagi ro'yxat o'zgardi!
return royxat
asl = [1, 2]
qayta_ishla(asl)
print(asl) # [1, 2, "qo'shildi"] — ⚠️3. Optimizatsiya — internlash va keshlash
a = "salom"
b = "salom"
print(a is b) # True — bitta obyekt, xotira tejaldiBu faqat o'zgarmas obyektlar uchun xavfsiz.
4. Ko'p oqimli ishlash (thread safety)
O'zgarmas obyektni bir necha oqim qulflashsiz o'qishi mumkin — u o'zgarmaydi.
14-qismda batafsil.
2.3. Xeshlash — chuqur
matn = "salom"
print(hash(matn)) # masalan: -8455457274524873561Xesh har dastur ishga tushganda o'zgaradi:
python -c "print(hash('salom'))"
python -c "print(hash('salom'))"
# Turli natijalar!Bu — xavfsizlik chorasi (PEP 456). Xesh to'qnashuvi hujumlaridan himoya: hujumchi bir xil xeshli minglab kalit yuborib, lug'atni O(n²) ga tushira olmaydi.
O'chirish (test uchun):
PYTHONHASHSEED=0 python main.pySonlar uchun xesh barqaror:
print(hash(42)) # 42
print(hash(42.0)) # 42 — teng qiymat, teng xesh
print(hash(True)) # 1
print(hash(-1)) # -2 — maxsus holatXesh qoidasi:
a == b → hash(a) == hash(b) MAJBURIY
hash(a) == hash(b) ↛ a == b to'qnashuv mumkinprint(42 == 42.0) # True
print(hash(42) == hash(42.0)) # True — qoida bajarildi
print(1 == True) # True
print(hash(1) == hash(True)) # True
lugat = {1: "bir", True: "rost", 1.0: "kasr"}
print(lugat) # {1: 'kasr'} — hammasi bitta kalit!O'z sinfingiz uchun (3.11-dars):
class Nuqta:
def __init__(self, x, y):
self.x, self.y = x, y
def __eq__(self, boshqa):
return (self.x, self.y) == (boshqa.x, boshqa.y)
def __hash__(self):
return hash((self.x, self.y)) # juftlik xeshi __eq__ yozsangiz __hash__ avtomatik o'chadi:
class A:
def __eq__(self, b): return True
# hash(A()) # ❌ TypeError: unhashable typefrozen=True bilan dataclass:
from dataclasses import dataclass
@dataclass(frozen=True)
class Nuqta:
x: int
y: int
n = Nuqta(1, 2)
# n.x = 5 # ❌ FrozenInstanceError
print(hash(n)) # ✅ avtomatik __hash__
print({n}) # ✅ to'plamga qo'shish mumkin8-qismda batafsil.
2.4. Internlash — chuqur
4.1-darsda tanishgandik. Endi to'liq.
Avtomatik internlash qoidalari (CPython):
# 1. Kompilyatsiya paytidagi doimiylar
a = "salom"
b = "salom"
print(a is b) # True
# 2. Identifikatorga o'xshash satrlar
a = "foydalanuvchi_ismi"
b = "foydalanuvchi_ismi"
print(a is b) # True
# 3. Bo'sh satr va bitta belgi
print("" is "") # True
print("a" is "a") # True
# 4. Doimiylarni birlashtirish
a = "salom"
b = "sal" + "om" # kompilyator birlashtiradi
print(a is b) # TrueInternlanmaydiganlar:
# Bo'sh joy, tinish belgisi bor
a = "salom dunyo!"
b = "salom dunyo!"
print(a is b) # False
# Ishlash paytida yaratilgan
qism = "sal"
c = qism + "om"
print("salom" is c) # False
# f-satr
ism = "salom"
d = f"{ism}"
print("salom" is d) # ehtimol FalseQo'lda internlash:
import sys
a = sys.intern("salom dunyo!")
b = sys.intern("salom dunyo!")
print(a is b) # TrueQachon foydali:
# Katta ma'lumotda takrorlanuvchi satrlar
import sys
# ❌ Har biri alohida obyekt
yozuvlar = [{"turi": f"turi_{i % 5}"} for i in range(1_000_000)]
# ✅ Beshta obyekt
TURLAR = [sys.intern(f"turi_{i}") for i in range(5)]
yozuvlar = [{"turi": TURLAR[i % 5]} for i in range(1_000_000)]Xotira tejaladi va is bilan solishtirish tezlashadi.
Lekin:
if matn is "salom": # ❌ HECH QACHON
if matn == "salom": # ✅Internlash — optimizatsiya tafsiloti, kafolat emas. U CPython versiyalari orasida o'zgaradi.
Kichik sonlar keshi (3.1-dars):
print(256 is 256) # True — keshlangan
print(257 is 257) # False — (faylda)Diapazon: -5 dan 256 gacha.
2.5. Satr qurish — barcha usul
Muammo: satrlar o'zgarmas, shuning uchun += har safar yangi satr yaratadi.
natija = ""
for i in range(5):
natija += str(i)
print(f" {i}: id={id(natija)}, qiymat={natija!r}") 0: id=140234567890, qiymat='0'
1: id=140234567920, qiymat='01'
2: id=140234567950, qiymat='012'
...Har safar boshqa id — yangi obyekt.
Nazariy murakkablik: O(n²) — chunki har safar butun satr nusxalanadi.
Barcha usul:
import io
import time
N = 100_000
def usul_qoshish():
natija = ""
for i in range(N):
natija += str(i)
return natija
def usul_join_generator():
return "".join(str(i) for i in range(N))
def usul_join_royxat():
bolaklar = []
for i in range(N):
bolaklar.append(str(i))
return "".join(bolaklar)
def usul_join_comprehension():
return "".join([str(i) for i in range(N)])
def usul_stringio():
bufer = io.StringIO()
for i in range(N):
bufer.write(str(i))
return bufer.getvalue()
def usul_map():
return "".join(map(str, range(N)))
usullar = [
("+= bilan", usul_qoshish),
("join(generator)", usul_join_generator),
("ro'yxat + join", usul_join_royxat),
("join(comprehension)", usul_join_comprehension),
("StringIO", usul_stringio),
("join(map)", usul_map),
]
natijalar = []
for nom, f in usullar:
boshlandi = time.perf_counter()
natija = f()
vaqt = time.perf_counter() - boshlandi
natijalar.append((nom, vaqt))
eng_tez = min(v for _, v in natijalar)
for nom, vaqt in sorted(natijalar, key=lambda x: x[1]):
print(f" {nom:<22} {vaqt * 1000:>8.1f} ms {vaqt / eng_tez:>5.1f}x")Tipik natija:
join(map) 18.2 ms 1.0x
join(comprehension) 21.4 ms 1.2x
join(generator) 24.8 ms 1.4x
ro'yxat + join 28.3 ms 1.6x
StringIO 31.7 ms 1.7x
+= bilan 42.1 ms 2.3x += kutilganidan tezroq — nega?
CPython'da optimizatsiya bor: agar satrga faqat bitta havola bo'lsa, += uni joyida kengaytirishga urinadi (realloc).
natija = ""
natija += "a" # bitta havola → optimizatsiya ishlaydiLekin bu optimizatsiya buziladi:
natija = ""
nusxa = natija # ikkinchi havola!
for i in range(N):
natija += str(i) # ❌ optimizatsiya ishlamaydi → O(n²)Va u kafolatlanmagan:
- PyPy, Jython, IronPython'da yo'q
- CPython versiyalari orasida o'zgarishi mumkin
- Murakkab kodda ishlamasligi mumkin
Qoida:
Katta satr qurishda doim
joinishlating.+=— faqat bir necha bo'lak uchun.
2.6. io.StringIO — bufer
import io
bufer = io.StringIO()
bufer.write("Salom")
bufer.write(", ")
bufer.write("dunyo!")
print(bufer.getvalue()) # 'Salom, dunyo!'
bufer.close()print bilan:
bufer = io.StringIO()
print("Birinchi", file=bufer)
print("Ikkinchi", file=bufer)
print(bufer.getvalue(), end="")Kontekst menejeri sifatida:
with io.StringIO() as bufer:
bufer.write("matn")
natija = bufer.getvalue()Qachon StringIO?
| Vaziyat | Vosita |
|---|---|
| Barcha bo'lak oldindan ma'lum | join |
| Bo'laklar shartli qo'shiladi | join + ro'yxat |
Fayl kabi interfeys kerak (file=) |
StringIO |
print/csv.writer chiqishini ushlash |
StringIO |
Chiqishni ushlash:
import contextlib
import io
bufer = io.StringIO()
with contextlib.redirect_stdout(bufer):
print("Bu buferda")
print("Bu ham")
print(f"Ushlangan: {bufer.getvalue()!r}")CSV bilan (4.5-dars):
import csv
chiqish = io.StringIO()
yozuvchi = csv.writer(chiqish)
yozuvchi.writerow(["a", "b"])
print(chiqish.getvalue())2.7. bytearray — o'zgaruvchan baytlar
ba = bytearray(b"salom")
ba[0] = 83 # ✅ o'zgartirish
ba.append(33) # ✅ qo'shish
ba.extend(b"!!") # ✅
print(ba) # bytearray(b'Salom!!!')
print(bytes(ba)) # b'Salom!!!'bytes va bytearray:
bytes |
bytearray |
|
|---|---|---|
| O'zgaruvchanlik | O'zgarmas | O'zgaruvchan |
| Xeshlanadi | Ha | Yo'q |
| Literal | b"..." |
Yo'q |
Qachon bytearray?
# Ikkilik ma'lumotni bosqichma-bosqich qurish
bufer = bytearray()
for bolak in bolaklar:
bufer.extend(bolak)
malumot = bytes(bufer)Bu — bytes bilan += dan ancha tez (bytes ham o'zgarmas).
Satr uchun analog yo'q — Pythonda mutable string turi yo'q. Sabab: join yetarlicha tez va sodda.
2.8. Xotira va nusxalash
Kesim yangi obyekt yaratadi (4.2-dars):
import sys
katta = "a" * 1_000_000
print(sys.getsizeof(katta)) # ~1,000,049
qism = katta[:500_000]
print(sys.getsizeof(qism)) # ~500,049 — NUSXAButun kesim — nusxa emas:
print(katta[:] is katta) # True — satr o'zgarmasRo'yxatda esa nusxa:
royxat = [1, 2, 3]
print(royxat[:] is royxat) # Falsememoryview — nusxasiz (faqat baytlar uchun):
baytlar = b"a" * 1_000_000
nusxa = baytlar[:500_000] # nusxalaydi
korinish = memoryview(baytlar)[:500_000] # NUSXALAMAYDI
print(sys.getsizeof(nusxa)) # ~500,033
print(sys.getsizeof(korinish)) # 184 — faqat metama'lumotKatta faylni qayta ishlashda muhim:
# ❌ Butun faylni xotiraga
with open("katta.txt", encoding="utf-8") as f:
matn = f.read()
for qator in matn.splitlines():
...
# ✅ Qatorma-qator
with open("katta.txt", encoding="utf-8") as f:
for qator in f:
...16-qismda batafsil.
2.9. sys.getsizeof va haqiqiy xotira
import sys
print(sys.getsizeof("")) # 49
print(sys.getsizeof("a")) # 50
print(sys.getsizeof("a" * 100)) # 149
print(sys.getsizeof("а")) # 76 — kirillcha (2 bayt)
print(sys.getsizeof("😊")) # 80 — 4 baytFormula (CPython, ASCII uchun):
49 (sarlavha) + uzunlik × 1 bayt getsizeof ichma-ich obyektlarni hisoblamaydi:
royxat = ["a" * 1000 for _ in range(100)]
print(sys.getsizeof(royxat)) # ~920 — faqat ro'yxat!
haqiqiy = sys.getsizeof(royxat) + sum(sys.getsizeof(s) for s in royxat)
print(haqiqiy) # ~105,820To'liq o'lchash:
from pympler import asizeof # pip install pympler
print(asizeof.asizeof(royxat))Internlash bilan tejash:
import sys
N = 100_000
# Har biri alohida obyekt
alohida = [f"turi_{i % 5}" for i in range(N)]
noyob_alohida = len({id(s) for s in alohida})
# Internlangan
internlangan = [sys.intern(f"turi_{i % 5}") for i in range(N)]
noyob_intern = len({id(s) for s in internlangan})
print(f" Alohida: {noyob_alohida} noyob obyekt")
print(f" Internlangan: {noyob_intern} noyob obyekt")Eslatma: zamonaviy CPython f-satrlarni ham optimallashtiradi, shuning uchun farq ko'rinmasligi mumkin. Bu — amalga oshiruv tafsiloti.
3. Tez ma'lumotnoma
O'zgarmas turlar
int float complex bool str bytes
tuple frozenset range NoneTypeNega o'zgarmas — 4 sabab
1. Xeshlanish → lug'at kaliti bo'la oladi
2. Xavfsizlik → funksiyada o'zgartirilmaydi
3. Optimizatsiya → internlash, keshlash
4. Ko'p oqim → qulflashsiz o'qishXesh qoidasi
a == b → hash(a) == hash(b) MAJBURIY
__eq__ yozsangiz — __hash__ ham yozing
@dataclass(frozen=True) — avtomatikSatr qurish
"".join(map(str, x)) eng tez
"".join(str(i) for i in x) generator
"".join([...]) comprehension
bolaklar.append(); "".join() shartli qo'shish
io.StringIO() fayl interfeysi kerak bo'lsa
natija += x ⚠️ faqat bir necha bo'lakMuqobil vositalar
io.StringIO() matn buferi
bytearray() o'zgaruvchan baytlar
memoryview() nusxasiz kesim (baytlar)Tuzoqlar
matn.strip() natijani SAQLANG
if matn is "salom": ❌ internlash kafolat emas
sys.getsizeof(royxat) ichma-ich obyektlarni hisoblamaydi4. Batafsil misollar
Misol 1 — O'zgarmaslikni ko'rish
"""Satrlar o'zgarmas — barcha oqibati."""
import sys
print("=== 1. O'zgartirishga urinish ===")
matn = "salom"
amallar = [
("matn[0] = 'S'", lambda: matn.__setitem__(0, "S")),
("matn.append('!')", lambda: matn.append("!")),
("del matn[0]", lambda: matn.__delitem__(0)),
("matn += '!'", lambda: None), # ✅ ishlaydi
]
for kod, f in amallar[:3]:
try:
f()
print(f" {kod:<22} ✅")
except (TypeError, AttributeError) as x:
print(f" {kod:<22} ❌ {type(x).__name__}")
print("\n=== 2. Metodlar yangi obyekt qaytaradi ===")
matn = " salom "
print(f" Asl id: {id(matn)}")
for nom, natija in [("strip()", matn.strip()),
("upper()", matn.upper()),
("replace('a','A')", matn.replace("a", "A"))]:
print(f" {nom:<20} id={id(natija)} {natija!r}")
print(f" Asl o'zgarmadi: {matn!r}")
print("\n=== 3. Optimizatsiya — o'zgarish bo'lmasa ===")
matn = "salom"
tekshiruvlar = [
("matn.lower()", matn.lower()),
("matn.replace('z','x')", matn.replace("z", "x")),
("matn[:]", matn[:]),
("matn.upper()", matn.upper()),
("matn.strip()", matn.strip()),
]
for kod, natija in tekshiruvlar:
xil = "bir xil obyekt ✅" if natija is matn else "yangi obyekt"
print(f" {kod:<24} {xil}")
print("\n ⚠️ Bu — CPython optimizatsiyasi, KAFOLAT EMAS")
print("\n=== 4. Xeshlanish ===")
obyektlar = [
("satr", "salom"),
("son", 42),
("juftlik", (1, 2)),
("frozenset", frozenset([1, 2])),
("ro'yxat", [1, 2]),
("lug'at", {"a": 1}),
("to'plam", {1, 2}),
]
print(f" {'Tur':<12} {'Xeshlanadi':<12} {'Kalit boʻla oladi'}")
print(" " + "─" * 42)
for nom, obyekt in obyektlar:
try:
hash(obyekt)
print(f" {nom:<12} {'✅ ha':<12} ✅")
except TypeError:
print(f" {nom:<12} {'❌ yoʻq':<12} ❌")
print("\n=== 5. Nega ro'yxat kalit bo'la olmaydi ===")
print("""
Tasavvur qiling, ruxsat berilgan:
kalit = [1, 2]
lugat = {kalit: "qiymat"} # xesh(1,2) katakka joylashdi
kalit.append(3) # xesh o'zgardi!
lugat[kalit] # xesh(1,2,3) katakda qidiradi
# → element YO'QOLDI
O'zgarmas obyektning xeshi hech qachon o'zgarmaydi.
""")
print("=== 6. Funksiyaga uzatish ===")
def satrni_ishla(matn: str) -> str:
matn = matn.upper() # mahalliy o'zgaruvchi
return matn
def royxatni_ishla(royxat: list) -> list:
royxat.append("qo'shildi") # TASHQARIDAGI obyekt!
return royxat
asl_satr = "salom"
satrni_ishla(asl_satr)
print(f" Satr: {asl_satr!r} ← o'zgarmadi ✅")
asl_royxat = [1, 2]
royxatni_ishla(asl_royxat)
print(f" Ro'yxat: {asl_royxat} ← O'ZGARDI ⚠️")=== 1. O'zgartirishga urinish ===
matn[0] = 'S' ❌ TypeError
matn.append('!') ❌ AttributeError
del matn[0] ❌ TypeError
=== 3. Optimizatsiya — o'zgarish bo'lmasa ===
matn.lower() bir xil obyekt ✅
matn.replace('z','x') bir xil obyekt ✅
matn[:] bir xil obyekt ✅
matn.upper() yangi obyekt
matn.strip() bir xil obyekt ✅
=== 4. Xeshlanish ===
Tur Xeshlanadi Kalit bo'la oladi
──────────────────────────────────────────
satr ✅ ha ✅
son ✅ ha ✅
juftlik ✅ ha ✅
frozenset ✅ ha ✅
ro'yxat ❌ yo'q ❌
lug'at ❌ yo'q ❌
to'plam ❌ yo'q ❌Nima ko'rsatdi: 2.1, 2.2, 2.3-bo'limlar.
Misol 2 — Satr qurish usullari va tezligi
"""Barcha usul — o'lchov bilan."""
import io
import sys
import time
def olcha(f, *args) -> tuple[str, float]:
boshlandi = time.perf_counter()
natija = f(*args)
return natija, time.perf_counter() - boshlandi
def usul_qoshish(n):
natija = ""
for i in range(n):
natija += str(i)
return natija
def usul_qoshish_havola_bilan(n):
"""Ikkinchi havola optimizatsiyani buzadi."""
natija = ""
for i in range(n):
nusxa = natija # ← ikkinchi havola HAR iteratsiyada
natija += str(i) # (tsikldan oldin bo'lsa, u faqat "" ga
return natija # ishora qilardi va hech narsa buzilmasdi)
def usul_join_map(n):
return "".join(map(str, range(n)))
def usul_join_generator(n):
return "".join(str(i) for i in range(n))
def usul_join_comprehension(n):
return "".join([str(i) for i in range(n)])
def usul_royxat_join(n):
bolaklar = []
for i in range(n):
bolaklar.append(str(i))
return "".join(bolaklar)
def usul_stringio(n):
bufer = io.StringIO()
for i in range(n):
bufer.write(str(i))
return bufer.getvalue()
USULLAR = [
("join(map)", usul_join_map),
("join(comprehension)", usul_join_comprehension),
("join(generator)", usul_join_generator),
("ro'yxat + join", usul_royxat_join),
("StringIO", usul_stringio),
("+= bilan", usul_qoshish),
("+= (2-havola bilan)", usul_qoshish_havola_bilan),
]
for N in [10_000, 100_000]:
print(f"\n=== N = {N:,} ===")
natijalar = []
kutilgan = None
for nom, f in USULLAR:
natija, vaqt = olcha(f, N)
if kutilgan is None:
kutilgan = natija
assert natija == kutilgan, f"{nom} boshqa natija berdi!"
natijalar.append((nom, vaqt))
eng_tez = min(v for _, v in natijalar)
print(f" {'Usul':<24} {'Vaqt':>10} {'Nisbat':>8}")
print(" " + "─" * 46)
for nom, vaqt in sorted(natijalar, key=lambda x: x[1]):
nisbat = vaqt / eng_tez
chiziq = "█" * min(int(nisbat * 8), 40)
print(f" {nom:<24} {vaqt * 1000:>7.1f} ms {nisbat:>7.1f}x {chiziq}")
print("\n\n=== id() bilan kuzatish ===")
natija = ""
print(" += bilan (bitta havola):")
for i in range(5):
natija += str(i)
print(f" {i}: id={id(natija)}")
print("\n ⚠️ CPython optimizatsiyasi:")
print(" Agar satrga BITTA havola bo'lsa, += uni joyida")
print(" kengaytirishga urinadi (realloc). Shuning uchun")
print(" id ba'zan o'zgarmaydi.")
print("\n Lekin bu KAFOLATLANMAGAN:")
print(" • PyPy, Jython'da yo'q")
print(" • Ikkinchi havola bo'lsa ishlamaydi")
print(" • Versiyalar orasida o'zgarishi mumkin")
print("\n\n=== Amaliy tavsiya ===")
print("""
✅ Barcha bo'lak ma'lum:
"".join(map(str, elementlar))
✅ Shartli qo'shish:
bolaklar = []
for x in elementlar:
if shart(x):
bolaklar.append(format(x))
natija = "".join(bolaklar)
✅ Fayl interfeysi kerak (print, csv.writer):
bufer = io.StringIO()
print("matn", file=bufer)
natija = bufer.getvalue()
⚠️ Faqat bir necha bo'lak:
natija = a + b + c
natija = f"{a}{b}{c}" ← yaxshiroq
""")Natijaning muhim qismi:
=== N = 100,000 ===
Usul Vaqt Nisbat
──────────────────────────────────────────────
...
+= (2-havola bilan) ... ms ...x ...Diqqat: o'sib borayotgan satrga ikkinchi havola bo'lganda += optimizatsiyasi buziladi va har qadamda butun satr nusxalanadi. Farq N bilan kvadratik o'sadi — yuqoridagi o'lchovda 100 000 elementda ~88× (10 000 da esa ~12×) sekinroq.
Nima ko'rsatdi: 2.5, 2.6-bo'limlar.
Misol 3 — Internlash va xotira
"""Internlash — xotira tejash."""
import sys
def obyektlar_soni(satrlar: list[str]) -> int:
"""Noyob obyektlar sonini qaytaradi."""
return len({id(s) for s in satrlar})
print("=== 1. Avtomatik internlash ===")
sinovlar = [
("salom", "identifikatorga o'xshash"),
("foydalanuvchi_ismi", "pastki chiziq bilan"),
("salom dunyo!", "bo'sh joy va tinish"),
("", "bo'sh satr"),
("x", "bitta belgi"),
("O'zbekiston", "apostrof bilan"),
("salom123", "raqam bilan"),
]
print(f" {'Satr':<22} {'Internlangan':<14} Izoh")
print(" " + "─" * 58)
for matn, izoh in sinovlar:
a = matn
b = matn[:] # nusxa olishga urinish
# Kompilyatsiya doimiylarini solishtirish uchun eval
x = eval(repr(matn))
y = eval(repr(matn))
holat = "✅ ha" if x is y else "❌ yo'q"
print(f" {matn!r:<22} {holat:<14} {izoh}")
print("\n=== 2. Kompilyatsiya vs ishlash paytida ===")
a = "salom"
b = "sal" + "om" # kompilyator birlashtiradi
qism = "sal"
c = qism + "om" # ishlash paytida
d = "".join(["sal", "om"])
e = f"sal{'om'}"
print(f" a = 'salom' ")
print(f" b = 'sal' + 'om' a is b: {a is b}")
print(f" c = qism + 'om' a is c: {a is c}")
print(f" d = ''.join([...]) a is d: {a is d}")
print(f" e = f'sal{{...}}' a is e: {a is e}")
print(f"\n Lekin hammasi teng: {a == b == c == d == e}")
print("\n=== 3. sys.intern ===")
matn = "salom dunyo!"
a = matn
b = "salom" + " dunyo!"
c = sys.intern(a)
d = sys.intern(b)
print(f" a is b: {a is b}")
print(f" c is d: {c is d} ← intern bilan")
print(f" a is c: {a is c}")
print("\n=== 4. Xotira tejash ===")
N = 200_000
TURLAR = ["oddiy", "vip", "premium", "sinov", "bloklangan"]
# Har biri alohida obyekt
alohida = []
for i in range(N):
alohida.append("".join(TURLAR[i % 5])) # ishlash paytida
# Internlangan
INTERNLANGAN = [sys.intern(t) for t in TURLAR]
internlangan = [INTERNLANGAN[i % 5] for i in range(N)]
print(f" {N:,} ta element, {len(TURLAR)} xil qiymat\n")
print(f" Alohida: {obyektlar_soni(alohida):>8,} noyob obyekt")
print(f" Internlangan: {obyektlar_soni(internlangan):>8,} noyob obyekt")
# Taxminiy xotira
xotira_alohida = sum(sys.getsizeof(s) for s in set(map(id, alohida)) for s in [""])
print(f"\n Har bir satr ~{sys.getsizeof('oddiy')} bayt")
print(f" Alohida: ~{obyektlar_soni(alohida) * 54:>10,} bayt")
print(f" Internlangan: ~{obyektlar_soni(internlangan) * 54:>10,} bayt")
print("\n=== 5. Xesh barqarorligi ===")
print(f" hash('salom') = {hash('salom')}")
print("\n ⚠️ Bu qiymat HAR DASTUR ISHGA TUSHGANDA o'zgaradi")
print(" Sabab: xesh to'qnashuvi hujumlaridan himoya (PEP 456)")
print(" O'chirish: PYTHONHASHSEED=0")
print(f"\n Lekin sonlar barqaror:")
for x in [42, 42.0, True, -1, 1000]:
print(f" hash({x!r:<6}) = {hash(x)}")
print("\n=== 6. Xesh qoidasi ===")
print(f" 42 == 42.0: {42 == 42.0}")
print(f" hash(42) == hash(42.0): {hash(42) == hash(42.0)} ← MAJBURIY")
print(f" 1 == True: {1 == True}")
print(f" hash(1) == hash(True): {hash(1) == hash(True)}")
lugat = {1: "bir", True: "rost", 1.0: "kasr"}
print(f"\n {{1: 'bir', True: 'rost', 1.0: 'kasr'}}")
print(f" → {lugat} ← hammasi BITTA kalit!")Natijaning muhim qismi:
=== 2. Kompilyatsiya vs ishlash paytida ===
a = 'salom'
b = 'sal' + 'om' a is b: True
c = qism + 'om' a is c: False
d = ''.join([...]) a is d: False
e = f'sal{...}' a is e: False
Lekin hammasi teng: True
=== 4. Xotira tejash ===
200,000 ta element, 5 xil qiymat
Alohida: 200,000 noyob obyekt
Internlangan: 5 noyob obyekt
=== 6. Xesh qoidasi ===
42 == 42.0: True
hash(42) == hash(42.0): True ← MAJBURIY
1 == True: True
hash(1) == hash(True): True
{1: 'bir', True: 'rost', 1.0: 'kasr'}
→ {1: 'kasr'} ← hammasi BITTA kalit!Nima ko'rsatdi: 2.3, 2.4, 2.9-bo'limlar.
Misol 4 — Amaliy: HTML generatori
"""Satr qurishning to'g'ri usullari — amaliy misol."""
import html
import io
import time
from dataclasses import dataclass
@dataclass
class Mahsulot:
nom: str
narx: int
mavjud: bool
izoh: str = ""
MAHSULOTLAR = [
Mahsulot("Klaviatura <Pro>", 250_000, True, "Mexanik, RGB yoritish"),
Mahsulot("Sichqoncha \"Gaming\"", 120_000, True),
Mahsulot("Monitor 24\"", 2_500_000, False, "IPS, 1920x1080"),
Mahsulot("Kabel HDMI & USB", 45_000, True),
]
# ═══ ❌ Yomon usul ═══
def html_qoshish(mahsulotlar: list[Mahsulot]) -> str:
"""+= bilan — sekin va o'qish qiyin."""
natija = "<table>\n"
natija += " <thead><tr><th>Nom</th><th>Narx</th><th>Holat</th></tr></thead>\n"
natija += " <tbody>\n"
for m in mahsulotlar:
natija += " <tr>\n"
natija += f" <td>{html.escape(m.nom)}</td>\n"
natija += f" <td>{m.narx:,}</td>\n"
natija += f" <td>{'Bor' if m.mavjud else 'Yoʻq'}</td>\n"
natija += " </tr>\n"
natija += " </tbody>\n"
natija += "</table>"
return natija
# ═══ ✅ join bilan ═══
def html_join(mahsulotlar: list[Mahsulot]) -> str:
"""Ro'yxat + join — tez va toza."""
qatorlar = [
"<table>",
" <thead><tr><th>Nom</th><th>Narx</th><th>Holat</th></tr></thead>",
" <tbody>",
]
for m in mahsulotlar:
holat = "Bor" if m.mavjud else "Yo'q"
qatorlar.extend([
" <tr>",
f" <td>{html.escape(m.nom)}</td>",
f" <td>{m.narx:,}</td>",
f" <td>{holat}</td>",
" </tr>",
])
qatorlar.extend([" </tbody>", "</table>"])
return "\n".join(qatorlar)
# ═══ ✅ StringIO bilan ═══
def html_stringio(mahsulotlar: list[Mahsulot]) -> str:
"""StringIO — print bilan qulay."""
bufer = io.StringIO()
print("<table>", file=bufer)
print(" <thead><tr><th>Nom</th><th>Narx</th><th>Holat</th></tr></thead>",
file=bufer)
print(" <tbody>", file=bufer)
for m in mahsulotlar:
holat = "Bor" if m.mavjud else "Yo'q"
print(" <tr>", file=bufer)
print(f" <td>{html.escape(m.nom)}</td>", file=bufer)
print(f" <td>{m.narx:,}</td>", file=bufer)
print(f" <td>{holat}</td>", file=bufer)
print(" </tr>", file=bufer)
print(" </tbody>", file=bufer)
print("</table>", file=bufer, end="")
return bufer.getvalue()
# ═══ ✅ Generator bilan ═══
def html_generator(mahsulotlar: list[Mahsulot]) -> str:
"""Generator — eng ixcham."""
def qatorlar():
yield "<table>"
yield " <thead><tr><th>Nom</th><th>Narx</th><th>Holat</th></tr></thead>"
yield " <tbody>"
for m in mahsulotlar:
yield " <tr>"
yield f" <td>{html.escape(m.nom)}</td>"
yield f" <td>{m.narx:,}</td>"
yield f" <td>{'Bor' if m.mavjud else 'Yo\'q'}</td>"
yield " </tr>"
yield " </tbody>"
yield "</table>"
return "\n".join(qatorlar())
print("=== Natija (join usuli) ===")
print(html_join(MAHSULOTLAR))
print("\n\n=== Barcha usul bir xil natija beradi ===")
natijalar = {
"qoshish": html_qoshish(MAHSULOTLAR),
"join": html_join(MAHSULOTLAR),
"stringio": html_stringio(MAHSULOTLAR),
"generator": html_generator(MAHSULOTLAR),
}
birinchi = natijalar["join"]
for nom, n in natijalar.items():
print(f" {nom:<12} {'✅ bir xil' if n == birinchi else '❌ farqli'}")
print("\n\n=== Tezlik (10 000 mahsulot) ===")
KATTA = MAHSULOTLAR * 2500
usullar = [
("+= bilan", html_qoshish),
("ro'yxat + join", html_join),
("StringIO", html_stringio),
("generator + join", html_generator),
]
natijalar = []
for nom, f in usullar:
boshlandi = time.perf_counter()
f(KATTA)
natijalar.append((nom, time.perf_counter() - boshlandi))
eng_tez = min(v for _, v in natijalar)
for nom, vaqt in sorted(natijalar, key=lambda x: x[1]):
print(f" {nom:<20} {vaqt * 1000:>7.1f} ms {vaqt / eng_tez:>5.1f}x")
print("\n\n=== XSS himoyasi ===")
print(" html.escape() ishlatilgani muhim:")
xavfli = Mahsulot("<script>alert('XSS')</script>", 0, True)
print(f" Xavfli nom: {xavfli.nom}")
print(f" Ekranlangan: {html.escape(xavfli.nom)}")=== Natija (join usuli) ===
<table>
<thead><tr><th>Nom</th><th>Narx</th><th>Holat</th></tr></thead>
<tbody>
<tr>
<td>Klaviatura <Pro></td>
<td>250,000</td>
<td>Bor</td>
</tr>
...
</tbody>
</table>
=== Tezlik (10 000 mahsulot) ===
ro'yxat + join 28.4 ms 1.0x
generator + join 31.2 ms 1.1x
StringIO 42.7 ms 1.5x
+= bilan 58.3 ms 2.1xNima ko'rsatdi: 2.5, 2.6-bo'limlar amaliyotda.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "O'zgarmaslik — cheklov" | Xeshlanish, xavfsizlik va optimizatsiyani beradi |
"matn.strip() satrni o'zgartiradi" |
Yangi satr qaytaradi. Natijani saqlang |
"+= har doim yangi obyekt yaratadi" |
CPython optimizatsiyasi bor, lekin kafolatlanmagan |
"is satrlar uchun ishonchli" |
Internlash — amalga oshiruv tafsiloti |
"hash('a') doim bir xil" |
Har dastur ishga tushganda o'zgaradi |
"getsizeof to'liq xotirani beradi" |
Ichma-ich obyektlarni hisoblamaydi |
| "Pythonda mutable string bor" | Yo'q. io.StringIO yoki list + join |
"matn[:] nusxa yaratadi" |
Satrda — yo'q (is matn). Ro'yxatda — ha |
6. Keng tarqalgan xatolar va yechimlari
1. Metod natijasini saqlamaslik
matn.strip() # ❌ natija yo'qoladi
matn = matn.strip() # ✅2. Siklda +=
natija = ""
for x in katta:
natija += str(x) # ⚠️ sekin
natija = "".join(map(str, katta)) # ✅3. is bilan solishtirish
if matn is "salom": # ❌
if matn == "salom": # ✅4. __eq__ bor, __hash__ yo'q
class A:
def __eq__(self, b): ...
# set(a_lar) # ❌ TypeError
class A:
def __eq__(self, b): ...
def __hash__(self): return hash(...) # ✅5. dataclass ni to'plamga qo'shish
@dataclass
class A: x: int
# {A(1)} # ❌ unhashable
@dataclass(frozen=True)
class A: x: int
print({A(1)}) # ✅6. Katta satrni kesish
qism = katta[:1000] # nusxa yaratadi
# Baytlar uchun:
korinish = memoryview(baytlar)[:1000] # nusxasiz7. bytes bilan +=
b = b""
for bolak in bolaklar:
b += bolak # ⚠️ bytes ham o'zgarmas
ba = bytearray() # ✅
for bolak in bolaklar:
ba.extend(bolak)
b = bytes(ba)8. Xesh qiymatini saqlash
# ❌ Xesh dastur ishga tushganda o'zgaradi
saqla_bazaga(hash(matn))
import hashlib # ✅ barqaror
saqla_bazaga(hashlib.sha256(matn.encode()).hexdigest())7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 3.1-dars (o'tilgan): yorliq modeli,
isva== - 3.7-dars (o'tilgan): satr o'zgarmasligi
- 4.5-dars (o'tilgan):
joinva tezlik - 6-qism: o'zgaruvchan va o'zgarmas to'plamlar,
frozenset - 6-qism: xeshlash, lug'at ichkarisi
- 8-qism:
__hash__,__eq__,frozen=True - 11-qism: xotira boshqaruvi, havolalarni sanash
- 14-qism: ko'p oqimli ishlash — o'zgarmaslik xavfsizligi
8. Eng yaxshi amaliyotlar
Metod natijasini doim saqlang.
matn = matn.strip()— satrlar o'zgarmas.Katta satr qurishda
join.+=optimizatsiyasi kafolatlanmagan.isni satrlar bilan ishlatmang. FaqatNone,True,False.__eq__bilan__hash__yozing. Yoki@dataclass(frozen=True).Takrorlanuvchi satrlarni internlang. Katta ma'lumotda xotira tejaydi.
Barqaror xesh uchun
hashlib.hash()seansdan seansga o'zgaradi.bytesuchunbytearrayishlating. U ham o'zgarmas.O'zgarmaslikni afzal ko'ring. O'z sinflaringizda ham — kod xavfsizroq va tushunarliroq bo'ladi.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
matn = "salom"
1. matn is matn.lower()
2. matn is matn.upper()
3. matn is matn[:]
4. matn is matn.replace("z", "x")
5. hash("a") == hash("a")
6. {1: "a", True: "b", 1.0: "c"}
7. hash((1, 2))
8. hash([1, 2])
9. "sal" + "om" is "salom"
10. "".join(["sal", "om"]) is "salom"Javoblar
False—lower()o'zgarish bo'lmasa ham yangi obyekt qaytaradi (CPython tafsiloti;replaceva[:]esa o'zini qaytarishi mumkin)FalseTrueTrue— topilmadiTrue— shu seansda{1: 'c'}— hammasi bitta kalit- Son (masalan
-3550055125485641917) -
TypeError True(SyntaxWarningbilan) — kompilyator birlashtiradiFalse
Vazifa 2: Tezlikni o'lchash
2-misoldagi barcha usulni o'zingiz yozing va N = 50_000, N = 500_000 uchun o'lchang.
Savol: N oshganda nisbat qanday o'zgaradi? Nega?
Vazifa 3: Xeshlanadigan sinf
Manzil sinfini yarating (shahar, kocha, uy). Talablar:
- To'plamga qo'shish mumkin bo'lsin
- Lug'at kaliti bo'la olsin
- Teng manzillar teng deb hisoblansin (registrsiz)
- O'zgartirib bo'lmasin
Yechim
from dataclasses import dataclass
@dataclass(frozen=True)
class Manzil:
shahar: str
kocha: str
uy: str
def __eq__(self, boshqa):
if not isinstance(boshqa, Manzil):
return NotImplemented
return self._kalit() == boshqa._kalit()
def __hash__(self):
return hash(self._kalit())
def _kalit(self):
return (self.shahar.casefold(),
self.kocha.casefold(),
self.uy.casefold())
a = Manzil("Toshkent", "Amir Temur", "12A")
b = Manzil("TOSHKENT", "amir temur", "12a")
print(a == b) # True
print({a, b}) # bitta element
print(len({a, b})) # 1Vazifa 4: Satr quruvchi sinf
SatrQuruvchi sinfini yarating — StringIO ga o'xshash, lekin:
qosh(matn)— bo'lak qo'shadiqosh_qator(matn)— yangi qator bilanchekinish(n)— keyingi qatorlar uchun chekinishnatija()— yakuniy satr
Ichkarida ro'yxat + join ishlating.
Vazifa 5: Internlash tajribasi
3-misoldagi tajribani takrorlang, lekin:
- 1 000 000 element bilan
- Xotirani
pymplerbilan o'lchang (yoki taxminiy hisoblang) isbilan solishtirish tezligini ham o'lchang
Vazifa 6: HTML generatorini kengaytirish
4-misoldagi dasturga qo'shing:
- CSS sinflari (
class="mavjud"/class="yoq") - Ustunlar bo'yicha saralash havolalari
- Jami qator
- Sahifalash (
limit,offset)
Vazifa 7: O'ylash
Nega Pythonda o'zgaruvchan satr turi (mutable string) yo'q, lekin bytearray bor?
Javob
Uch sabab:
1. join yetarlicha tez. Satr qurish uchun "".join(bolaklar) optimallashtirilgan: u avval umumiy uzunlikni hisoblaydi, bir marta xotira ajratadi va nusxalaydi. Bu — o'zgaruvchan satr bilan bir xil murakkablikda (O(n)).
2. Xeshlanish muhimroq. Satrlar eng ko'p ishlatiladigan lug'at kaliti. Agar str o'zgaruvchan bo'lganida, har bir lug'at operatsiyasida nusxa olish kerak bo'lardi — bu butun tilni sekinlashtirardi.
3. bytes uchun holat boshqacha. Ikkilik ma'lumot bilan ishlashda:
- Bir baytni o'zgartirish tez-tez kerak bo'ladi (protokollar, rasm qayta ishlash)
byteskalit sifatida kam ishlatiladi- Bo'lak-bo'lak qurish odatiy holat (tarmoq buferlari)
Shuning uchun bytearray mavjud, lekin u xeshlanmaydi.
Solishtiring boshqa tillar bilan:
| Til | Satr | O'zgaruvchan variant |
|---|---|---|
| Python | str (o'zgarmas) |
yo'q (list/StringIO) |
| Java | String (o'zgarmas) |
StringBuilder |
| C# | string (o'zgarmas) |
StringBuilder |
| Go | string (o'zgarmas) |
strings.Builder |
| Rust | String (o'zgaruvchan) |
— |
| C++ | std::string (o'zgaruvchan) |
— |
Ko'p zamonaviy til satrni o'zgarmas qilgan — bu tasodif emas.
Nimani mustahkamlaydi: 2.1, 2.2, 2.3, 2.4, 2.5-bo'limlar.
Xulosa
Bu darsda o'zgarmaslikni chuqur o'rgandik.
Eng muhim uch fikr:
O'zgarmaslik — cheklov emas, dizayn qarori. U to'rt narsani beradi: xeshlanish (lug'at kaliti bo'la olish), xavfsizlik (funksiyada o'zgartirilmaslik), optimizatsiya (internlash) va ko'p oqimli xavfsizlik.
Katta satr qurishda
join.+=uchun CPython optimizatsiyasi bor, lekin u kafolatlanmagan — ikkinchi havola paydo bo'lsa yoki boshqa amalga oshiruvda ishlamaydi.isni satrlar bilan hech qachon ishlatmang. Internlash — amalga oshiruv tafsiloti:"salom" is "salom"True, lekin"salom dunyo!" is "salom dunyo!"False.
4-QISM YAKUNLANDI
O'n ikki darsni tugatdingiz. Satrlar haqida bilishingiz kerak bo'lgan deyarli hamma narsa endi sizda:
| Dars | Nima o'rgandingiz |
|---|---|
| 4.1 | Ichki tuzilma: PEP 393, kod nuqtasi, grafema, internlash |
| 4.2 | Kesimlar: slice obyekti, manfiy qadam, chegara qoidalari |
| 4.3 | Qidirish: find/index, isdigit tuzog'i, endswith juftlik |
| 4.4 | Tozalash: strip belgilar to'plami, translate, normalizatsiya |
| 4.5 | Bo'lish: split ikki rejimi, partition, join |
| 4.6 | % va .format(): kechiktirilgan shablon, Template xavfsizligi |
| 4.7 | f-satr chuqur: bytecode, {x=}, __format__, PEP 701 |
| 4.8 | Unicode: normalizatsiya, o'zbek apostroflari, homoglifiya |
| 4.9 | Kodlash: UTF-8, mojibake, BOM, "Unicode sendvich" |
| 4.10 | Ekranlash: xom satrlar, regex uchun r"", Windows yo'llari |
| 4.11 | Ko'p qatorli: textwrap, chekinish, docstring |
| 4.12 | O'zgarmaslik: xeshlash, internlash, samarali qurish |
Endi siz matn bilan professional darajada ishlay olasiz — foydalanuvchi kiritmasini tozalashdan tortib, Unicode xavfsizligigacha.
Keyingi qism: 5-QISM — BOSHQARUV OQIMI. 12 dars: if/elif/else, sikllar, range, enumerate, zip, break/continue va Python 3.10 dagi match/case. Bu — dasturga mantiq qo'shish qismi.
Bosqich 1 (Poydevor) yakuniga yaqin: 1-4 qismlar tugadi, 5-qism qoldi.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!