IlmHamroh
Python kursi/OOP10/20-dars38 daqiqa
Mundarija (21)

8.10-dars: Kapsulatsiya, _ va __

8-QISM — OBYEKTGA YO'NALTIRILGAN DASTURLASH · 10-dars


1. Kirish va motivatsiya

Pythonda private kalit so'zi yo'q:

python
class Hisob:
    def __init__(self):
        self.balans = 1000
        self._ichki = "ichki"
        self.__sirli = "sirli"


h = Hisob()
h.balans                        # 1000     ✅ ommaviy
h._ichki                        # 'ichki'  ⚠️ ishlaydi!
h.__sirli                       # ❌ AttributeError
h._Hisob__sirli                 # 'sirli'  ⚠️ ishlaydi!

Uch savol:

  • Nega _ichki ga kirish mumkin?
  • __sirli qayerga g'oyib bo'ldi?
  • Agar hammasiga kirish mumkin bo'lsa, kapsulatsiya bormi?

Java dasturchi buni ko'rib: "Pythonda kapsulatsiya yo'q" deydi. Bu — noto'g'ri. Python boshqa yondashuvni tanlagan:

"Biz hammamiz kattalarmiz" (we're all consenting adults here)

— Python jamoasining norasmiy shiori

Bu darsda:

  • _ — kelishuv, majburlash emas
  • __ — nom buzilishi (name mangling), yashirish emas
  • Nom buzilishining haqiqiy maqsadi
  • @property bilan nazorat
  • __slots__, __setattr__ bilan cheklash
  • Pythonning kapsulatsiya falsafasi

2. Nazariya — chuqur tushuntirish

2.1. Uch daraja

python
class A:
    ommaviy = 1                 # ✅ har kim ishlatishi mumkin
    _ichki = 2                  # ⚠️ kelishuv: tegmang
    __xususiy = 3               # ⚠️ nom buzilishi
Prefiks Nomi Ma'nosi Majburlanadimi
nom Ommaviy Ommaviy API —
_nom Ichki (protected) "Bu — ichki tafsilot" Yo'q
__nom Nom buzilishi Avlodda to'qnashuvni oldini olish Qisman
__nom__ Dunder Pythonning maxsus metodlari —

2.2. _ — kelishuv

python
class Ombor:
    def __init__(self):
        self._malumot = {}      # ⚠️ "tegmang"

    def saqla(self, k, v):
        self._malumot[k] = v    # ✅ ichkarida ishlatiladi

_ nimani bildiradi:

  1. "Bu — ichki tafsilot, ommaviy API emas"
  2. "Kelasi versiyada o'zgarishi mumkin"
  3. "Ishlatsangiz — o'z javobgarligingizda"

Amaliy ta'siri:

python
from modul import *             # ⭐ _ bilan boshlanadiganlar KELMAYDI
help(obj)                       # ⚠️ ko'rsatadi
dir(obj)                        # ⚠️ ko'rsatadi
IDE avtoto'ldirish              # odatda yashiradi
linterlar                       # ogohlantiradi

Lekin hech narsa to'smaydi:

python
o = Ombor()
o._malumot["x"] = 1             # ✅ ishlaydi
o._malumot = None               # ✅ ishlaydi (obyektni buzadi)

2.3. __ — nom buzilishi (name mangling)

python
class A:
    def __init__(self):
        self.__x = 1

a = A()
a.__dict__                      # {'_A__x': 1}   ⭐ nom O'ZGARDI

Qoida: sinf tanasidagi __nom (ikki pastki chiziq bilan boshlanadi, ko'pi bilan bitta bilan tugaydi) _SinfNomi__nom ga aylanadi.

python
class Hisob:
    def __init__(self):
        self.__balans = 0       # → self._Hisob__balans

    def olish(self):
        return self.__balans    # → self._Hisob__balans (avtomatik)

Bu — yashirish emas:

python
h = Hisob()
h.__balans                      # ❌ AttributeError
h._Hisob__balans                # ✅ 0

Haqiqiy maqsad — avlodda to'qnashuvni oldini olish:

python
class Ota:
    def __init__(self):
        self.__holat = "ota"    # _Ota__holat

    def ota_metodi(self):
        return self.__holat     # _Ota__holat


class Bola(Ota):
    def __init__(self):
        super().__init__()
        self.__holat = "bola"   # _Bola__holat  ⭐ BOSHQA atribut

    def bola_metodi(self):
        return self.__holat     # _Bola__holat


b = Bola()
b.ota_metodi()                  # 'ota'   ✅ buzilmadi
b.bola_metodi()                 # 'bola'
b.__dict__                      # {'_Ota__holat': 'ota', '_Bola__holat': 'bola'}

Bir prefikssiz:

python
class Ota:
    def __init__(self): self._holat = "ota"
class Bola(Ota):
    def __init__(self):
        super().__init__()
        self._holat = "bola"    # ⚠️ Ota niki YO'Q BO'LDI

2.4. Nom buzilishi qoidalari

Qachon amal qiladi:

python
class A:
    __x = 1                     # ✅ _A__x
    def f(self):
        self.__y = 2            # ✅ _A__y
        __z = 3                 # ✅ lokal — buzilmaydi (funksiyada)

Qachon amal qilmaydi:

python
__nom__                         # ❌ dunder — buzilmaydi
__nom_                          # ✅ buziladi (bitta _ bilan tugaydi)
__nom__                         # ❌ ikki _ bilan tugaydi
_nom                            # ❌ bitta _
__                              # ❌ faqat pastki chiziqlar

Sinf tashqarisida ishlamaydi:

python
class A:
    def __init__(self): self.__x = 1

a = A()
a.__x                           # ❌ AttributeError — buzilish yo'q

Satr orqali kirish buzilmaydi:

python
class A:
    def f(self):
        return getattr(self, "__x")     # ⚠️ buzilmaydi → AttributeError
        return self.__x                 # ✅ buziladi

Metodlarda ham:

python
class Ota:
    def f(self): return self.__g()      # _Ota__g
    def __g(self): return "ota"

class Bola(Ota):
    def __g(self): return "bola"        # _Bola__g — BOSHQA metod

Bola().f()                              # 'ota' ✅ qayta aniqlanmadi

Bu — mo'rt asosiy sinf muammosining yechimi (8.6-darsda ko'rgan edik).

2.5. @property bilan nazorat

Kapsulatsiyaning haqiqiy vositasi — nazorat qilingan kirish:

python
class Harorat:
    def __init__(self, selsiy: float = 0):
        self._selsiy = 0
        self.selsiy = selsiy            # setter tekshiradi

    @property
    def selsiy(self) -> float:
        return self._selsiy

    @selsiy.setter
    def selsiy(self, q: float) -> None:
        if q < -273.15:
            raise ValueError("Mutlaq noldan past")
        self._selsiy = q

    @property
    def farengeyt(self) -> float:       # ⭐ faqat o'qish
        return self._selsiy * 9 / 5 + 32

Afzalligi — chaqiruvchi kod o'zgarmaydi:

python
# 1-versiya
class A:
    def __init__(self): self.x = 0      # oddiy atribut

# 2-versiya — nazorat qo'shildi
class A:
    @property
    def x(self): return self._x
    @x.setter
    def x(self, q):
        if q < 0: raise ValueError(...)
        self._x = q

a.x = 5                                 # ✅ ikkalasida ham bir xil

Shuning uchun Pythonda getter/setter yozilmaydi — kerak bo'lganda @property qo'shiladi.

@property to'liq — 8.15-darsda.

2.6. Boshqa cheklash usullari

A) __slots__ — yangi atribut qo'shishni to'sadi:

python
class A:
    __slots__ = ("x", "y")

a = A()
a.x = 1                         # ✅
a.z = 2                         # ❌ AttributeError

B) __setattr__ — har yozishni nazorat qiladi:

python
class Muzlatilgan:
    def __init__(self, x):
        object.__setattr__(self, "x", x)

    def __setattr__(self, nom, q):
        raise AttributeError(f"O'zgarmas: {nom}")

C) @dataclass(frozen=True):

python
@dataclass(frozen=True)
class Nuqta:
    x: float
    y: float

n = Nuqta(1, 2)
n.x = 5                         # ❌ FrozenInstanceError

D) MappingProxyType — o'zgarmas ko'rinish:

python
from types import MappingProxyType

class A:
    def __init__(self):
        self._d = {"a": 1}

    @property
    def d(self):
        return MappingProxyType(self._d)     # ✅ o'qish uchun

2.7. Pythonning falsafasi

Nega private yo'q:

  1. Amaliy foyda kam. C++/Java da ham private ni chetlab o'tish mumkin (refleksiya, ko'rsatkichlar).

  2. Nosozlikni tuzatish qiyinlashadi. Ichki holatni ko'ra olmaslik — muammo.

  3. Test qilish qiyinlashadi. Ichki metodni to'g'ridan-to'g'ri test qilish kerak bo'lishi mumkin.

  4. Monkey patching mumkin bo'lmaydi. Uchinchi tomon kutubxonasidagi xatoni tuzatib bo'lmaydi.

  5. Ishonch madaniyati. "Hujjatda _ deb yozilgan — demak tegmang" yetarli.

Guido van Rossum:

"Pythonda hamma narsa ochiq. Biz dasturchilarni kattalar deb hisoblaymiz. Agar kimdir ichki API ni ishlatsa — u nima qilayotganini biladi va oqibatini o'z zimmasiga oladi."

Bu javobgarlikni ham anglatadi:

python
# Kutubxona muallifi:
class Kutubxona:
    def _ichki(self): ...       # "kelasi versiyada o'zgaradi"

# Foydalanuvchi:
k._ichki()                      # ⚠️ o'z javobgarligida
                                #    versiya yangilansa buziladi

3. Tez ma'lumotnoma

Uch daraja

python
nom         ommaviy API
_nom        ichki (kelishuv, majburlanmaydi)
__nom       nom buzilishi → _Sinf__nom
__nom__     dunder (Python uchun)

Nom buzilishi

