Mundarija (20)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. O'zgartirish va qayta bog'lash
- 2.2. Taxallus tuzoqlari
- 2.3. "O'zgarmas" obyektlarning ichi
- 2.4. Inkapsulyatsiya sizishi va himoya nusxalari
- 2.5. Iteratsiya paytida o'zgartirish
- 2.6. Havolalar grafi
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Taxallus tuzoqlari va umumiy qismlarni aniqlash
- Misol 2 — O'zgartirish va qayta bog'lash jadvali
- Misol 3 — "O'zgarmas" obyektlar va inkapsulyatsiya sizishi
- Misol 4 — Amaliy: iteratsiya paytida o'zgartirish va graf nusxalash
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
11.3-dars: Havola semantikasi
11-QISM — OBYEKT MODELI VA XOTIRA · 3-dars
1. Kirish va motivatsiya
7.2-darsda Python argumentlarni "obyektga havolani qiymat bo'yicha" (call by sharing) uzatishini ko'rdik: x.append(1) tashqarida ko'rinadi, x = [1] esa yo'q. 3.1-darsda o'zgaruvchini "quti emas, yorliq" deb ta'rifladik, 6.18-darsda copy va deepcopy ni o'rgandik.
Lekin havola semantikasi faqat funksiya argumentlarida emas — u butun tilda: ro'yxat elementlarida, lug'at qiymatlarida, klass atributlarida, dataclass maydonlarida, += da. Mana real xato — jadvalli o'yinning boshlanishi:
maydon = [[" "] * 3] * 3
maydon[0][0] = "X"
print(maydon)
# [['X', ' ', ' '], ['X', ' ', ' '], ['X', ' ', ' ']]Birinchi yurishdan keyin butun ustun to'ldi. Yana biri — "o'zgarmas" konfiguratsiya:
@dataclass(frozen=True)
class Sozlama:
ruxsatlar: list
s = Sozlama(["o'qish"])
s.ruxsatlar += ["o'chirish"] # FrozenInstanceError: cannot assign to field 'ruxsatlar'
s.ruxsatlar # ["o'qish", "o'chirish"] — xato berdi, lekin o'zgardi!Python'da hamma narsa havola orqali ushlanadi: nomlar, konteyner elementlari, atributlar. Qaysi amal obyektni o'zgartiradi va qaysi biri nomni qayta bog'laydi — shu farqni har amal uchun aniq bilish taxalluslar (alias) xatolarini, "o'zgarmas" obyektlardagi teshiklarni va iteratsiya paytidagi buzilishlarni oldini oladi.
Bu darsda:
- O'zgartirish va qayta bog'lash — har amal uchun aniq jadval
- Taxallus tuzoqlari:
[[x]*n]*m,dict.fromkeys, umumiy klass atributlari - "O'zgarmas" obyektlarning ichi: kortej,
frozendataclass,property - Inkapsulyatsiya sizishi va himoya nusxalari
- Iteratsiya paytida konteynerni o'zgartirish
- Havolalar grafi: umumiy qismlar va sikllar;
deepcopyva JSON farqi
2. Nazariya — chuqur tushuntirish
2.1. O'zgartirish va qayta bog'lash
Har amal ikki xil ishdan birini qiladi:
- O'zgartirish (mutation) — obyektning o'zi o'zgaradi; unga ishora qiluvchi barcha nomlar yangi holatni ko'radi
- Qayta bog'lash (rebinding) — nom (yoki konteyner uyasi, yoki atribut) boshqa obyektga ishora qila boshlaydi; eski obyekt va unga boshqa havolalar o'zgarmaydi
| Amal | Nima bo'ladi | id(x) o'zgaradimi |
|---|---|---|
x = y |
x nomi qayta bog'lanadi |
Ha (yangi obyekt) |
x.append(v), x.extend(...), x.sort() |
x obyekti o'zgaradi |
Yo'q |
x[i] = v, x[k] = v |
x o'zgaradi (uyasi qayta bog'lanadi) |
Yo'q |
x[:] = [...] |
x joyida almashtiriladi |
Yo'q |
x.attr = v |
x o'zgaradi (atribut qayta bog'lanadi) |
Yo'q |
x += y (list, set, dict |=) |
__iadd__ — joyida, keyin x o'shaga qayta bog'lanadi |
Yo'q |
x += y (int, str, tuple) |
Yangi obyekt, x qayta bog'lanadi |
Ha |
x = x + y |
Har doim yangi obyekt | Ha |
sorted(x), x + [v], x.copy() |
Yangi obyekt, x o'zgarmaydi |
— |
del x |
Nom o'chadi | — |
del x[i] |
x o'zgaradi |
Yo'q |
Qoida: nuqta, qavs yoki mutatsiya metodi — obyektni o'zgartiradi; yalang'och nomga = — faqat nomni qayta bog'laydi.
2.2. Taxallus tuzoqlari
Konteyner elementlari ham havolalar. Bitta obyektni ko'p joyga qo'yish oson:
| Kod | Muammo | To'g'risi |
|---|---|---|
[[0] * 3] * 3 |
Uch qator — bitta ro'yxat | [[0] * 3 for _ in range(3)] |
[[]] * n |
n ta havola bitta bo'sh ro'yxatga |
[[] for _ in range(n)] |
dict.fromkeys(kalitlar, []) |
Hamma kalitda bitta ro'yxat | {k: [] for k in kalitlar} yoki defaultdict(list) |
class K: elementlar = [] |
Barcha nusxalar bitta ro'yxatni bo'lishadi | __init__ da self.elementlar = [] |
def f(x=[]) |
Barcha chaqiruvlar bitta ro'yxat | 11.6-dars |
[0] * 3 esa xavfsiz — 0 o'zgarmas, x[i] = 1 uyani qayta bog'laydi, sonni o'zgartirmaydi. Muammo faqat o'zgaruvchan element ko'paytirilganda.
2.3. "O'zgarmas" obyektlarning ichi
O'zgarmaslik sayoz: obyekt o'z havolalarini almashtirmaydi, lekin ular ishora qilgan obyektlar o'zgarishi mumkin.
| Obyekt | Himoyalangan | Himoyalanmagan |
|---|---|---|
tuple |
Elementlar ro'yxati | Ichidagi list, dict |
frozenset |
A'zolar | — (a'zolar xeshlanadigan bo'lishi kerak) |
@dataclass(frozen=True) |
Maydonlarni qayta bog'lash | Maydondagi list ning ichi |
property (setter'siz) |
Atributni qayta bog'lash | Qaytarilgan obyektning ichi |
MappingProxyType |
Lug'atga yozish | Qiymatlarning ichi |
+= tuzog'i: obyekt.maydon += [...] — ikki qadam:
obyekt.maydon.__iadd__([...])— ro'yxat joyida o'zgaradi muvaffaqiyatliobyekt.maydon = natija— qayta bog'lashFrozenInstanceError/AttributeError
Natija: xato ko'tariladi, lekin o'zgarish allaqachon bo'lgan. 10.14-darsdagi t[0] += [x] bilan bir xil mexanizm.
Chuqur o'zgarmaslik uchun ichki qiymatlarni ham o'zgarmas turlarga o'giring: tuple, frozenset, MappingProxyType (sayoz), yoki __post_init__ da konvertatsiya.
2.4. Inkapsulyatsiya sizishi va himoya nusxalari
class Guruh:
def __init__(self, talabalar):
self._talabalar = talabalar # ⚠️ chaqiruvchining ro'yxati
@property
def talabalar(self):
return self._talabalar # ⚠️ ichki ro'yxat tashqarigaIkki teshik:
- Kirishda: chaqiruvchi keyin o'z ro'yxatini o'zgartirsa,
Guruhning ichi ham o'zgaradi - Chiqishda:
guruh.talabalar.append(...)— klass tekshiruvlarini chetlab o'tadi
Yechimlar:
| Usul | Narxi | Qachon |
|---|---|---|
Kirishda nusxa: list(talabalar) |
O(n) har yaratishda | Klass holatni o'zi boshqaradi |
Chiqishda nusxa: return list(self._talabalar) |
O(n) har murojaatda | Kam murojaat |
O'zgarmas ko'rinish: tuple(...), MappingProxyType |
Kirishda bir marta O(n) | Ko'p o'qish, kam yozish |
O'zgarmas ma'lumot turlari: frozen dataclass + tuple maydonlar |
Loyiha darajasida | Qiymat obyektlari |
2.5. Iteratsiya paytida o'zgartirish
| Konteyner | Iteratsiya paytida o'lcham o'zgarsa |
|---|---|
list |
Xato yo'q — lekin elementlar o'tkazib yuboriladi yoki takrorlanadi |
dict |
RuntimeError: dictionary changed size during iteration |
set |
RuntimeError: Set changed size during iteration |
dict ning mavjud kalitlari qiymatini o'zgartirish esa ruxsat etilgan.
Naqshlar:
- Nusxa ustida iteratsiya:
for x in list(royxat):,for k in list(lugat): - Yangi konteyner yig'ish:
royxat[:] = [x for x in royxat if shart(x)] - O'zgarishlarni to'plab, keyin qo'llash
2.6. Havolalar grafi
Ichma-ich tuzilmalar — graf: bir obyekt bir nechta joydan ishora qilinishi (umumiy qism) yoki o'ziga qaytishi (sikl) mumkin.
| Vosita | Umumiy qism | Sikl |
|---|---|---|
copy.copy |
Tashqi nusxa, ichkilar umumiy | Ichki havolalar asl |
copy.deepcopy |
Saqlanadi (memo lug'ati) |
Saqlanadi |
json.loads(json.dumps(...)) |
Har joyda alohida nusxa | ValueError: Circular reference detected |
pickle |
Saqlanadi | Saqlanadi |
"Chuqur nusxa" usulini tanlashda so'rang: nusxada umumiy qismlar umumiy qolishi kerakmi?
3. Tez ma'lumotnoma
O'zgartirish yoki qayta bog'lash
x = ... qayta bog'lash (faqat nom)
x.metod(), x[i] = , x.a = , x[:] = , del x[i] o'zgartirish (obyekt)
x += y list/set/dict → o'zgartirish int/str/tuple → yangi obyekt
x = x + y har doim yangi obyektTaxalluslar
[[0]*3]*3 ❌ [[0]*3 for _ in range(3)] ✅
dict.fromkeys(k, []) ❌ {x: [] for x in k} / defaultdict(list) ✅
class K: lst = [] ❌ self.lst = [] (__init__ da) ✅"O'zgarmas" ichi
tuple, frozen dataclass, property, MappingProxyType — sayoz himoya
obj.maydon += [...] → ichki ro'yxat o'zgaradi VA keyin xatoIteratsiya
list — jim o'tkazib yuboradi dict/set — RuntimeError
for x in list(konteyner): royxat[:] = [x for x in royxat if ...]Graf nusxalash
deepcopy / pickle — umumiy qism va sikl saqlanadi
json — umumiy qism yo'qoladi, sikl → ValueError4. Batafsil misollar
Misol 1 — Taxallus tuzoqlari va umumiy qismlarni aniqlash
"""[[x]*n]*m, [[]]*n, dict.fromkeys, klass atributi; umumiy ichki obyektlarni topuvchi funksiya."""
def umumiylar(tuzilma, yol="root", korildi=None, natija=None):
"""Bir nechta joydan ishora qilingan o'zgaruvchan obyektlarni topadi."""
if korildi is None:
korildi, natija = {}, {}
if isinstance(tuzilma, (list, dict, set)):
if id(tuzilma) in korildi:
natija.setdefault(id(tuzilma), [korildi[id(tuzilma)]]).append(yol)
return natija
korildi[id(tuzilma)] = yol
elementlar = tuzilma.items() if isinstance(tuzilma, dict) else enumerate(tuzilma)
for kalit, qiymat in elementlar:
umumiylar(qiymat, f"{yol}[{kalit!r}]", korildi, natija)
return natija
print("=== 1. [[' '] * 3] * 3 ===")
yomon = [[" "] * 3] * 3
yomon[0][0] = "X"
print(f" yurishdan keyin: {yomon}")
print(f" umumiy qismlar: {list(umumiylar(yomon).values())}")
yaxshi = [[" "] * 3 for _ in range(3)]
yaxshi[0][0] = "X"
print(f" ✅ comprehension: {yaxshi}, umumiy: {list(umumiylar(yaxshi).values())}")
print("\n=== 2. [0] * 3 xavfsiz — element o'zgarmas ===")
sonlar = [0] * 3
sonlar[0] += 5
print(f" {sonlar} — uya qayta bog'landi, 0 obyekti o'zgarmadi")
print("\n=== 3. dict.fromkeys va [[]] * n ===")
guruhlar = dict.fromkeys(["A", "B", "C"], [])
guruhlar["A"].append("Aziz")
print(f" dict.fromkeys: {guruhlar}")
navbatlar = [[]] * 3
navbatlar[1].append("vazifa")
print(f" [[]] * 3: {navbatlar}")
from collections import defaultdict
guruhlar2 = defaultdict(list)
guruhlar2["A"].append("Aziz")
print(f" ✅ defaultdict: {dict(guruhlar2)}, ✅ comprehension: {[[] for _ in range(3)]}")
print("\n=== 4. Klass atributi — hamma nusxalar uchun bitta ===")
class Savat:
mahsulotlar = []
def qosh(self, m):
self.mahsulotlar.append(m)
a, b = Savat(), Savat()
a.qosh("non")
print(f" a.qosh('non') → b.mahsulotlar: {b.mahsulotlar}, bitta obyekt: {a.mahsulotlar is b.mahsulotlar is Savat.mahsulotlar}")
class SavatTogri:
def __init__(self):
self.mahsulotlar = []
print(f" ✅ __init__ da: {SavatTogri().mahsulotlar is SavatTogri().mahsulotlar}")
print("\n=== 5. Murakkab konfiguratsiyada umumiy qismlar ===")
standart_portlar = [80, 443]
sozlama = {
"web": {"portlar": standart_portlar, "host": "0.0.0.0"},
"api": {"portlar": standart_portlar, "host": "127.0.0.1"},
"zaxira": [standart_portlar, {"portlar": standart_portlar}],
}
for yollar in umumiylar(sozlama).values():
print(f" bitta obyekt {len(yollar)} joyda: {yollar}")
sozlama["web"]["portlar"].append(8080)
print(f" web ga 8080 qo'shildi → api portlari: {sozlama['api']['portlar']}")Natijaning muhim qismi:
=== 1. [[' '] * 3] * 3 ===
yurishdan keyin: [['X', ' ', ' '], ['X', ' ', ' '], ['X', ' ', ' ']]
umumiy qismlar: [['root[0]', 'root[1]', 'root[2]']]
✅ comprehension: [['X', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']], umumiy: []
=== 2. [0] * 3 xavfsiz — element o'zgarmas ===
[5, 0, 0] — uya qayta bog'landi, 0 obyekti o'zgarmadi
=== 3. dict.fromkeys va [[]] * n ===
dict.fromkeys: {'A': ['Aziz'], 'B': ['Aziz'], 'C': ['Aziz']}
[[]] * 3: [['vazifa'], ['vazifa'], ['vazifa']]
✅ defaultdict: {'A': ['Aziz']}, ✅ comprehension: [[], [], []]
=== 4. Klass atributi — hamma nusxalar uchun bitta ===
a.qosh('non') → b.mahsulotlar: ['non'], bitta obyekt: True
✅ __init__ da: False
=== 5. Murakkab konfiguratsiyada umumiy qismlar ===
bitta obyekt 4 joyda: ["root['web']['portlar']", "root['api']['portlar']", "root['zaxira'][0]", "root['zaxira'][1]['portlar']"]
web ga 8080 qo'shildi → api portlari: [80, 443, 8080]Nima ko'rsatdi: 2.2-bo'lim. umumiylar() ning o'zi ham id ni faqat tuzilma tirik bo'lgan paytda ishlatadi — 11.1-darsdagi qoidaga mos.
Misol 2 — O'zgartirish va qayta bog'lash jadvali
"""Har amal uchun: id o'zgardimi va taxallus o'zgarishni ko'rdimi — list, tuple, str, set, dict."""
def sina(nom, boshlangich, amal_matni):
muhit = {"x": boshlangich}
muhit["taxallus"] = muhit["x"]
eski_id = id(muhit["x"])
oldingi = repr(muhit["taxallus"])
exec(amal_matni, {}, muhit)
yangi_obyekt = id(muhit["x"]) != eski_id
taxallus_ozgardi = repr(muhit["taxallus"]) != oldingi
tur = "qayta bog'lash" if yangi_obyekt else "o'zgartirish"
return f" {nom:6} {amal_matni:22} {tur:15} id o'zgardi: {str(yangi_obyekt):5} taxallus ko'rdi: {taxallus_ozgardi}"
print("=== list ===")
for amal in ["x.append(4)", "x[0] = 9", "x[:] = [7, 7]", "x += [4]", "x = x + [4]", "x = [1, 2, 3]",
"x.sort(reverse=True)", "x = sorted(x)", "del x[0]"]:
print(sina("list", [1, 2, 3], amal))
print("\n=== tuple, str, int — o'zgarmas ===")
for nom, qiymat, amal in [("tuple", (1, 2), "x += (3,)"), ("str", "ab", "x += 'c'"), ("int", 5000, "x += 1"),
("str", "ab", "x = x.upper()")]:
print(sina(nom, qiymat, amal))
print("\n=== set, dict ===")
for nom, qiymat, amal in [("set", {1, 2}, "x.add(3)"), ("set", {1, 2}, "x |= {3}"), ("set", {1, 2}, "x = x | {3}"),
("dict", {"a": 1}, "x['b'] = 2"), ("dict", {"a": 1}, "x |= {'b': 2}"),
("dict", {"a": 1}, "x = x | {'b': 2}"), ("dict", {"a": 1}, "x.update(c=3)")]:
print(sina(nom, qiymat, amal))Natijaning muhim qismi:
=== list ===
list x.append(4) o'zgartirish id o'zgardi: False taxallus ko'rdi: True
list x[0] = 9 o'zgartirish id o'zgardi: False taxallus ko'rdi: True
list x[:] = [7, 7] o'zgartirish id o'zgardi: False taxallus ko'rdi: True
list x += [4] o'zgartirish id o'zgardi: False taxallus ko'rdi: True
list x = x + [4] qayta bog'lash id o'zgardi: True taxallus ko'rdi: False
list x = [1, 2, 3] qayta bog'lash id o'zgardi: True taxallus ko'rdi: False
list x.sort(reverse=True) o'zgartirish id o'zgardi: False taxallus ko'rdi: True
list x = sorted(x) qayta bog'lash id o'zgardi: True taxallus ko'rdi: False
list del x[0] o'zgartirish id o'zgardi: False taxallus ko'rdi: True
=== tuple, str, int — o'zgarmas ===
tuple x += (3,) qayta bog'lash id o'zgardi: True taxallus ko'rdi: False
str x += 'c' qayta bog'lash id o'zgardi: True taxallus ko'rdi: False
int x += 1 qayta bog'lash id o'zgardi: True taxallus ko'rdi: False
str x = x.upper() qayta bog'lash id o'zgardi: True taxallus ko'rdi: False
=== set, dict ===
set x.add(3) o'zgartirish id o'zgardi: False taxallus ko'rdi: True
set x |= {3} o'zgartirish id o'zgardi: False taxallus ko'rdi: True
set x = x | {3} qayta bog'lash id o'zgardi: True taxallus ko'rdi: False
dict x['b'] = 2 o'zgartirish id o'zgardi: False taxallus ko'rdi: True
dict x |= {'b': 2} o'zgartirish id o'zgardi: False taxallus ko'rdi: True
dict x = x | {'b': 2} qayta bog'lash id o'zgardi: True taxallus ko'rdi: False
dict x.update(c=3) o'zgartirish id o'zgardi: False taxallus ko'rdi: TrueNima ko'rsatdi: 2.1-bo'lim — += o'zgaruvchan turlarda o'zgartirish, o'zgarmaslarda qayta bog'lash; x = x + ... va x = x | ... esa har doim yangi obyekt.
Misol 3 — "O'zgarmas" obyektlar va inkapsulyatsiya sizishi
"""frozen dataclass va property da += tuzog'i; kirish/chiqishdagi sizish; himoya nusxalari va o'zgarmas ko'rinishlar."""
from dataclasses import FrozenInstanceError, dataclass, field
from types import MappingProxyType
print("=== 1. ⚠️ frozen dataclass: xato berdi, lekin o'zgardi ===")
@dataclass(frozen=True)
class Sozlama:
ruxsatlar: list = field(default_factory=list)
s = Sozlama(["o'qish"])
try:
s.ruxsatlar += ["o'chirish"]
except FrozenInstanceError as e:
print(f" FrozenInstanceError: {e}")
print(f" s.ruxsatlar: {s.ruxsatlar} ⚠️ __iadd__ allaqachon ishladi")
s.ruxsatlar.append("admin")
print(f" append ga hech qanday to'siq yo'q: {s.ruxsatlar}")
print("\n=== 2. ⚠️ Setter'siz property ===")
class Hisob:
def __init__(self):
self._tarix = []
@property
def tarix(self):
return self._tarix
h = Hisob()
try:
h.tarix += ["soxta yozuv"]
except AttributeError as e:
print(f" AttributeError: {e}")
print(f" h._tarix: {h._tarix}")
print("\n=== 3. ✅ Chuqur o'zgarmas dataclass ===")
@dataclass(frozen=True)
class SozlamaToza:
ruxsatlar: tuple = ()
chegaralar: MappingProxyType = field(default_factory=lambda: MappingProxyType({}))
def __post_init__(self):
object.__setattr__(self, "ruxsatlar", tuple(self.ruxsatlar))
object.__setattr__(self, "chegaralar", MappingProxyType(dict(self.chegaralar)))
manba_ruxsat = ["o'qish"]
manba_chegara = {"sorov": 100}
t = SozlamaToza(manba_ruxsat, manba_chegara)
manba_ruxsat.append("o'chirish")
manba_chegara["sorov"] = 999
print(f" manba o'zgargandan keyin: {t.ruxsatlar}, {dict(t.chegaralar)}")
for tavsif, amal in [("ruxsatlar += ", lambda: setattr(t, "ruxsatlar", t.ruxsatlar + ("x",))),
("ruxsatlar.append", lambda: t.ruxsatlar.append("x")),
("chegaralar['sorov'] =", lambda: t.chegaralar.__setitem__("sorov", 1))]:
try:
amal()
except (FrozenInstanceError, AttributeError, TypeError) as e:
print(f" {tavsif:24} → {type(e).__name__}")
print("\n=== 4. Inkapsulyatsiya sizishi va himoya nusxalari ===")
class GuruhYomon:
MAKS = 3
def __init__(self, talabalar):
if len(talabalar) > self.MAKS:
raise ValueError("guruh to'la")
self._talabalar = talabalar
@property
def talabalar(self):
return self._talabalar
class GuruhToza:
MAKS = 3
def __init__(self, talabalar):
talabalar = tuple(talabalar)
if len(talabalar) > self.MAKS:
raise ValueError("guruh to'la")
self._talabalar = talabalar
@property
def talabalar(self):
return self._talabalar
def qosh(self, ism):
if len(self._talabalar) >= self.MAKS:
raise ValueError("guruh to'la")
self._talabalar += (ism,)
for klass in (GuruhYomon, GuruhToza):
royxat = ["Aziz", "Dilnoza"]
g = klass(royxat)
royxat.append("Bobur")
royxat.append("Malika")
try:
g.talabalar.append("Sardor")
except AttributeError:
pass
print(f" {klass.__name__:11} kirish sizishi + chiqish sizishi → {len(g.talabalar)} talaba (MAKS {klass.MAKS}): {list(g.talabalar)}")Natijaning muhim qismi:
=== 1. ⚠️ frozen dataclass: xato berdi, lekin o'zgardi ===
FrozenInstanceError: cannot assign to field 'ruxsatlar'
s.ruxsatlar: ["o'qish", "o'chirish"] ⚠️ __iadd__ allaqachon ishladi
append ga hech qanday to'siq yo'q: ["o'qish", "o'chirish", 'admin']
=== 2. ⚠️ Setter'siz property ===
AttributeError: property 'tarix' of 'Hisob' object has no setter
h._tarix: ['soxta yozuv']
=== 3. ✅ Chuqur o'zgarmas dataclass ===
manba o'zgargandan keyin: ("o'qish",), {'sorov': 100}
ruxsatlar += → FrozenInstanceError
ruxsatlar.append → AttributeError
chegaralar['sorov'] = → AttributeError
=== 4. Inkapsulyatsiya sizishi va himoya nusxalari ===
GuruhYomon kirish sizishi + chiqish sizishi → 5 talaba (MAKS 3): ['Aziz', 'Dilnoza', 'Bobur', 'Malika', 'Sardor']
GuruhToza kirish sizishi + chiqish sizishi → 2 talaba (MAKS 3): ['Aziz', 'Dilnoza']Nima ko'rsatdi: 2.3, 2.4-bo'limlar. GuruhYomon da tekshiruv bir marta ishladi, keyin chaqiruvchi ro'yxatni tashqaridan to'ldirib yubordi; GuruhToza kirishda tuple ga o'girgani uchun ikkala teshik ham yopildi.
Misol 4 — Amaliy: iteratsiya paytida o'zgartirish va graf nusxalash
Buyurtmalar tizimida bekor qilinganlarini tozalash, kutilayotganlarni guruhlash va sozlamalar grafini saqlash — havolalar bilan bog'liq uch real vazifa.
"""list/dict/set ni iteratsiya paytida o'zgartirish; to'g'ri naqshlar; deepcopy, pickle va JSON umumiy qism va sikllar bilan."""
import copy
import json
import pickle
print("=== 1. ⚠️ Ro'yxatdan iteratsiya paytida o'chirish ===")
buyurtmalar = ["bekor", "bekor", "yangi", "bekor", "tolangan", "bekor"]
yomon = list(buyurtmalar)
for holat in yomon:
if holat == "bekor":
yomon.remove(holat)
print(f" for + remove: {yomon} ⚠️ xato yo'q, lekin 'bekor' qoldi")
ikkinchi = list(buyurtmalar)
for holat in list(ikkinchi):
if holat == "bekor":
ikkinchi.remove(holat)
print(f" ✅ nusxa ustida: {ikkinchi}")
uchinchi = list(buyurtmalar)
taxallus = uchinchi
uchinchi[:] = [h for h in uchinchi if h != "bekor"]
print(f" ✅ royxat[:] = [...]: {uchinchi}, taxallus ham yangilandi: {taxallus is uchinchi}")
print("\n=== 2. dict va set ===")
holatlar = {"#1": "bekor", "#2": "yangi", "#3": "bekor"}
try:
for kalit in holatlar:
if holatlar[kalit] == "bekor":
del holatlar[kalit]
except RuntimeError as e:
print(f" dict: RuntimeError: {e}")
for kalit in holatlar:
holatlar[kalit] = holatlar[kalit].upper()
print(f" qiymatlarni o'zgartirish ruxsat: {holatlar}")
teglar = {"shoshilinch", "katta"}
try:
for t in teglar:
teglar.add(t + "!")
except RuntimeError as e:
print(f" set: RuntimeError: {e}")
holatlar = {"#1": "bekor", "#2": "yangi", "#3": "bekor"}
for kalit in [k for k, v in holatlar.items() if v == "bekor"]:
del holatlar[kalit]
print(f" ✅ avval kalitlarni yig'ish: {holatlar}")
print("\n=== 3. Graf: umumiy qism va sikl ===")
standart = {"valyuta": "UZS", "soliq": 12}
sozlama = {"toshkent": standart, "samarqand": standart, "ota": None}
sozlama["ota"] = sozlama
def tekshir(nom, nusxa):
umumiy = nusxa["toshkent"] is nusxa["samarqand"]
sikl = nusxa["ota"] is nusxa
print(f" {nom:14} umumiy qism saqlandi: {umumiy}, sikl saqlandi: {sikl}, aslidan mustaqil: {nusxa['toshkent'] is not standart}")
tekshir("copy.copy", copy.copy(sozlama))
tekshir("copy.deepcopy", copy.deepcopy(sozlama))
tekshir("pickle", pickle.loads(pickle.dumps(sozlama)))
try:
json.dumps(sozlama)
except ValueError as e:
print(f" json (sikl bilan): ValueError: {e}")
siklsiz = {"toshkent": standart, "samarqand": standart}
j = json.loads(json.dumps(siklsiz))
print(f" json (siklsiz): umumiy qism saqlandi: {j['toshkent'] is j['samarqand']}")
j["toshkent"]["soliq"] = 15
print(f" ⚠️ json nusxasida toshkent soliqi o'zgartirildi → samarqand: {j['samarqand']['soliq']} (endi bog'lanmagan)")Natijaning muhim qismi:
=== 1. ⚠️ Ro'yxatdan iteratsiya paytida o'chirish ===
for + remove: ['yangi', 'tolangan', 'bekor'] ⚠️ xato yo'q, lekin 'bekor' qoldi
✅ nusxa ustida: ['yangi', 'tolangan']
✅ royxat[:] = [...]: ['yangi', 'tolangan'], taxallus ham yangilandi: True
=== 2. dict va set ===
dict: RuntimeError: dictionary changed size during iteration
qiymatlarni o'zgartirish ruxsat: {'#2': 'YANGI', '#3': 'BEKOR'}
set: RuntimeError: Set changed size during iteration
✅ avval kalitlarni yig'ish: {'#2': 'yangi'}
=== 3. Graf: umumiy qism va sikl ===
copy.copy umumiy qism saqlandi: True, sikl saqlandi: False, aslidan mustaqil: False
copy.deepcopy umumiy qism saqlandi: True, sikl saqlandi: True, aslidan mustaqil: True
pickle umumiy qism saqlandi: True, sikl saqlandi: True, aslidan mustaqil: True
json (sikl bilan): ValueError: Circular reference detected
json (siklsiz): umumiy qism saqlandi: False
⚠️ json nusxasida toshkent soliqi o'zgartirildi → samarqand: 12 (endi bog'lanmagan)Nima ko'rsatdi: 2.5, 2.6-bo'limlar. Ro'yxat xatosiz, lekin noto'g'ri natija berdi — bu dict va set dagi RuntimeError dan xavfliroq. JSON orqali "chuqur nusxa" esa umumiy sozlamani ikki mustaqil nusxaga ajratib, dasturning ma'nosini o'zgartirdi.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
"x += y har doim x = x + y" |
O'zgaruvchan turlarda joyida o'zgartirish — taxalluslar ham ko'radi |
"[[0]*3]*3 — uch mustaqil qator" |
Bitta ro'yxatga uch havola |
"frozen dataclass ichidagi hamma narsa o'zgarmas" |
Faqat maydonlarni qayta bog'lash taqiqlangan |
| "Xato ko'tarilgan bo'lsa, hech narsa o'zgarmagan" | obj.maydon += [...] — o'zgarish xatodan oldin bo'ladi |
"Setter'siz property — faqat o'qish" |
Qaytarilgan o'zgaruvchan obyektni o'zgartirish mumkin |
| "Ro'yxatdan iteratsiya paytida o'chirish — xato beradi" | Jim, noto'g'ri natija |
"JSON orqali chuqur nusxa — deepcopy bilan bir xil" |
Umumiy qismlar ajraladi, sikllar xato beradi |
| "Klass atributidagi ro'yxat — har nusxaning o'ziniki" | Barcha nusxalar bo'lishadi |
6. Keng tarqalgan xatolar va yechimlari
1. Ichma-ich ro'yxatni ko'paytirish
jadval = [[0] * ustunlar] * qatorlar # ❌
jadval = [[0] * ustunlar for _ in range(qatorlar)] # ✅2. dict.fromkeys bilan o'zgaruvchan qiymat
guruhlar = dict.fromkeys(nomlar, []) # ❌
guruhlar = {n: [] for n in nomlar} # ✅3. Klass darajasidagi o'zgaruvchan atribut
class Foydalanuvchi:
teglar = [] # ❌ umumiy
class Foydalanuvchi:
def __init__(self):
self.teglar = [] # ✅4. Qabul qilingan ro'yxatni to'g'ridan-to'g'ri saqlash
def __init__(self, elementlar):
self.elementlar = elementlar # ❌ chaqiruvchi keyin o'zgartiradi
self.elementlar = list(elementlar) # ✅ (yoki tuple)5. Ichki holatni qaytarish
@property
def buyurtmalar(self):
return self._buyurtmalar # ❌ tashqaridan append
return tuple(self._buyurtmalar) # ✅6. frozen obyektda o'zgaruvchan maydon
@dataclass(frozen=True)
class Marshrut:
bekatlar: list # ❌ ichi o'zgaradi
bekatlar: tuple # ✅ + __post_init__ da tuple(...)7. Iteratsiya paytida o'chirish
for x in royxat:
if yomon(x):
royxat.remove(x) # ❌ o'tkazib yuboradi
royxat[:] = [x for x in royxat if not yomon(x)] # ✅8. JSON bilan chuqur nusxa
nusxa = json.loads(json.dumps(sozlama)) # ❌ umumiy qismlar, sikllar, turlar yo'qoladi
nusxa = copy.deepcopy(sozlama) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 3.1, 7.2-darslar (o'tilgan): yorliq modeli va argument uzatish
- 6.18-dars (o'tilgan):
copyvadeepcopy - 8.5, 8.17-darslar (o'tilgan): klass va nusxa atributlari,
dataclass(frozen=True) - 10.14-dars (o'tilgan):
__iadd__,t[0] += [x]tuzog'i - 11.4–11.5-darslar: havolalar soni va sikllar — obyekt qachon o'ladi
- 11.6-dars: o'zgaruvchan standart argument — taxallusning maxsus holati
- 14-qism: iplar orasida umumiy o'zgaruvchan obyektlar va poyga holati
- 24-qism: NumPy kesmalari — nusxa emas, ko'rinish (view)
8. Eng yaxshi amaliyotlar
Har amalda so'rang: obyekt o'zgaradimi yoki nom qayta bog'lanadimi?
Ichma-ich o'zgaruvchan tuzilmalarni comprehension bilan yarating — ko'paytirish bilan emas.
O'zgaruvchan qiymatlarni klass darajasida emas,
__init__da.Qabul qilingan o'zgaruvchan argumentni saqlashdan oldin nusxalang yoki o'zgarmas turga o'giring.
Ichki holatni o'zgarmas ko'rinish sifatida qaytaring —
tuple,frozenset,MappingProxyType."O'zgarmas" obyektlarda maydonlarni ham o'zgarmas qiling —
frozensayoz.Iteratsiya paytida konteynerni o'zgartirmang — nusxa yoki yangi konteyner.
Chuqur nusxa uchun
deepcopy— JSON faqat serializatsiya uchun.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
from dataclasses import dataclass, field, FrozenInstanceError
1. a = [1, 2]
b = a
a += [3]
print(b)
2. a = [1, 2]
b = a
a = a + [3]
print(b)
3. a = (1, 2)
b = a
a += (3,)
print(b, a is b)
4. m = [[0] * 2] * 2
m[1][0] = 5
print(m)
5. d = dict.fromkeys("ab", [])
d["a"].append(1)
print(d)
6. class K:
x = []
K().x.append(1)
print(K().x)
7. @dataclass(frozen=True)
class F:
l: list = field(default_factory=list)
f = F()
try:
f.l += [1]
except FrozenInstanceError:
pass
print(f.l)
8. r = [1, 2, 2, 3]
for x in r:
if x == 2:
r.remove(x)
print(r)
9. d = {"a": 1}
for k in d:
d[k] = 2
print(d)
10. s = [1, 2]
t = s
s[:] = [9]
print(t, s is t)
11. x = [1]
y = [x, x]
y[0].append(2)
print(y)
12. import copy
x = [1]
y = copy.deepcopy([x, x])
print(y[0] is y[1], y[0] is x)Javoblar
[1, 2, 3]—+=joyida o'zgartirdi[1, 2]—a + [3]yangi ro'yxat(1, 2) False— kortej o'zgarmas,aqayta bog'landi[[5, 0], [5, 0]]— bitta ro'yxat ikki marta{'a': [1], 'b': [1]}[1]— klass atributi umumiy[1]— xato berildi, lekin ro'yxat o'zgardi[1, 2, 3]— ikkinchi2o'tkazib yuborildi{'a': 2}— qiymatni o'zgartirish ruxsat[9] True— joyida almashtirish[[1, 2], [1, 2]]True False—deepcopyumumiy qismni saqlaydi, lekin asldan ajraladi
Vazifa 2: Xatolarni tuzating
1. def yangi_taxta(n):
return [["."] * n] * n
2. class Kutubxona:
kitoblar = {}
def qosh(self, isbn, nom):
self.kitoblar[isbn] = nom
3. @dataclass(frozen=True)
class Marshrut:
bekatlar: list
m = Marshrut(bekatlar)
bekatlar.append("Chilonzor") # m ga ta'sir qilmasligi kerak edi
4. def faol_emaslarni_ochir(foydalanuvchilar):
for f in foydalanuvchilar:
if not f["faol"]:
foydalanuvchilar.remove(f)
5. def konfiguratsiya_nusxasi(cfg):
return json.loads(json.dumps(cfg)) # cfg da datetime va umumiy qismlar borJavoblar
1. def yangi_taxta(n):
return [["."] * n for _ in range(n)]
2. class Kutubxona:
def __init__(self):
self.kitoblar = {} # har nusxaga o'z lug'ati
def qosh(self, isbn, nom):
self.kitoblar[isbn] = nom
3. @dataclass(frozen=True)
class Marshrut:
bekatlar: tuple
def __post_init__(self):
object.__setattr__(self, "bekatlar", tuple(self.bekatlar))
4. def faol_emaslarni_ochir(foydalanuvchilar):
foydalanuvchilar[:] = [f for f in foydalanuvchilar if f["faol"]]
5. def konfiguratsiya_nusxasi(cfg):
return copy.deepcopy(cfg) # turlar, umumiy qismlar va sikllar saqlanadiVazifa 3: taxalluslar_xaritasi(tuzilma)
Misol 1 dagi umumiylar ni kengaytiring:
- Sikllarni aniqlasin va alohida ko'rsatsin ("
root['ota']→root") dataclassva__dict__li obyektlar ichiga ham kirsin- Har umumiy obyekt uchun "xavf darajasi" — o'zgaruvchan bo'lsa yuqori,
tuple/frozensetbo'lsa past - Natijani daraxt ko'rinishida chop etsin
Vazifa 4: Chuqur o'zgarmas konvertor
muzlat(obj) funksiyasini yozing:
list→tuple,set→frozenset,dict→MappingProxyType(rekursiv)- Umumiy qismlarni saqlasin (bir obyekt — bir natija,
memo) - Sikllarda cheksiz rekursiyaga tushmasin (sikl bo'lsa
ValueError, chunki kortejda sikl yasab bo'lmaydi — nega?) eritish(obj)— teskari amal@dataclass(frozen=True)uchun__post_init__da avtomatik ishlatiladigan dekorator
Vazifa 5: Himoyalangan kolleksiya
Buyurtmalar klassini yozing:
- Ichida
list, tashqariga faqat o'qish uchun ko'rinish (collections.abc.Sequenceasosida, nusxa olmasdan) qosh,bekor_qil— tekshiruvlar bilan- Tashqi kod iteratsiya qilayotgan paytda
qoshchaqirilsa —RuntimeError("kolleksiya iteratsiya paytida o'zgardi"),dictkabi (versiya hisoblagichi bilan) - Nusxa va ko'rinish usullarining tezligini 100 000 element bilan solishtiring
Vazifa 6: Nusxalash strategiyalari solishtiruvi
Bir xil sozlamalar grafi uchun (umumiy qismlar, sikl, datetime, Decimal, o'z klassingiz nusxasi):
copy.copy,copy.deepcopy,pickle,json(+default=str),marshalnatijalarini jadvalda solishtiring: umumiy qism, sikl, turlar, tezlik__deepcopy__yozib, klassingiz uchun ba'zi maydonlarni nusxalamaslikni (masalan, ulanish obyekti) ta'minlang- Qaysi vaziyatda qaysi usul to'g'ri ekanini xulosa qiling
Vazifa 7: O'ylash
Python'da barcha qiymatlar havola orqali ushlanadi va o'zgarmaslik sayoz. Rust'da esa egalik (ownership) va qarz olish (borrowing) tizimi bor, funksional tillarda (Haskell, Clojure) ma'lumotlar sukut bo'yicha o'zgarmas. Bu yondashuvlar taxallus xatolarini qanday hal qiladi va Python dasturchisi ulardan nimani o'rganishi mumkin?
Javob
Qisqa javob: taxallus xatosi — ikki joydan bir o'zgaruvchan obyektga ta'sir qilish va biri ikkinchisidan bexabar bo'lishi. Rust buni kompilyator darajasida taqiqlaydi: bir vaqtda yo bitta o'zgartiruvchi, yo ko'p o'qiydigan havola. Funksional tillar esa o'zgartirishni umuman olib tashlaydi: "o'zgartirish" yangi qiymat yaratadi, eski qiymat esa hech qachon o'zgarmaydi. Python ikkala kafolatni ham bermaydi, lekin dasturchi shu g'oyalarni konvensiya va o'zgarmas turlar orqali qo'llay oladi.
1. Rust: egalik va qarz olish
let mut royxat = vec![1, 2, 3];
let birinchi = &royxat[0]; // o'qish uchun qarz
royxat.push(4); // ❌ kompilyatsiya xatosi: o'qish qarzi tirik paytda o'zgartirish- Har qiymatning bitta egasi bor
&T— ko'p o'qish havolalari,&mut T— faqat bitta o'zgartirish havolasi, ikkalasi bir vaqtda emas- Taxallus + o'zgartirish xatolari, iteratsiya paytida o'zgartirish va ma'lumotlar poygasi kompilyatsiyada ushlanadi
- O'rganish qiyin; ba'zi tuzilmalar (graflar, ikki tomonlama bog'langan ro'yxatlar) murakkab (
Rc<RefCell<T>>)
2. Funksional tillar: o'zgarmas ma'lumot
(def sozlama {:portlar [80 443]})
(def yangi (update sozlama :portlar conj 8080))
; sozlama — o'zgarmagan, yangi — {:portlar [80 443 8080]}- "O'zgartirish" — yangi qiymat; eski qiymat abadiy bir xil
- Tuzilmaviy bo'lishish (structural sharing): yangi versiya eski versiyaning o'zgarmagan qismlarini qayta ishlatadi — nusxalash arzon
- Taxallus xavfsiz — umumiy qismni hech kim o'zgartira olmaydi; iplar orasida qulf kerak emas
- Ba'zi algoritmlar sekinroq yoki boshqacha yozilishi kerak; xotira yuki
3. Python holati
- Hamma narsa havola, o'zgarmaslik sayoz, kompilyator taxalluslarni tekshirmaydi
- O'zgarmas turlar bor (
tuple,frozenset,str,frozendataclass), lekin ichma-ich o'zgarmaslik avtomatik emas - Tuzilmaviy bo'lishuvchi kolleksiyalar standart kutubxonada yo'q (uchinchi tomon kutubxonalari bor, masalan
pyrsistent)
4. Python dasturchisi nimani o'rganishi mumkin
Rust'dan — egalik g'oyasi:
- Har o'zgaruvchan obyektning aniq egasi bo'lsin: kim uni o'zgartirishga haqli
- Chegara orqali o'tganda (funksiya argumenti, qaytarish qiymati, klassga saqlash) — nusxa yoki o'zgarmas ko'rinish (2.4)
- Iteratsiya paytida o'zgartirmaslik — Rust kompilyatori taqiqlaydigan narsani qo'lda taqiqlash
Funksional tillardan — o'zgarmas qiymatlar:
- Qiymat obyektlarini (
Pul,Sana,Sozlama) chuqur o'zgarmas qiling - "O'zgartirish" o'rniga yangi obyekt qaytaring:
dataclasses.replace(obj, maydon=...),obj | {"yangi": 1},sorted(x) - O'zgartirishni chekkaga suring: yadro sof funksiyalar, holat esa kichik, aniq joylarda
5. Amaliy qoidalar
| Qoida | Qaysi g'oya |
|---|---|
| Argumentni saqlashdan oldin nusxalang | Egalik |
Ichki holatni tuple/MappingProxyType sifatida qaytaring |
Qarz — faqat o'qish |
Qiymat obyektlari — frozen + o'zgarmas maydonlar |
O'zgarmaslik |
dataclasses.replace, ` |
` operatori |
| Iplar orasida o'zgarmas obyektlar uzating | Poygasiz bo'lishish |
| Iteratsiya paytida o'zgartirmang | Qarz qoidasi |
6. Xulosa
- Taxallus xatosi — bir o'zgaruvchan obyektga bexabar ikki havola
- Rust buni egalik va qarz olish qoidalari bilan kompilyatsiyada taqiqlaydi
- Funksional tillar o'zgartirishni olib tashlab, tuzilmaviy bo'lishish bilan samaradorlikni saqlaydi
- Python kafolat bermaydi — lekin egalik chegaralari, o'zgarmas qiymat obyektlari va yangi qiymat qaytarish odati bilan xuddi shu xavfsizlikni amalda olish mumkin
- Eng ko'p foyda — chegaralarda (API, klass kiritish/chiqarishi, iplar) aniq qoidalar
Nimani mustahkamlaydi: 2.1–2.6-bo'limlar.
Xulosa
Bu darsda havola semantikasini butun til bo'ylab ko'rib chiqdik.
Eng muhim uch fikr:
Har amal yo obyektni o'zgartiradi, yo nomni qayta bog'laydi. Nuqta, qavs va mutatsiya metodlari (
x.append,x[i] =,x.a =,x[:] =) obyektni o'zgartiradi va barcha taxalluslar buni ko'radi; yalang'och nomga=faqat nomni qayta bog'laydi.+=o'zgaruvchan turlarda joyida o'zgartiradi, o'zgarmaslarda yangi obyekt yaratadi. Konteyner elementlari ham havola —[[0]*3]*3,[[]]*n,dict.fromkeys(k, [])va klass atributidagi ro'yxat bitta obyektni ko'p joyga qo'yadi.O'zgarmaslik sayoz, chegaralar esa himoya talab qiladi.
tuple,frozendataclass, setter'sizpropertyvaMappingProxyTypefaqat o'z havolalarini himoyalaydi;obj.maydon += [...]ichki ro'yxatni o'zgartirib, keyin xato beradi. Qabul qilingan o'zgaruvchan argumentni saqlashdan oldin nusxalang, ichki holatni o'zgarmas ko'rinish sifatida qaytaring va qiymat obyektlarining maydonlarini ham o'zgarmas turlarga o'giring.Iteratsiya va nusxalash — havolalar grafi bilan ishlash. Ro'yxatni iteratsiya paytida o'zgartirish jim noto'g'ri natija beradi,
dictvasetesaRuntimeErrorko'taradi — nusxa ustida iteratsiya qiling yoki yangi konteyner yig'ing.deepcopyvapickleumumiy qismlar va sikllarni saqlaydi, JSON esa umumiy qismlarni ajratib, sikllarda xato beradi.
Keyingi darsda obyektlar qachon o'lishini o'rganamiz: havolalar hisobi (reference counting), sys.getrefcount, __del__ qachon chaqiriladi, qaysi amallar havola qo'shadi va nima uchun obyekt kutilganidan uzoq yashashi mumkin.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!