python
class A:
    __x = 1                 → _A__x
    def __g(self): ...      → _A__g

Qoidalar:
  ✅ __nom, __nom_          buziladi
  ❌ __nom__, _nom, __      buzilmaydi
  ❌ sinf tashqarisida      buzilmaydi
  ❌ getattr(o, "__x")      buzilmaydi

MAQSADI: avlodda nom to'qnashuvini oldini olish
EMAS:    yashirish (a._A__x ishlaydi)

Nazorat vositalari

python
@property               nazorat qilingan kirish  ⭐ asosiy
__slots__               yangi atributni to'sadi
__setattr__             har yozishni ushlaydi
@dataclass(frozen=True) o'zgarmas
MappingProxyType        o'qish uchun ko'rinish

from modul import *

_nom  → import qilinmaydi
__all__ → aniq ro'yxat

4. Batafsil misollar

Misol 1 — Uch daraja

python
"""_, __ va ommaviy atributlar."""

print("=== 1. Uch daraja ===\n")


class Namoyish:
    """Uch xil atribut."""

    OMMAVIY_KONST = "hamma ishlatishi mumkin"
    _ICHKI_KONST = "tegmang"
    __XUSUSIY_KONST = "nom buzildi"

    def __init__(self):
        self.ommaviy = "ommaviy qiymat"
        self._ichki = "ichki qiymat"
        self.__xususiy = "xususiy qiymat"

    def ommaviy_metod(self):
        return "ommaviy metod"

    def _ichki_metod(self):
        return "ichki metod"

    def __xususiy_metod(self):
        return "xususiy metod"

    def hammasini_ol(self):
        """⭐ Ichkarida hammasiga kirish mumkin."""
        return {
            "ommaviy": self.ommaviy,
            "_ichki": self._ichki,
            "__xususiy": self.__xususiy,        # ⭐ ishlaydi
            "__xususiy_metod": self.__xususiy_metod(),
        }


n = Namoyish()

print(f"  Ichkaridan (hammasi ishlaydi):")
for k, v in n.hammasini_ol().items():
    print(f"    {k:<18} {v!r}")

print(f"\n  Tashqaridan:")
KIRISHLAR = [
    ("n.ommaviy",           lambda: n.ommaviy),
    ("n._ichki",            lambda: n._ichki),
    ("n.__xususiy",         lambda: n.__xususiy),
    ("n._Namoyish__xususiy", lambda: n._Namoyish__xususiy),
    ("n.ommaviy_metod()",   lambda: n.ommaviy_metod()),
    ("n._ichki_metod()",    lambda: n._ichki_metod()),
    ("n.__xususiy_metod()", lambda: n.__xususiy_metod()),
    ("n._Namoyish__xususiy_metod()",
     lambda: n._Namoyish__xususiy_metod()),
]

for kod, f in KIRISHLAR:
    try:
        natija = f"✅ {f()!r}"
    except AttributeError as e:
        natija = f"❌ AttributeError"
    print(f"    {kod:<32} {natija}")


print("\n=== ⭐ 2. __dict__ da nima bor ===\n")

print(f"  n.__dict__:")
for k, v in n.__dict__.items():
    print(f"    {k:<24} {v!r}")

print(f"\n  ⭐ '__xususiy' YO'Q — u '_Namoyish__xususiy' bo'ldi")

print(f"\n  Namoyish.__dict__ dagi atributlar:")
for k in Namoyish.__dict__:
    if not k.startswith("__") or k.startswith("_Namoyish"):
        print(f"    {k}")


print("\n=== 3. Nom buzilishi qoidalari ===\n")


class Qoidalar:
    __ikki = "buziladi"                 # ✅
    __ikki_ = "buziladi"                # ✅ bitta _ bilan tugaydi
    __ikki__ = "buzilmaydi"             # ❌ dunder
    _bitta = "buzilmaydi"               # ❌
    __ = "buzilmaydi"                   # ❌ faqat _
    ___uch = "buziladi"                 # ✅ ikkidan ko'p ham


print(f"  {'Yozilgan nom':<16} {'__dict__ dagi nom':<26} {'Buzildimi'}")
print("  " + "─" * 56)
NOMLAR = ["__ikki", "__ikki_", "__ikki__", "_bitta", "__", "___uch"]
for nom in NOMLAR:
    buzilgan = f"_Qoidalar{nom}"
    if buzilgan in Qoidalar.__dict__:
        print(f"  {nom:<16} {buzilgan:<26} ✅")
    elif nom in Qoidalar.__dict__:
        print(f"  {nom:<16} {nom:<26} ❌")

print(f"""
  ⭐ QOIDA: ikki (yoki ko'p) _ bilan BOSHLANADI va
     ko'pi bilan BITTA _ bilan TUGAYDI → buziladi
""")


print("=== ⚠️ 4. Nom buzilishi ishlamaydigan joylar ===\n")


class Sinov:
    def __init__(self):
        self.__x = 42

    def togri(self):
        return self.__x                         # ✅ buziladi

    def getattr_bilan(self):
        return getattr(self, "__x")             # ⚠️ buzilmaydi

    def getattr_togri(self):
        return getattr(self, "_Sinov__x")       # ✅ qo'lda

    def dict_bilan(self):
        return self.__dict__.get("__x")         # ⚠️ buzilmaydi

    def exec_bilan(self):
        d = {}
        exec("natija = self.__x", {"self": self}, d)    # ⚠️
        return d.get("natija")


s = Sinov()
USULLAR = ["togri", "getattr_bilan", "getattr_togri", "dict_bilan"]
for u in USULLAR:
    try:
        natija = f"✅ {getattr(s, u)()!r}"
    except AttributeError:
        natija = "❌ AttributeError"
    print(f"  s.{u+'()':<22} {natija}")

try:
    s.exec_bilan()
    print(f"  s.exec_bilan()          ⚠️ o'tdi")
except AttributeError:
    print(f"  s.exec_bilan()          ❌ AttributeError (exec da buzilmaydi)")

print(f"""
  ⭐ Nom buzilishi — KOMPILYATSIYA PAYTIDA, matn darajasida.
     Ish vaqtidagi satrlar (getattr, exec, eval) buzilmaydi.
""")


print("=== 5. from modul import * ===\n")

import types

modul = types.ModuleType("sinov_moduli")
exec("""
ommaviy = 1
_ichki = 2
__xususiy = 3
def f(): pass
def _g(): pass
""", modul.__dict__)

import sys
sys.modules["sinov_moduli"] = modul             # import uni shu yerdan topadi
nom_fazosi = {}
exec("from sinov_moduli import *", nom_fazosi)

keldi = sorted(k for k in nom_fazosi if not k.startswith("__built"))
print(f"  Modulda: ommaviy, _ichki, __xususiy, f, _g")
print(f"  import * dan keyin: {keldi}")
print(f"\n  ⭐ _ bilan boshlanadiganlar KELMADI")

modul2 = types.ModuleType("sinov2")
exec("""
__all__ = ["ommaviy", "_maxsus"]
ommaviy = 1
_ichki = 2
_maxsus = 3
""", modul2.__dict__)
sys.modules["sinov2"] = modul2
nf2 = {}
exec("from sinov2 import *", nf2)
keldi2 = sorted(k for k in nf2 if not k.startswith("__built"))

print(f"\n  __all__ = ['ommaviy', '_maxsus'] bilan:")
print(f"    keldi: {keldi2}")
print(f"    ⭐ __all__ ustuvor — _maxsus ham keldi")


print("\n=== 6. dir() va help() ===\n")

ommaviy_atr = [a for a in dir(n) if not a.startswith("_")]
ichki_atr = [a for a in dir(n) if a.startswith("_")
             and not a.startswith("__")]
buzilgan = [a for a in dir(n) if a.startswith("_Namoyish")]

print(f"  Ommaviy:   {ommaviy_atr}")
print(f"  _ bilan:   {[a for a in ichki_atr if not a.startswith('_Namoyish')]}")
print(f"  Buzilgan:  {buzilgan}")

print(f"""
  ⚠️ dir() HAMMASINI ko'rsatadi — yashirish yo'q.

  ⭐ IDE lar odatda _ bilan boshlanadiganlarni
     avtoto'ldirishda yashiradi (lekin yoqish mumkin).
""")

Natijaning muhim qismi:

text
=== 1. Uch daraja ===

  Tashqaridan:
    n.ommaviy                        ✅ 'ommaviy qiymat'
    n._ichki                         ✅ 'ichki qiymat'
    n.__xususiy                      ❌ AttributeError
    n._Namoyish__xususiy             ✅ 'xususiy qiymat'
    n.__xususiy_metod()              ❌ AttributeError
    n._Namoyish__xususiy_metod()     ✅ 'xususiy metod'

=== ⭐ 2. __dict__ da nima bor ===

  n.__dict__:
    ommaviy                  'ommaviy qiymat'
    _ichki                   'ichki qiymat'
    _Namoyish__xususiy       'xususiy qiymat'

=== 3. Nom buzilishi qoidalari ===

  Yozilgan nom     __dict__ dagi nom          Buzildimi
  ────────────────────────────────────────────────────────
  __ikki           _Qoidalar__ikki            ✅
  __ikki_          _Qoidalar__ikki_           ✅
  __ikki__         __ikki__                   ❌
  _bitta           _bitta                     ❌

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

Misol 2 — Nom buzilishining haqiqiy maqsadi

python
"""__ nima uchun kerak — avlodda to'qnashuv."""

print("=== ⚠️ 1. _ bilan to'qnashuv ===\n")


class KeshOta:
    """Ichki kesh ishlatadi."""

    def __init__(self):
        self._kesh = {}                     # ⚠️ bitta _
        self._urish = 0

    def ol(self, k):
        if k in self._kesh:
            self._urish += 1
            return self._kesh[k]
        q = f"hisoblangan({k})"
        self._kesh[k] = q
        return q

    def statistika(self):
        return {"hajm": len(self._kesh), "urish": self._urish}


class KeshBola(KeshOta):
    """⚠️ Bilmasdan bir xil nom ishlatdi."""

    def __init__(self):
        super().__init__()
        self._kesh = []                     # ⚠️ list, dict emas!
        self._urish = "yo'q"

    def qosh(self, x):
        self._kesh.append(x)


b = KeshBola()
b.qosh("a")
b.qosh("b")

print(f"  b.qosh('a'); b.qosh('b')")
print(f"    b._kesh = {b._kesh}")

print(f"\n  Endi ota-sinf metodini chaqiramiz:")
try:
    b.ol("kalit")
    print(f"    b.ol('kalit') → ⚠️ o'tdi")
except (AttributeError, TypeError) as e:
    print(f"    b.ol('kalit') → ❌ {type(e).__name__}: {e}")

try:
    stat = b.statistika()
except TypeError as e:
    print(f"    b.statistika() → ❌ TypeError: {e}")
else:
    print(f"    b.statistika() → {stat}   ⚠️ xato bermadi, lekin ma'nosiz")

print(f"""
  ⚠️ MUAMMO: KeshBola muallifi KeshOta ichida _kesh
     borligini bilmasdi va uni butunlay buzdi.
""")


print("=== ✅ 2. __ bilan yechim ===\n")


class KeshOtaX:
    """⭐ Xususiy atributlar — nom buzilishi bilan."""

    def __init__(self):
        self.__kesh = {}                    # → _KeshOtaX__kesh
        self.__urish = 0

    def ol(self, k):
        if k in self.__kesh:
            self.__urish += 1
            return self.__kesh[k]
        q = f"hisoblangan({k})"
        self.__kesh[k] = q
        return q

    def statistika(self):
        return {"hajm": len(self.__kesh), "urish": self.__urish}


class KeshBolaX(KeshOtaX):
    """⭐ Bir xil nom, lekin to'qnashuv YO'Q."""

    def __init__(self):
        super().__init__()
        self.__kesh = []                    # → _KeshBolaX__kesh
        self.__urish = "yo'q"

    def qosh(self, x):
        self.__kesh.append(x)

    def bola_kesh(self):
        return self.__kesh


bx = KeshBolaX()
bx.qosh("a")
bx.qosh("b")
bx.ol("kalit")
bx.ol("kalit")

print(f"  bx.qosh('a'); bx.qosh('b'); bx.ol('kalit') × 2\n")
print(f"    bx.bola_kesh()    = {bx.bola_kesh()}")
print(f"    bx.statistika()   = {bx.statistika()}   ✅ ishladi")
print(f"\n  bx.__dict__:")
for k, v in bx.__dict__.items():
    print(f"    {k:<24} {v!r}")

print(f"\n  ⭐ IKKI ALOHIDA atribut — to'qnashuv yo'q")


print("\n=== 3. Metodlar uchun ham ===\n")


class QuvurOta:
    """Shablon metod naqshi."""

    def bajar(self, malumot):
        """Ommaviy API — qayta aniqlanmasin."""
        malumot = self.__tayyorla(malumot)          # _QuvurOta__tayyorla
        malumot = self.qayta_ishla(malumot)         # ✅ kengaytiriladigan
        return self.__yakunla(malumot)              # _QuvurOta__yakunla

    def __tayyorla(self, m):
        """⭐ Xususiy — avlod tasodifan qayta aniqlay olmaydi."""
        return [str(x).strip() for x in m]

    def __yakunla(self, m):
        return " | ".join(m)

    def qayta_ishla(self, m):
        """✅ Kengaytirish nuqtasi."""
        return m


class QuvurBola(QuvurOta):
    def qayta_ishla(self, m):
        return [x.upper() for x in m]

    def __tayyorla(self, m):
        """⚠️ Bir xil nom, lekin OTA-SINFNIKI EMAS."""
        return ["BUZILGAN"]

    def __yakunla(self, m):
        return "BUZILGAN"


MALUMOT = ["  bir  ", " ikki", "uch  "]

print(f"  Kirish: {MALUMOT}\n")
print(f"  QuvurOta().bajar()  = {QuvurOta().bajar(MALUMOT)!r}")
print(f"  QuvurBola().bajar() = {QuvurBola().bajar(MALUMOT)!r}")

print(f"""
  ⭐ QuvurBola.__tayyorla va __yakunla CHAQIRILMADI —
     ular _QuvurBola__tayyorla, ota-sinf esa
     _QuvurOta__tayyorla ni chaqiradi.

     Faqat qayta_ishla() (ommaviy) kengaytirildi. ✅
""")

print(f"  Metod nomlari:")
for S in (QuvurOta, QuvurBola):
    metodlar = [m for m in S.__dict__ if "tayyorla" in m or "yakunla" in m]
    print(f"    {S.__name__:<12} {metodlar}")


print("\n=== ⚠️ 4. __ ning kamchiliklari ===\n")


class Asos:
    def __init__(self):
        self.__holat = "boshlang'ich"

    def olish(self):
        return self.__holat


class Kengaytirilgan(Asos):
    def ozgartir(self, q):
        """⚠️ Ota-sinf xususiy atributiga kirish MUMKIN EMAS."""
        try:
            self.__holat = q                # _Kengaytirilgan__holat!
            return "⚠️ yangi atribut yaratildi"
        except AttributeError as e:
            return f"❌ {e}"


k = Kengaytirilgan()
print(f"  k.olish()           = {k.olish()!r}")
print(f"  k.ozgartir('yangi') → {k.ozgartir('yangi')}")
print(f"  k.olish()           = {k.olish()!r}   ⚠️ o'zgarmadi")
print(f"  k.__dict__          = {k.__dict__}")

print(f"""
  ⚠️ __ AVLODLARNI HAM to'sadi — bu ba'zan kerak emas.

  ✅ Yechim: himoyalangan interfeys bering:

     class Asos:
         def __init__(self):
             self.__holat = "boshlang'ich"

         def _holat_orn(self, q):        # ⭐ avlodlar uchun
             self.__holat = q
""")


class AsosYaxshi:
    def __init__(self):
        self.__holat = "boshlang'ich"

    def olish(self):
        return self.__holat

    def _holat_orn(self, q):
        """⭐ Avlodlar uchun himoyalangan API."""
        if not isinstance(q, str):
            raise TypeError("Satr bo'lsin")
        self.__holat = q


class KengaytirilganYaxshi(AsosYaxshi):
    def ozgartir(self, q):
        self._holat_orn(q)                  # ✅
        return "✅ o'zgardi"


ky = KengaytirilganYaxshi()
print(f"  ky.olish()            = {ky.olish()!r}")
print(f"  ky.ozgartir('yangi')  → {ky.ozgartir('yangi')}")
print(f"  ky.olish()            = {ky.olish()!r}   ✅")


print("\n=== 5. Qachon __ ishlatish ===\n")

print("""
  ┌────────────────────────────────┬───────────────────────────┐
  │ Holat                          │ Tavsiya                   │
  ├────────────────────────────────┼───────────────────────────┤
  │ Kutubxona yozyapsiz            │ ⭐ __ (to'qnashuvdan      │
  │ (boshqalar meros oladi)        │    himoya)                │
  │ Shablon metod ichki qadamlari  │ ⭐ __                     │
  │ Mixin ichki holati             │ ⭐ __ (nom to'qnashuvi)   │
  │ Ichki loyiha, oddiy sinf       │ ✅ _ yetadi               │
  │ Avlodlar ishlatishi kerak      │ ✅ _ (protected)          │
  │ Ommaviy API                    │ ✅ prefikssiz             │
  └────────────────────────────────┴───────────────────────────┘

  ⭐ AMALIY QOIDA:
     Sukut bo'yicha _ ishlating.
     __ ni faqat AVLODDA TO'QNASHUV xavfi bo'lganda.

  ⚠️ __ ni "xavfsizlik" uchun ishlatmang — u himoya emas.
""")


print("=== 6. Amaliy: mixin ichki holati ===\n")


class KeshMixin:
    """⭐ __ bilan — boshqa mixinlar bilan to'qnashmaydi."""

    def __init__(self, **kw):
        super().__init__(**kw)
        self.__kesh = {}
        self.__urish = 0

    def _kesh_ol(self, k):
        if k in self.__kesh:
            self.__urish += 1
            return self.__kesh[k]
        return None

    def _kesh_qoy(self, k, v):
        self.__kesh[k] = v

    def kesh_holati(self):
        return {"hajm": len(self.__kesh), "urish": self.__urish}


class JurnalMixin:
    """⭐ Ham __kesh ishlatadi — lekin to'qnashuv yo'q."""

    def __init__(self, **kw):
        super().__init__(**kw)
        self.__kesh = []                    # _JurnalMixin__kesh
        self.__urish = 0

    def _yoz(self, x):
        self.__kesh.append(x)
        self.__urish += 1

    def jurnal_holati(self):
        return {"yozuvlar": len(self.__kesh), "jami": self.__urish}


class Xizmat(KeshMixin, JurnalMixin):
    def __init__(self, **kw):
        super().__init__(**kw)

    def ishla(self, k):
        self._yoz(f"so'rov: {k}")
        q = self._kesh_ol(k)
        if q is not None:
            self._yoz(f"keshdan: {k}")
            return q
        q = f"natija({k})"
        self._kesh_qoy(k, q)
        return q


x = Xizmat()
x.ishla("a"); x.ishla("b"); x.ishla("a"); x.ishla("a")

print(f"  4 ta so'rov (2 tasi takroriy):")
print(f"    kesh:   {x.kesh_holati()}")
print(f"    jurnal: {x.jurnal_holati()}")

print(f"\n  x.__dict__ kalitlari:")
for k in sorted(x.__dict__):
    print(f"    {k}")

print(f"""
  ⭐ Ikkala mixin ham __kesh va __urish ishlatadi,
     lekin ular BOSHQA atributlar:
       _KeshMixin__kesh
       _JurnalMixin__kesh

     __ bo'lmasa — ikkinchi mixin birinchisini BUZARDI.
""")

Natijaning muhim qismi:

text
=== ⚠️ 1. _ bilan to'qnashuv ===

  b._kesh = ['a', 'b']
    b.ol('kalit') → ❌ TypeError: list indices must be integers or slices, not str
    b.statistika() → {'hajm': 2, 'urish': "yo'q"}   ⚠️ xato bermadi, lekin ma'nosiz

=== ✅ 2. __ bilan yechim ===

    bx.bola_kesh()    = ['a', 'b']
    bx.statistika()   = {'hajm': 1, 'urish': 1}   ✅ ishladi

  bx.__dict__:
    _KeshOtaX__kesh          {'kalit': 'hisoblangan(kalit)'}
    _KeshOtaX__urish         1
    _KeshBolaX__kesh         ['a', 'b']
    _KeshBolaX__urish        "yo'q"

=== 3. Metodlar uchun ham ===

  QuvurOta().bajar()  = 'bir | ikki | uch'
  QuvurBola().bajar() = 'BIR | IKKI | UCH'

=== 6. Amaliy: mixin ichki holati ===

  x.__dict__ kalitlari:
    _JurnalMixin__kesh
    _JurnalMixin__urish
    _KeshMixin__kesh
    _KeshMixin__urish

Nima ko'rsatdi: 2.3, 2.4-bo'limlar.

Misol 3 — Haqiqiy kapsulatsiya vositalari

python
"""_ va __ emas, NAZORAT — haqiqiy kapsulatsiya."""

from __future__ import annotations

from dataclasses import dataclass, field, FrozenInstanceError
from types import MappingProxyType
from typing import Any

print("=== 1. @property — nazorat qilingan kirish ===\n")


class Harorat:
    """Fizik chegaralar majburlanadi."""

    MUTLAQ_NOL = -273.15

    def __init__(self, selsiy: float = 0.0):
        self._selsiy = 0.0
        self.selsiy = selsiy                    # ⭐ setter tekshiradi

    @property
    def selsiy(self) -> float:
        return self._selsiy

    @selsiy.setter
    def selsiy(self, q: float) -> None:
        if not isinstance(q, (int, float)) or isinstance(q, bool):
            raise TypeError(f"Son bo'lsin: {type(q).__name__}")
        if q < self.MUTLAQ_NOL:
            raise ValueError(f"Mutlaq noldan past: {q}")
        self._selsiy = float(q)

    @property
    def farengeyt(self) -> float:
        """⭐ Hosila — faqat o'qish."""
        return self._selsiy * 9 / 5 + 32

    @farengeyt.setter
    def farengeyt(self, q: float) -> None:
        """⭐ Teskari hisoblash bilan yozish."""
        self.selsiy = (q - 32) * 5 / 9

    @property
    def kelvin(self) -> float:
        return self._selsiy - self.MUTLAQ_NOL

    def __repr__(self):
        return f"Harorat({self._selsiy:.2f}°C)"


h = Harorat(25)
print(f"  {h}")
print(f"    farengeyt = {h.farengeyt:.2f}°F")
print(f"    kelvin    = {h.kelvin:.2f}K")

h.farengeyt = 100
print(f"\n  h.farengeyt = 100 → {h}")

print(f"\n  Himoya:")
for kod, f in [
    ("h.selsiy = -300",     lambda: setattr(h, "selsiy", -300)),
    ("h.selsiy = 'issiq'",  lambda: setattr(h, "selsiy", "issiq")),
    ("h.selsiy = True",     lambda: setattr(h, "selsiy", True)),
    ("h.kelvin = 0",        lambda: setattr(h, "kelvin", 0)),
    ("h.farengeyt = -500",  lambda: setattr(h, "farengeyt", -500)),
]:
    try:
        f(); n = "⚠️ O'TDI"
    except (ValueError, TypeError, AttributeError) as e:
        n = f"✅ {type(e).__name__}: {str(e)[:36]}"
    print(f"    {kod:<24} {n}")

print(f"\n  ⚠️ Lekin _selsiy hali ochiq:")
h._selsiy = -1000
print(f"    h._selsiy = -1000 → {h}   ⚠️ invariant buzildi")
h._selsiy = 25.0

print(f"""
  ⭐ @property — asosiy vosita, lekin _ ni to'liq to'smaydi.
     To'liq himoya uchun __setattr__ yoki __slots__ kerak.
""")


print("=== 2. __setattr__ — to'liq nazorat ===\n")


class Qatiy:
    """Har yozish tekshiriladi."""

    RUXSAT = frozenset({"nom", "yosh", "email"})
    TURLAR = {"nom": str, "yosh": int, "email": str}

    def __init__(self, nom: str, yosh: int, email: str = ""):
        self.nom = nom
        self.yosh = yosh
        self.email = email
        object.__setattr__(self, "_muzlatilgan", True)   # ⭐ oxirida

    def __setattr__(self, nom: str, q: Any) -> None:
        if getattr(self, "_muzlatilgan", False):
            raise AttributeError(f"Obyekt muzlatilgan: {nom}")
        if nom not in self.RUXSAT and not nom.startswith("_"):
            raise AttributeError(
                f"Noma'lum atribut: {nom!r}. "
                f"Ruxsat: {sorted(self.RUXSAT)}"
            )
        kutilgan = self.TURLAR.get(nom)
        if kutilgan and not isinstance(q, kutilgan):
            raise TypeError(
                f"{nom}: {kutilgan.__name__} kutildi, "
                f"{type(q).__name__} keldi"
            )
        if nom == "yosh" and not 0 <= q <= 150:
            raise ValueError(f"Yosh 0–150: {q}")
        object.__setattr__(self, nom, q)

    def __delattr__(self, nom: str) -> None:
        raise AttributeError(f"O'chirib bo'lmaydi: {nom}")

    def __repr__(self):
        return f"Qatiy({self.nom!r}, {self.yosh})"


q = Qatiy("Aziz", 30, "aziz@wisar.uz")
print(f"  {q}")

print(f"\n  Himoya:")
for kod, f in [
    ("q.nom = 'Yangi'",     lambda: setattr(q, "nom", "Yangi")),
    ("q.yoshi = 40",        lambda: setattr(q, "yoshi", 40)),
    ("del q.nom",           lambda: delattr(q, "nom")),
    ("Qatiy('A', 200)",     lambda: Qatiy("A", 200)),
    ("Qatiy('A', '30')",    lambda: Qatiy("A", "30")),
    ("Qatiy(42, 30)",       lambda: Qatiy(42, 30)),
]:
    try:
        f(); n = "⚠️ O'TDI"
    except (AttributeError, TypeError, ValueError) as e:
        n = f"✅ {type(e).__name__}: {str(e)[:40]}"
    print(f"    {kod:<24} {n}")


print("\n=== 3. __slots__ — atributlarni cheklash ===\n")


class Slotli:
    __slots__ = ("x", "y", "_ichki")

    def __init__(self, x, y):
        self.x = x
        self.y = y
        self._ichki = None

    def __repr__(self):
        return f"Slotli({self.x}, {self.y})"


s = Slotli(1, 2)
print(f"  {s}")
print(f"    s.x = {s.x}, s._ichki = {s._ichki}")

print(f"\n  Himoya:")
for kod, f in [
    ("s.x = 10",        lambda: setattr(s, "x", 10)),
    ("s.z = 3",         lambda: setattr(s, "z", 3)),
    ("s.__dict__",      lambda: s.__dict__),
]:
    try:
        natija = f()
        n = f"✅ {natija!r}" if natija is not None else "✅ o'tdi"
    except AttributeError as e:
        n = f"✅ AttributeError: {str(e)[:44]}"
    print(f"    {kod:<20} {n}")

import tracemalloc


def bayt(yasash, n: int = 20_000) -> int:
    """Bitta obyektning o'rtacha hajmi — tracemalloc bilan (8.19-dars).

    ⚠️ sys.getsizeof(o.__dict__) Python 3.11+ da lug'atni o'zi yaratib,
       natijani oshirib yuboradi — shuning uchun bu usul.
    """
    joy = [None] * n
    tracemalloc.start()
    asos = tracemalloc.get_traced_memory()[0]
    for i in range(n):
        joy[i] = yasash()
    jami = tracemalloc.get_traced_memory()[0] - asos
    tracemalloc.stop()
    return round(jami / n)


class Oddiy:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self._ichki = None


print(f"\n  Xotira (bitta obyekt, 3 atribut):")
print(f"    __slots__ bilan:  {bayt(lambda: Slotli(1, 2))} bayt")
print(f"    __dict__ bilan:   {bayt(lambda: Oddiy(1, 2))} bayt")


print("\n=== 4. frozen dataclass ===\n")


@dataclass(frozen=True, slots=True)
class Nuqta:
    x: float
    y: float

    @property
    def masofa(self) -> float:
        return (self.x ** 2 + self.y ** 2) ** 0.5

    def kochir(self, dx: float, dy: float) -> "Nuqta":
        """⭐ Yangi obyekt qaytaradi."""
        return Nuqta(self.x + dx, self.y + dy)


n = Nuqta(3, 4)
print(f"  {n}, masofa = {n.masofa}")
print(f"  n.kochir(1, 1) = {n.kochir(1, 1)}")
print(f"  n o'zgarmadi: {n}")

print(f"\n  Himoya:")
for kod, f in [
    ("n.x = 10",        lambda: setattr(n, "x", 10)),
    ("n.z = 1",         lambda: setattr(n, "z", 1)),
    ("del n.x",         lambda: delattr(n, "x")),
]:
    try:
        f(); msg = "⚠️ O'TDI"
    except (FrozenInstanceError, AttributeError) as e:
        msg = f"✅ {type(e).__name__}"
    print(f"    {kod:<16} {msg}")

print(f"\n  ⭐ Bonus: hashable → to'plam/lug'at kaliti bo'la oladi")
print(f"    {{Nuqta(1,2), Nuqta(1,2)}} → "
      f"{len({Nuqta(1,2), Nuqta(1,2)})} element")


print("\n=== 5. MappingProxyType — o'qish uchun ko'rinish ===\n")


class Sozlama:
    def __init__(self, **kw):
        self._d = dict(kw)

    @property
    def hammasi(self):
        """⭐ O'zgartirib bo'lmaydigan ko'rinish."""
        return MappingProxyType(self._d)

    def orn(self, k, v):
        """Nazorat qilingan yozish."""
        if k.startswith("_"):
            raise KeyError(f"Ichki kalit: {k}")
        self._d[k] = v


sz = Sozlama(host="localhost", port=8000)
print(f"  sz.hammasi = {dict(sz.hammasi)}")
print(f"  turi: {type(sz.hammasi).__name__}")

print(f"\n  Himoya:")
for kod, f in [
    ("sz.hammasi['port'] = 1",  lambda: sz.hammasi.__setitem__("port", 1)),
    ("sz.hammasi.clear()",      lambda: sz.hammasi.clear()),
    ("sz.orn('debug', True)",   lambda: sz.orn("debug", True)),
    ("sz.orn('_x', 1)",         lambda: sz.orn("_x", 1)),
]:
    try:
        f(); n = "✅ o'tdi"
    except (TypeError, AttributeError, KeyError) as e:
        n = f"✅ {type(e).__name__}"
    print(f"    {kod:<28} {n}")

print(f"\n  Oxirgi holat: {dict(sz.hammasi)}")

print(f"""
  ⚠️ MappingProxyType — KO'RINISH, nusxa emas:
     Asl dict o'zgarsa — ko'rinish ham o'zgaradi.
""")

k = sz.hammasi
sz.orn("yangi", 1)
print(f"    sz.orn('yangi', 1) dan keyin: {dict(k)}")


print("\n=== 6. Solishtirish ===\n")

print("""
  ┌──────────────────────┬──────────┬──────────┬───────────────┐
  │ Vosita               │ Kuchi    │ Narxi    │ Qachon        │
  ├──────────────────────┼──────────┼──────────┼───────────────┤
  │ _ prefiks            │ ⚠️ nol   │ ✅ yo'q  │ Doim (belgi)  │
  │ __ nom buzilishi     │ ⚠️ past  │ ✅ yo'q  │ Meros xavfi   │
  │ @property            │ ✅ o'rta │ ⚠️ kichik│ Invariant     │
  │ __slots__            │ ✅ o'rta │ ✅ tejaydi│ Ko'p obyekt  │
  │ __setattr__          │ ✅ yuqori│ ⚠️ har   │ Qat'iy nazorat│
  │                      │          │   yozishda│              │
  │ frozen dataclass     │ ✅ yuqori│ ✅ kichik│ O'zgarmas    │
  │ MappingProxyType     │ ✅ o'rta │ ✅ yo'q  │ Konteyner     │
  └──────────────────────┴──────────┴──────────┴───────────────┘

  ⭐ AMALIY TANLOV:

     Ma'lumot obyekti          → @dataclass(frozen=True)
     Invariant bor             → @property + setter
     Ko'p obyekt (100k+)       → __slots__
     Konteyner qaytarish       → tuple() / frozenset() / MappingProxyType
     Ichki tafsilot belgisi    → _ prefiks
     Meros to'qnashuvi xavfi   → __ prefiks
     Umuman o'zgarmasin        → __setattr__ yoki frozen
""")

Natijaning muhim qismi:

text
=== 1. @property — nazorat qilingan kirish ===

  Harorat(25.00°C)
    farengeyt = 77.00°F

  Himoya:
    h.selsiy = -300          ✅ ValueError: Mutlaq noldan past: -300
    h.selsiy = 'issiq'       ✅ TypeError: Son bo'lsin: str
    h.kelvin = 0             ✅ AttributeError: property 'kelvin' of 'Harorat' objec

  ⚠️ Lekin _selsiy hali ochiq:
    h._selsiy = -1000 → Harorat(-1000.00°C)   ⚠️ invariant buzildi

=== 2. __setattr__ — to'liq nazorat ===

    q.nom = 'Yangi'          ✅ AttributeError: Obyekt muzlatilgan: nom
    q.yoshi = 40             ✅ AttributeError: Obyekt muzlatilgan
    Qatiy('A', 200)          ✅ ValueError: Yosh 0–150: 200

=== 3. __slots__ — atributlarni cheklash ===

    s.z = 3              ✅ AttributeError: 'Slotli' object has no
                            attribute 'z'
  Xotira (bitta obyekt, 3 atribut):
    __slots__ bilan:  56 bayt
    __dict__ bilan:   96 bayt

Nima ko'rsatdi: 2.5, 2.6-bo'limlar.

Misol 4 — Amaliy: kutubxona API si

python
"""Ommaviy va ichki API ni to'g'ri ajratish."""

from __future__ import annotations

import re
from dataclasses import dataclass, field
from datetime import datetime
from types import MappingProxyType
from typing import Any, Iterator

print("=== VAZIFA BOSHQARUVCHISI ===\n")

__all__ = ["VazifaRoyxati", "Vazifa", "Holat", "VazifaXatosi"]


# ═══ OMMAVIY API ═══

class VazifaXatosi(Exception):
    """Ommaviy istisno."""


class Holat:
    """Vazifa holatlari."""
    YANGI = "yangi"
    JARAYONDA = "jarayonda"
    TUGALLANGAN = "tugallangan"
    BEKOR = "bekor"

    HAMMASI = frozenset({YANGI, JARAYONDA, TUGALLANGAN, BEKOR})
    OTISHLAR = MappingProxyType({
        YANGI:       frozenset({JARAYONDA, BEKOR}),
        JARAYONDA:   frozenset({TUGALLANGAN, BEKOR, YANGI}),
        TUGALLANGAN: frozenset(),
        BEKOR:       frozenset({YANGI}),
    })


@dataclass
class Vazifa:
    """Bitta vazifa.

    OMMAVIY: id, sarlavha, holat, teglar, muddat
    ICHKI:   _tarix (o'zgarishlar jurnali)
    """

    id: int
    sarlavha: str
    holat: str = Holat.YANGI
    muddat: datetime | None = None
    _teglar: set[str] = field(default_factory=set, repr=False)
    _tarix: list[tuple[datetime, str, str]] = field(
        default_factory=list, repr=False
    )

    def __post_init__(self):
        if not self.sarlavha.strip():
            raise VazifaXatosi("Sarlavha bo'sh bo'lmasin")
        if self.holat not in Holat.HAMMASI:
            raise VazifaXatosi(f"Noma'lum holat: {self.holat!r}")
        self.sarlavha = self.sarlavha.strip()
        self._tarix.append((datetime.now(), "", self.holat))

    # ── Ommaviy API ──

    @property
    def teglar(self) -> frozenset[str]:
        """⭐ O'zgarmas nusxa."""
        return frozenset(self._teglar)

    @property
    def tarix(self) -> tuple[tuple[datetime, str, str], ...]:
        return tuple(self._tarix)

    @property
    def tugallanganmi(self) -> bool:
        return self.holat == Holat.TUGALLANGAN

    @property
    def kechikkanmi(self) -> bool:
        if self.muddat is None or self.tugallanganmi:
            return False
        return datetime.now() > self.muddat

    def holat_ozgartir(self, yangi: str) -> None:
        """Holatni o'zgartiradi (o'tish qoidalari bilan)."""
        if yangi not in Holat.HAMMASI:
            raise VazifaXatosi(f"Noma'lum holat: {yangi!r}")
        ruxsat = Holat.OTISHLAR[self.holat]
        if yangi not in ruxsat:
            raise VazifaXatosi(
                f"{self.holat!r} → {yangi!r} mumkin emas. "
                f"Ruxsat: {sorted(ruxsat) or 'yakuniy holat'}"
            )
        self._tarix.append((datetime.now(), self.holat, yangi))
        self.holat = yangi

    def teg_qosh(self, *teglar: str) -> None:
        for t in teglar:
            t = self.__teg_normalla(t)              # ⭐ xususiy
            if t:
                self._teglar.add(t)

    def teg_ochir(self, teg: str) -> bool:
        t = self.__teg_normalla(teg)
        if t in self._teglar:
            self._teglar.discard(t)
            return True
        return False

    # ── Xususiy (nom buzilishi bilan) ──

    @staticmethod
    def __teg_normalla(teg: str) -> str:
        """⭐ Ichki normallashtirish qoidasi.

        __ bilan — avlod sinf tasodifan qayta aniqlay olmaydi.
        """
        return re.sub(r"[^\w-]", "", teg.strip().lower())


class VazifaRoyxati:
    """Vazifalar to'plami.

    OMMAVIY API:
        qosh, ochir, topish, filtrla, statistika
        __len__, __iter__, __contains__, __getitem__

    ICHKI (_):
        _vazifalar, _keyingi_id
        _tekshir — avlodlar qayta aniqlashi mumkin

    XUSUSIY (__):
        __indeks — ichki tuzilma, avlodlar tegmasin
    """

    MAKS_VAZIFA = 1000

    def __init__(self, nom: str = "Ro'yxat"):
        self.nom = nom                              # ommaviy
        self._vazifalar: dict[int, Vazifa] = {}     # ichki
        self._keyingi_id = 1
        self.__indeks: dict[str, set[int]] = {}     # xususiy
        self.__ozgarishlar = 0

    # ── Ommaviy API ──

    def qosh(self, sarlavha: str, **kw) -> Vazifa:
        if len(self._vazifalar) >= self.MAKS_VAZIFA:
            raise VazifaXatosi(f"Chegara: {self.MAKS_VAZIFA}")
        self._tekshir(sarlavha)                     # ⭐ kengaytiriladigan
        v = Vazifa(id=self._keyingi_id, sarlavha=sarlavha, **kw)
        self._vazifalar[v.id] = v
        self._keyingi_id += 1
        self.__indeksga_qosh(v)                     # ⭐ xususiy
        return v

    def ochir(self, vazifa_id: int) -> bool:
        v = self._vazifalar.pop(vazifa_id, None)
        if v is None:
            return False
        self.__indeksdan_ochir(v)
        return True

    def topish(self, vazifa_id: int) -> Vazifa:
        if vazifa_id not in self._vazifalar:
            raise VazifaXatosi(f"Topilmadi: #{vazifa_id}")
        return self._vazifalar[vazifa_id]

    def filtrla(self, *, holat: str | None = None,
                teg: str | None = None,
                kechikkan: bool | None = None) -> list[Vazifa]:
        natija = list(self._vazifalar.values())
        if holat is not None:
            natija = [v for v in natija if v.holat == holat]
        if teg is not None:
            idlar = self.__indeks.get(teg.lower(), set())
            natija = [v for v in natija if v.id in idlar]
        if kechikkan is not None:
            natija = [v for v in natija if v.kechikkanmi == kechikkan]
        return natija

    def statistika(self) -> dict[str, Any]:
        holatlar: dict[str, int] = {}
        for v in self._vazifalar.values():
            holatlar[v.holat] = holatlar.get(v.holat, 0) + 1
        return {
            "jami": len(self._vazifalar),
            "holatlar": holatlar,
            "kechikkan": sum(1 for v in self._vazifalar.values()
                             if v.kechikkanmi),
            "teglar": len(self.__indeks),
            "ozgarishlar": self.__ozgarishlar,
        }

    def teg_qosh(self, vazifa_id: int, *teglar: str) -> None:
        v = self.topish(vazifa_id)
        self.__indeksdan_ochir(v)
        v.teg_qosh(*teglar)
        self.__indeksga_qosh(v)

    # ── Konteyner protokoli ──

    def __len__(self) -> int:
        return len(self._vazifalar)

    def __iter__(self) -> Iterator[Vazifa]:
        return iter(self._vazifalar.values())

    def __contains__(self, x) -> bool:
        if isinstance(x, Vazifa):
            return x.id in self._vazifalar
        return x in self._vazifalar

    def __getitem__(self, vazifa_id: int) -> Vazifa:
        return self.topish(vazifa_id)

    def __repr__(self) -> str:
        return f"VazifaRoyxati({self.nom!r}, {len(self)} vazifa)"

    # ── Himoyalangan (_) — avlodlar kengaytirishi mumkin ──

    def _tekshir(self, sarlavha: str) -> None:
        """⭐ Kengaytirish nuqtasi."""
        if len(sarlavha) > 200:
            raise VazifaXatosi("Sarlavha juda uzun (>200)")

    # ── Xususiy (__) — avlodlar TEGMASIN ──

    def __indeksga_qosh(self, v: Vazifa) -> None:
        for t in v.teglar:
            self.__indeks.setdefault(t, set()).add(v.id)
        self.__ozgarishlar += 1

    def __indeksdan_ochir(self, v: Vazifa) -> None:
        for t in v.teglar:
            self.__indeks.get(t, set()).discard(v.id)
            if not self.__indeks.get(t):
                self.__indeks.pop(t, None)
        self.__ozgarishlar += 1


print("1. Ommaviy API:\n")

r = VazifaRoyxati("Loyiha")

v1 = r.qosh("Dizaynni tayyorlash")
v2 = r.qosh("Backend yozish", muddat=datetime(2026, 1, 1))
v3 = r.qosh("Testlarni yozish")

r.teg_qosh(v1.id, "dizayn", "UI/UX", "  MUHIM  ")
r.teg_qosh(v2.id, "backend", "python")
r.teg_qosh(v3.id, "test", "python")

v1.holat_ozgartir(Holat.JARAYONDA)
v1.holat_ozgartir(Holat.TUGALLANGAN)
v2.holat_ozgartir(Holat.JARAYONDA)

print(f"  {r}\n")
for v in r:
    teglar = ", ".join(sorted(v.teglar)) or "—"
    kech = " ⏰" if v.kechikkanmi else ""
    print(f"    #{v.id} {v.sarlavha:<24} [{v.holat:<12}] {teglar}{kech}")

print(f"\n  Statistika: {r.statistika()}")


print("\n\n2. ⭐ Teg normallashtirish (xususiy metod):\n")

print(f"  Kiritildi: 'dizayn', 'UI/UX', '  MUHIM  '")
print(f"  Saqlandi:  {sorted(v1.teglar)}")
print(f"    ⭐ Vazifa.__teg_normalla ishladi (kichik harf, belgilar olib "
      f"tashlandi)")


print("\n\n3. Holat o'tishlari:\n")

print(f"  {'Joriy':<14} {'Ruxsat etilgan'}")
print("  " + "─" * 44)
for h, ruxsat in Holat.OTISHLAR.items():
    print(f"  {h:<14} {sorted(ruxsat) or '(yakuniy)'}")

print(f"\n  Sinovlar:")
for kod, f in [
    ("v1 (tugallangan) → yangi",
     lambda: v1.holat_ozgartir(Holat.YANGI)),
    ("v3 (yangi) → tugallangan",
     lambda: v3.holat_ozgartir(Holat.TUGALLANGAN)),
    ("v3 (yangi) → jarayonda",
     lambda: v3.holat_ozgartir(Holat.JARAYONDA)),
    ("v3 → 'noma`lum'",
     lambda: v3.holat_ozgartir("noma'lum")),
]:
    try:
        f(); n = "✅ o'tdi"
    except VazifaXatosi as e:
        n = f"✅ VazifaXatosi: {str(e)[:44]}"
    print(f"    {kod:<28} {n}")


print("\n\n4. ⭐ Kapsulatsiya darajalari:\n")

TEKSHIRUV = [
    ("r.nom",                   lambda: r.nom, "ommaviy"),
    ("r.qosh(...)",             lambda: "metod", "ommaviy"),
    ("len(r)",                  lambda: len(r), "ommaviy"),
    ("r[1]",                    lambda: r[1].sarlavha, "ommaviy"),
    ("r._vazifalar",            lambda: len(r._vazifalar), "ichki (_)"),
    ("r._tekshir",              lambda: "metod", "ichki (_)"),
    ("r.__indeks",              lambda: r.__indeks, "xususiy (__)"),
    ("r._VazifaRoyxati__indeks",
     lambda: len(r._VazifaRoyxati__indeks), "xususiy (__)"),
    ("v1._teglar",              lambda: len(v1._teglar), "ichki (_)"),
    ("v1.teglar",               lambda: len(v1.teglar), "ommaviy"),
]

print(f"  {'Kirish':<32} {'Daraja':<14} {'Natija'}")
print("  " + "─" * 62)
for kod, f, daraja in TEKSHIRUV:
    try:
        natija = f"✅ {f()!r}"
    except AttributeError:
        natija = "❌ AttributeError"
    print(f"  {kod:<32} {daraja:<14} {natija}")


print("\n\n5. ⭐ Ichki ma'lumot himoyasi:\n")

for kod, f in [
    ("v1.teglar.add('yangi')",  lambda: v1.teglar.add("yangi")),
    ("v1.tarix.append(...)",    lambda: v1.tarix.append(None)),
    ("Holat.OTISHLAR['x'] = 1",
     lambda: Holat.OTISHLAR.__setitem__("x", 1)),
    ("Holat.HAMMASI.add('x')",  lambda: Holat.HAMMASI.add("x")),
]:
    try:
        f(); n = "⚠️ O'TDI"
    except (AttributeError, TypeError) as e:
        n = f"✅ {type(e).__name__}"
    print(f"  {kod:<32} {n}")

print(f"\n  ⚠️ Lekin _ bilan boshlanadiganlar ochiq:")
print(f"    v1._teglar.add('buzildi')")
v1._teglar.add("buzildi")
print(f"    v1.teglar = {sorted(v1.teglar)}   ⚠️ o'zgardi")
v1._teglar.discard("buzildi")


print("\n\n6. Avlod sinf:\n")


class UstuvorRoyxat(VazifaRoyxati):
    """⭐ _tekshir ni kengaytiradi, __indeks ga tegmaydi."""

    TAQIQLANGAN = frozenset({"spam", "test123"})

    def __init__(self, nom="Ustuvor"):
        super().__init__(nom)
        self._ustuvorliklar: dict[int, int] = {}
        self.__indeks = "⚠️ boshqa atribut"          # _UstuvorRoyxat__indeks

    def _tekshir(self, sarlavha: str) -> None:
        """⭐ Ota-sinf tekshiruvini kengaytiradi."""
        super()._tekshir(sarlavha)
        past = sarlavha.lower()
        if any(t in past for t in self.TAQIQLANGAN):
            raise VazifaXatosi(f"Taqiqlangan so'z: {sarlavha!r}")

    def qosh(self, sarlavha, *, ustuvorlik: int = 5, **kw):
        v = super().qosh(sarlavha, **kw)
        self._ustuvorliklar[v.id] = ustuvorlik
        return v

    def ustuvor_boyicha(self) -> list[Vazifa]:
        return sorted(self, key=lambda v: -self._ustuvorliklar.get(v.id, 0))


u = UstuvorRoyxat()
u.qosh("Muhim vazifa", ustuvorlik=10)
u.qosh("Oddiy vazifa", ustuvorlik=3)
u.qosh("O'rtacha", ustuvorlik=7)

print(f"  {u}")
print(f"  Ustuvorlik bo'yicha:")
for v in u.ustuvor_boyicha():
    print(f"    #{v.id} {v.sarlavha:<20} ustuvorlik={u._ustuvorliklar[v.id]}")

print(f"\n  Kengaytirilgan tekshiruv:")
try:
    u.qosh("Bu spam xabar")
except VazifaXatosi as e:
    print(f"    u.qosh('Bu spam xabar') → ✅ VazifaXatosi: {e}")

print(f"\n  ⭐ __indeks to'qnashuvi YO'Q:")
for k in sorted(k for k in u.__dict__ if "indeks" in k):
    print(f"    {k}")

print(f"""

  ⭐ BU LOYIHADA KAPSULATSIYA:

     OMMAVIY (prefikssiz):
       • qosh, ochir, topish, filtrla, statistika
       • __len__, __iter__, __getitem__
       • teglar, tarix, tugallanganmi (property)
       → Hujjatlangan, barqaror API

     ICHKI (_):
       • _vazifalar, _keyingi_id
       • _tekshir — ⭐ avlodlar KENGAYTIRISHI mumkin
       → "Ishlatsangiz o'z javobgarligingizda"

     XUSUSIY (__):
       • __indeks, __ozgarishlar
       • __indeksga_qosh, __indeksdan_ochir
       • Vazifa.__teg_normalla
       → Avlodlar TASODIFAN buza olmaydi

     HIMOYA:
       • teglar → frozenset (nusxa)
       • tarix → tuple (nusxa)
       • Holat.OTISHLAR → MappingProxyType
       • Holat.HAMMASI → frozenset
       • holat_ozgartir → o'tish qoidalari

     __all__ = ["VazifaRoyxati", "Vazifa", "Holat", "VazifaXatosi"]
       → from modul import * faqat shularni beradi
""")

Natijaning muhim qismi:

text
1. Ommaviy API:

  VazifaRoyxati('Loyiha', 3 vazifa)

    #1 Dizaynni tayyorlash   [tugallangan ] dizayn, muhim, uiux
    #2 Backend yozish        [jarayonda   ] backend, python ⏰
    #3 Testlarni yozish      [yangi       ] python, test

3. Holat o'tishlari:

    v1 (tugallangan) → yangi     ✅ VazifaXatosi: 'tugallangan' → 'yangi'
                                    mumkin emas
    v3 (yangi) → tugallangan     ✅ VazifaXatosi: 'yangi' → 'tugallangan'
                                    mumkin emas

4. ⭐ Kapsulatsiya darajalari:

  Kirish                           Daraja         Natija
  ──────────────────────────────────────────────────────────────
  r._vazifalar                     ichki (_)      ✅ 3
  r.__indeks                       xususiy (__)   ❌ AttributeError
  r._VazifaRoyxati__indeks         xususiy (__)   ✅ 6

6. Avlod sinf:

  ⭐ __indeks to'qnashuvi YO'Q:
    _UstuvorRoyxat__indeks
    _VazifaRoyxati__indeks

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


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

Noto'g'ri fikr To'g'risi
"Pythonda kapsulatsiya yo'q" Bor — kelishuv va @property orqali
"__ atributni yashiradi" Nomini o'zgartiradi (_Sinf__nom)
"__ — xavfsizlik vositasi" Faqat to'qnashuvdan himoya
"_ ga kirish taqiqlangan" Kelishuv, texnik cheklov yo'q
"__ har doim yaxshiroq" Avlodlarni ham to'sadi
"Getter/setter yozish kerak" Ochiq atribut → keyin @property
"__nom__ ham buziladi" Dunder buzilmaydi
"getattr(o, '__x') ishlaydi" Satrlar buzilmaydi

6. Keng tarqalgan xatolar va yechimlari

1. Getter/setter yozish

python
def get_x(self): return self._x         # ⚠️ Java uslubi
self.x = x                              # ✅ keyin @property

2. __ ni "xavfsizlik" uchun

python
self.__parol = p                        # ⚠️ o'zgaruvchi himoya emas
                                        # ✅ shifrlang, saqlamang

3. Ichki konteynerni qaytarish

python
@property
def teglar(self): return self._teglar           # ⚠️ o'zgartiriladi
def teglar(self): return frozenset(self._teglar) # ✅

4. __ avlodlar kerak bo'lganda

python
self.__holat = q                        # ⚠️ avlod kira olmaydi
def _holat_orn(self, q): ...            # ✅ himoyalangan API

5. getattr bilan xususiy atributga kirish

python
getattr(self, "__x")                    # ❌ buzilmaydi
getattr(self, "_Sinf__x")               # ✅ (lekin nega?)

6. __all__ unutish

python
# modul.py — ⚠️ import * hamma ommaviy nomni beradi
__all__ = ["Asosiy", "yordamchi"]       # ✅

7. Sinf konstantasi o'zgaruvchan

python
TURLAR = {"a": 1}                       # ⚠️ o'zgartiriladi
TURLAR = MappingProxyType({"a": 1})     # ✅

8. _ bilan boshlangan @property orqasidagi maydonni ochiq qoldirish

python
@property
def x(self): return self._x             # ⚠️ o._x = ... invariantni buzadi
__slots__ / __setattr__                 # ✅ to'liq himoya kerak bo'lsa

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 8.3-dars (o'tilgan): invariant va __init__
  • 8.6-dars: mo'rt asosiy sinf — __ bilan yechim
  • 8.15-dars: @property to'liq
  • 8.17-dars: dataclass(frozen=True)
  • 8.19-dars: __slots__
  • PEP 8: nomlash kelishuvlari
  • ruff/pylint: _ bilan boshlangan a'zolarga kirishni ogohlantiradi

8. Eng yaxshi amaliyotlar

  1. Ochiq atribut yozing, keyin @property qo'shing. Getter/setter — Java uslubi.

  2. _ — sukut tanlov. "Bu ichki tafsilot" degan belgi.

  3. __ faqat meros to'qnashuvi xavfi bo'lganda. Kutubxona, mixin, shablon metod.

  4. Ichki konteynerni nusxa qilib qaytaring. tuple(), frozenset(), MappingProxyType.

  5. Avlodlar uchun _ bilan API bering. __ bilan yopilgan holatga kirish yo'li.

  6. __all__ yozing. import * nima berishini nazorat qiling.

  7. Sinf konstantalari o'zgarmas bo'lsin. frozenset, tuple, MappingProxyType.

  8. Kapsulatsiya — hujjat. Nima ommaviy, nima ichki — aniq yozing.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
1.  class A:
        def __init__(self): self.__x = 1
    print(A().__dict__)
2.  class A:
        def __init__(self): self.__x = 1
    a = A()
    print(a._A__x)
3.  class A:
        def __init__(self): self.__x = 1
    a = A()
    try: a.__x
    except AttributeError: print("xato")
4.  class A:
        __x = 1
        __y__ = 2
        _z = 3
    print([k for k in A.__dict__ if "x" in k or "y" in k or "z" in k])
5.  class A:
        def __init__(self): self.__x = 1
        def f(self): return getattr(self, "__x", "yo'q")
    print(A().f())
6.  class Ota:
        def __init__(self): self.__x = "ota"
    class Bola(Ota):
        def __init__(self):
            super().__init__()
            self.__x = "bola"
    print(sorted(Bola().__dict__))
7.  class A:
        def f(self): return self.__g()
        def __g(self): return "A"
    class B(A):
        def __g(self): return "B"
    print(B().f())
8.  class A:
        def __init__(self): self._x = 1
    a = A()
    a._x = 99
    print(a._x)
9.  class A:
        __slots__ = ("x",)
    a = A()
    a.x = 1
    try: a.y = 2
    except AttributeError: print("xato")
10. from dataclasses import dataclass
    @dataclass(frozen=True)
    class A: x: int
    a = A(1)
    try: a.x = 2
    except Exception as e: print(type(e).__name__)
11. class A:
        def __init__(self): self.__x = 1
    print(hasattr(A(), "__x"), hasattr(A(), "_A__x"))
12. class A:
        ___x = 1
    print([k for k in A.__dict__ if "x" in k])
Javoblar
  1. {'_A__x': 1}
  2. 1
  3. xato
  4. ['_A__x', '__y__', '_z'] — dunder buzilmadi
  5. yo'q — getattr satri buzilmaydi
  6. ['_Bola__x', '_Ota__x'] — ikki alohida atribut
  7. A — _A__g chaqirildi
  8. 99 — _ hech narsani to'smaydi
  9. xato
  10. FrozenInstanceError
  11. False True
  12. ['_A___x'] — uch _ ham buziladi

Vazifa 2: Xatolarni tuzating

python
1.  class A:
        def get_x(self): return self._x
        def set_x(self, v): self._x = v
2.  @property
    def teglar(self): return self._teglar     # set
3.  class A:
        TURLAR = {"a": 1}
4.  class Asos:
        def __init__(self): self.__holat = 1
    # avlodlar holatni o'zgartirishi kerak
5.  class A:
        def f(self): return getattr(self, "__x")
6.  # modul.py
    class Asosiy: ...
    def _yordamchi(): ...
    # __all__ yo'q
7.  class A:
        def __init__(self, parol): self.__parol = parol
8.  class A:
        @property
        def x(self): return self._x
    # a._x = -100 invariantni buzadi
Javoblar
python
1.  self.x = x  (ochiq atribut), kerak bo'lsa @property
2.  return frozenset(self._teglar)
3.  TURLAR = MappingProxyType({"a": 1})
4.  def _holat_orn(self, q): self.__holat = q   qo'shing
5.  return self.__x  (yoki getattr(self, "_A__x"))
6.  __all__ = ["Asosiy"]
7.  Parolni saqlamang — xesh saqlang (bcrypt)
8.  __slots__ yoki __setattr__ bilan _x ni ham himoyalang

Vazifa 3: Parol sinfi

Yozing:

  1. Parol hech qachon ochiq saqlanmasin (faqat xesh)
  2. tekshir(parol) — solishtirish
  3. __repr__ va __str__ xeshni ham ko'rsatmasin
  4. kuchlilik — property (0-100)
  5. Xeshlash algoritmini almashtirish mumkin bo'lsin
  6. Serializatsiyada parol chiqmasin

Vazifa 4: Kapsulatsiya tekshiruvchisi

Vosita yozing:

  1. Sinfning ommaviy/ichki/xususiy a'zolarini ajratsin
  2. _ bilan boshlangan a'zolarga tashqi kirishni topsin
  3. Ichki konteynerni qaytaruvchi @property larni
  4. Getter/setter naqshini (Java uslubi)
  5. O'zgaruvchan sinf konstantalarini
  6. __all__ yo'qligini

Vazifa 5: Muzlatilgan konfiguratsiya

  1. Yaratilgandan keyin o'zgarmasin
  2. Ichki dict ham himoyalangan bo'lsin
  3. bilan(**kw) — yangi nusxa qaytarsin
  4. Sirlar repr da ko'rinmasin
  5. __slots__ bilan xotira tejalsin
  6. Barcha himoyalarni test qiling

Vazifa 6: Shablon metod + __

  1. Quvur asos sinfi — bajar() ommaviy
  2. Ichki qadamlar __ bilan
  3. Kengaytirish nuqtalari _ bilan
  4. Uch avlod sinf
  5. Avlod ichki qadamlarni buza olmasin
  6. Testlar bilan isbotlang

Vazifa 7: O'ylash

Nega Python private kalit so'zini qo'shmadi, garchi bu ko'p tillarda bo'lsa ham?

Javob

Chunki private ning foydasi kutilganidan kam, narxi esa yuqori — va uni chetlab o'tish har qanday tilda mumkin.

1. private haqiqatan himoya qiladimi?

Java:

java
class A {
    private int x = 42;
}

// Refleksiya bilan:
Field f = A.class.getDeclaredField("x");
f.setAccessible(true);          // ⚠️ private o'chirildi
f.get(obj);                     // 42

C++:

cpp
class A {
private:
    int x = 42;
};

A a;
int* p = (int*)&a;              // ⚠️ ko'rsatkich
*p;                             // 42

// Yoki:
#define private public          // ⚠️ ha, bu ishlaydi
#include "A.h"

C#:

csharp
typeof(A).GetField("x", BindingFlags.NonPublic | BindingFlags.Instance)
         .GetValue(obj);        // ⚠️

Xulosa: private — kompilyator ogohlantirishi, xavfsizlik chegarasi emas. U tasodifiy xatolardan himoya qiladi, ataylab kirishdan emas.

Python buni ochiq tan oladi: himoya yo'q, faqat kelishuv.

2. private ning haqiqiy narxi

a) Nosozlikni tuzatish qiyinlashadi

python
# Python — hamma narsa ko'rinadi
>>> obj.__dict__
{'_kesh': {...}, '_holat': 'jarayonda', '_urinishlar': 3}

Java da private maydonlarni debuggerdan tashqarida ko'rish uchun refleksiya kerak.

b) Test qilish qiyinlashadi

python
# Ichki mantiqni to'g'ridan-to'g'ri test qilish
def test_ichki_hisoblash():
    o = Hisoblagich()
    assert o._xom_hisob(5) == 10        # ✅ Pythonda oson

Java da: @VisibleForTesting annotatsiyasi yoki paketni bir xil qilish kerak.

c) Monkey patching mumkin bo'lmaydi

python
# Uchinchi tomon kutubxonasidagi xatoni vaqtincha tuzatish
import kutubxona
kutubxona.Sinf._buzilgan_metod = mening_tuzatilgan_versiyam

Bu — haqiqiy amaliyot. Kutubxona muallifi tuzatishni chiqarguncha ishlab turadi.

d) Kengaytirish cheklanadi

python
class MeningVersiyam(TashqiSinf):
    def kengaytir(self):
        return self._ichki_holat        # ⚠️ Java da: mumkin emas

3. Pythonning muqobil yechimi

Kelishuv + hujjat + linterlar:

python
class Kutubxona:
    def ommaviy(self):
        """Barqaror API. Semantik versiyalash amal qiladi."""

    def _ichki(self):
        """Ichki. Har versiyada o'zgarishi mumkin.

        ⚠️ Ishlatsangiz — o'z javobgarligingizda.
        """

Bu ishlaydi, chunki:

  1. _ — barcha Python dasturchilariga tanish belgi
  2. IDE avtoto'ldirishda yashiradi
  3. Linterlar ogohlantiradi (ruff, pylint W0212)
  4. from modul import * olmaydi
  5. Kod ko'rib chiqishda darhol ko'rinadi

4. Nom buzilishi — kompromis

__ — private emas, lekin haqiqiy muammoni hal qiladi:

python
class Kutubxona:
    def __init__(self):
        self.__holat = {}       # _Kutubxona__holat

class Foydalanuvchi(Kutubxona):
    def __init__(self):
        super().__init__()
        self.__holat = []       # _Foydalanuvchi__holat  ✅ to'qnashuv yo'q

Bu — merosdagi tasodifiy to'qnashuv muammosi, ruxsat nazorati emas.

Guido bu haqda:

"Nom buzilishi ruxsat nazorati uchun emas. U — sinf o'z ichki holatini avlodlarning tasodifiy aralashuvidan himoya qilishi uchun."

5. Boshqa tillar bilan solishtirish

Til Mexanizm Chetlab o'tish
Java private, protected Refleksiya
C++ private, friend Ko'rsatkich, #define
C# private, internal Refleksiya
Ruby private, protected send(:metod)
JavaScript #maydon (ES2022) Haqiqatan yopiq
Python _, __ To'g'ridan-to'g'ri
Go Katta/kichik harf Paket chegarasi

Go eng qiziq:

go
type A struct {
    Ommaviy int        // katta harf — eksport qilinadi
    ichki   int        // kichik harf — faqat paket ichida
}

Kalit so'z yo'q — nomlash hal qiladi. Python bilan bir xil g'oya, lekin paket darajasida majburlanadi.

JavaScript # — haqiqiy yopiqlik:

javascript
class A {
    #x = 42;                    // haqiqatan yopiq
    get() { return this.#x; }
}

const a = new A();
a.#x;                           // ❌ SyntaxError (kompilyatsiya paytida)
Object.keys(a);                 // []
JSON.stringify(a);              // {}

Lekin bu ham narx bilan keldi: debugging qiyinlashdi, mavjud vositalar buzildi, dekoratorlar bilan muammo.

6. "Consenting adults" falsafasi

Bu ibora Python jamoasida keng tarqalgan:

"We're all consenting adults here."

Ma'nosi: dasturchilar — kattalar, ular:

  • Hujjatni o'qiy oladi
  • _ belgisini tushunadi
  • Ichki API ni ishlatish oqibatini biladi
  • O'z tanlovi uchun javobgar

Bu — ishonch madaniyati, majburlash o'rniga.

Amaliy natija:

python
# Kutubxona muallifi:
class Kutubxona:
    def _ichki(self): ...       # "o'zgarishi mumkin"

# Foydalanuvchi:
k._ichki()                      # ⚠️ ongli qaror

# 2.0 versiyada:
# _ichki() o'chirildi → foydalanuvchi kodi buzildi
# → lekin u OGOHLANTIRILGAN edi

7. Amalda bu ishlaydimi?

Ha. Dalillar:

a) Katta kutubxonalar muvaffaqiyatli

requests, django, numpy, pandas — millionlab foydalanuvchi. _ kelishuvi ishlaydi.

b) _ ni buzish — kamdan-kam

Tajribali dasturchilar buni faqat zarurat bo'lganda qiladi va izoh qoldiradi:

python
# ⚠️ Ichki API — kutubxona #1234 xatosi tufayli
# https://github.com/lib/issues/1234 yopilgach olib tashlanadi
javob._xom_malumot

c) Vositalar yordam beradi

bash
ruff check --select SLF         # private a'zoga kirish
pylint --enable=W0212           # protected-access

8. Qachon Pythonda haqiqiy himoya kerak

Ba'zan kerak — va usullari bor:

python
# 1. Invariantni majburlash
@property
def yosh(self): return self._yosh
@yosh.setter
def yosh(self, q):
    if q < 0: raise ValueError(...)
    self._yosh = q

# 2. To'liq o'zgarmaslik
@dataclass(frozen=True, slots=True)
class Nuqta: x: float; y: float

# 3. Atributlarni cheklash
class A:
    __slots__ = ("x", "y")

# 4. Har yozishni nazorat qilish
def __setattr__(self, n, q):
    if self._muzlatilgan: raise AttributeError(...)
    object.__setattr__(self, n, q)

# 5. Konteynerlarni himoyalash
return MappingProxyType(self._d)
return frozenset(self._s)
return tuple(self._l)

Muhim: bularning hammasi invariant haqida, ruxsat nazorati haqida emas.

9. Xulosa

Python private ni qo'shmadi, chunki:

  1. U himoya bermaydi — har tilda chetlab o'tish mumkin
  2. Narxi bor — debugging, testlash, kengaytirish qiyinlashadi
  3. Kelishuv yetarli — _ amalda ishlaydi
  4. Falsafaga zid — "consenting adults"
  5. Muqobillari bor — @property, frozen, __slots__

Va __ (nom buzilishi) — private emas, balki boshqa muammoning (avlodda nom to'qnashuvi) yechimi.

Umumiy dars: Python "ruxsat nazorati" o'rniga invariant nazoratini tanladi. Muhimi — obyekt noto'g'ri holatga tushmasligi, kim qaysi maydonga tegishi emas.

Nimani mustahkamlaydi: 2.2, 2.3, 2.7-bo'limlar.


Xulosa

Bu darsda kapsulatsiya va Pythonning unga yondashuvini o'rgandik.

Eng muhim uch fikr:

  1. _ — kelishuv, __ — nom buzilishi; ikkalasi ham himoya emas. _nom "bu ichki tafsilot" degan belgi, hech narsani to'smaydi. __nom esa _SinfNomi__nom ga aylanadi — bu yashirish emas, avlod sinfda tasodifiy nom to'qnashuvining oldini olish. a._Sinf__nom orqali kirish har doim mumkin.

  2. Haqiqiy kapsulatsiya — invariant nazorati. @property bilan tekshirilgan kirish, __slots__ bilan atribut cheklash, frozen=True bilan o'zgarmaslik, frozenset/tuple/MappingProxyType bilan konteyner himoyasi. Muhimi — obyekt noto'g'ri holatga tushmasligi, kim qaysi maydonni ko'rishi emas.

  3. Getter/setter yozmang. Ochiq atribut bilan boshlang; nazorat kerak bo'lganda @property qo'shasiz va chaqiruvchi kod o'zgarmaydi. Bu — Pythonda private yo'qligining bevosita foydasi: interfeys barqaror qoladi, amalga oshirish erkin o'zgaradi.

Keyingi darsda dunder metodlarga o'tamiz: __str__ va __repr__ — obyektni matn sifatida ko'rsatishning ikki turli maqsadi.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
8.10-dars: Kapsulatsiya, va — IlmHamroh