IlmHamroh
Python kursi/OOP15/20-dars39 daqiqa
Mundarija (21)

8.15-dars: @property

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


1. Kirish va motivatsiya

Java dasturchi Pythonda shunday yozadi:

python
class Harorat:
    def __init__(self, c):
        self._c = c

    def get_selsiy(self):           # ⚠️
        return self._c

    def set_selsiy(self, q):        # ⚠️
        self._c = q

Pythonda esa — oddiy atribut:

python
class Harorat:
    def __init__(self, c):
        self.selsiy = c

Va keyin, tekshiruv kerak bo'lganda — chaqiruvchi kod o'zgarmaydi:

python
class Harorat:
    def __init__(self, c):
        self.selsiy = c             # ⭐ setter chaqiriladi

    @property
    def selsiy(self):
        return self._c

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


h.selsiy = 25                       # ✅ ikkalasida ham bir xil

Bu — "Uniform Access Principle": atributmi yoki hisoblanadimi — chaqiruvchi bilmasligi kerak.

Bu darsda:

  • @property — getter, setter, deleter
  • Deskriptor protokoli — property aslida qanday ishlaydi
  • Data va non-data deskriptorlar
  • functools.cached_property
  • Meros va property tuzoqlari
  • O'z deskriptorlaringizni yozish

2. Nazariya — chuqur tushuntirish

2.1. Uch qism

python
class A:
    def __init__(self, x):
        self._x = x

    @property
    def x(self):                    # ⭐ getter
        """Docstring shu yerda."""
        return self._x

    @x.setter
    def x(self, q):                 # ⭐ setter
        self._x = q

    @x.deleter
    def x(self):                    # ⭐ deleter
        del self._x

Nomlar bir xil bo'lishi shart:

python
@property
def x(self): ...

@x.setter                           # ⭐ x, property nomi
def x(self, q): ...                 # ⭐ x, bir xil nom

Faqat o'qish:

python
@property
def yuza(self):
    return self.a * self.b
# setter yo'q → yozishga urinish AttributeError

Eski sintaksis (hali ishlaydi):

python
class A:
    def _x_ol(self): return self._x
    def _x_orn(self, q): self._x = q
    def _x_ochir(self): del self._x

    x = property(_x_ol, _x_orn, _x_ochir, "Docstring")

2.2. Deskriptor protokoli

property — sehr emas, deskriptor:

python
class Deskriptor:
    def __get__(self, obj, ega): ...        # o'qish
    def __set__(self, obj, q): ...          # yozish
    def __delete__(self, obj): ...          # o'chirish
    def __set_name__(self, ega, nom): ...   # ⭐ 3.6+, nomni bilish

Ikki tur:

Tur Metodlar Ustuvorlik
Data deskriptor __set__ yoki __delete__ bor Nusxa lug'atidan yuqori
Non-data deskriptor Faqat __get__ Nusxa lug'atidan past

To'liq atribut qidiruv tartibi (obj.x):

1. type(obj).__mro__ da DATA deskriptor topilsa → __get__
2. obj.__dict__["x"]
3. type(obj).__mro__ da NON-DATA deskriptor → __get__
4. type(obj).__mro__ da oddiy qiymat
5. type(obj).__getattr__("x")
6. AttributeError

property — data deskriptor (__set__ bor), shuning uchun u nusxa lug'atini to'sadi.

Funksiya — non-data deskriptor (faqat __get__), shuning uchun obj.metod = fn uni almashtira oladi.

2.3. property ni qo'lda yozish

python
class MeningProperty:
    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget, self.fset, self.fdel = fget, fset, fdel
        self.__doc__ = doc or (fget.__doc__ if fget else None)

    def __set_name__(self, ega, nom):
        self._nom = nom

    def __get__(self, obj, ega=None):
        if obj is None:
            return self                     # ⭐ sinfdan kirish
        if self.fget is None:
            raise AttributeError(f"'{self._nom}' o'qib bo'lmaydi")
        return self.fget(obj)

    def __set__(self, obj, q):
        if self.fset is None:
            raise AttributeError(f"'{self._nom}' yozib bo'lmaydi")
        self.fset(obj, q)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError(f"'{self._nom}' o'chirib bo'lmaydi")
        self.fdel(obj)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)

obj is None — A.x yozilganda property obyektining o'zi qaytadi:

python
A.x                             # <property object at 0x...>
A.x.fget                        # <function A.x>
A().x                           # haqiqiy qiymat

2.4. cached_property

python
from functools import cached_property

class Hujjat:
    def __init__(self, matn): self.matn = matn

    @cached_property
    def sozlar(self):
        print("hisoblanmoqda...")
        return self.matn.split()

cached_property — NON-data deskriptor (faqat __get__):

1-chaqiruv:  __get__ → hisoblaydi → obj.__dict__["sozlar"] = natija
2-chaqiruv:  obj.__dict__ da bor → deskriptor CHAQIRILMAYDI

Farqlari:

property cached_property
Tur Data Non-data
Har chaqiruvda Hisoblanadi Bir marta
Yozish setter bilan obj.x = q bevosita
O'chirish deleter del obj.__dict__["x"]
__slots__ bilan (__dict__ kerak)
Asos o'zgarsa Yangilanadi Eskiradi

Keshni bekor qilish:

python
del h.__dict__["sozlar"]        # yoki
h.__dict__.pop("sozlar", None)

2.5. Meros va property

Getter'ni qayta aniqlash — setter yo'qoladi:

python
class Ota:
    @property
    def x(self): return self._x
    @x.setter
    def x(self, q): self._x = q


class Bola(Ota):
    @property                       # ⚠️ YANGI property
    def x(self): return self._x * 2
    # setter YO'Q!


Bola().x = 5                        # ❌ AttributeError

To'g'ri usullar:

python
# 1. Ota-sinf property'sidan setter
class Bola(Ota):
    @Ota.x.getter
    def x(self): return self._x * 2
    # setter meros olinadi ✅

# 2. Ikkalasini qayta yozish
class Bola(Ota):
    @property
    def x(self): return self._x * 2
    @x.setter
    def x(self, q): self._x = q

# 3. super() bilan
class Bola(Ota):
    @property
    def x(self): return super().x * 2
    @x.setter
    def x(self, q):
        Ota.x.fset(self, q)         # ⭐ ota-sinf setteri

super().x = q ishlamaydi — super() faqat o'qish uchun.

2.6. property tuzoqlari

1. Rekursiya:

python
@property
def x(self): return self.x          # ❌ cheksiz rekursiya
def x(self): return self._x         # ✅

@x.setter
def x(self, q): self.x = q          # ❌ cheksiz
def x(self, q): self._x = q         # ✅

2. __init__ da tartib:

python
def __init__(self, x):
    self.x = x                      # ⭐ setter chaqiriladi

@x.setter
def x(self, q):
    if q > self.maks:               # ⚠️ maks hali yo'q!
        ...
    self._x = q

Bog'liq atributlarni oldin o'rnating.

3. Yon ta'sirli property:

python
@property
def malumot(self):
    return tarmoqdan_yukla()        # ⚠️ obj.malumot sekin!

Metod qiling: obj.malumot_yukla().

4. Istisno tashlaydigan property:

python
@property
def x(self):
    return self._d["x"]             # ⚠️ KeyError kutilmagan
    return self._d.get("x")         # ✅

hasattr(obj, "x") property ichidagi har qanday istisnoda False qaytaradi (3.2 gacha; hozir faqat AttributeError).

5. Sinf darajasida:

python
A.x                                 # <property object>, qiymat emas
A.x.fget(obj)                       # ⭐ qo'lda chaqirish

2.7. Qachon property

To'g'ri:

python
# 1. Hisoblanadigan qiymat
@property
def yuza(self): return self.a * self.b

# 2. Tekshiruv
@yosh.setter
def yosh(self, q):
    if q < 0: raise ValueError(...)

# 3. Faqat o'qish
@property
def id(self): return self._id

# 4. Bog'liq qiymatlar
@farengeyt.setter
def farengeyt(self, q): self.selsiy = (q - 32) * 5/9

# 5. Ichki holatni himoyalash
@property
def elementlar(self): return tuple(self._el)

Ortiqcha:

python
# 1. Hech narsa qilmaydi
@property
def x(self): return self._x
@x.setter
def x(self, q): self._x = q         # ⚠️ oddiy atribut yetadi

# 2. Qimmat amal
@property
def hamma_qatorlar(self):
    return db.sorov("SELECT *")     # ⚠️ metod bo'lsin

# 3. Argument kerak
@property
def qidir(self, naqsh): ...         # ❌ mumkin emas

Mezon: atribut kabi tuyulsa (ot) — property; amal kabi (fe'l) — metod.


3. Tez ma'lumotnoma

Uch qism

python
@property
def x(self): return self._x         getter (+ docstring)

@x.setter
def x(self, q): self._x = q         setter

@x.deleter
def x(self): del self._x            deleter

setter yo'q → yozishda AttributeError

Deskriptor protokoli

python
__get__(self, obj, ega)     o'qish
__set__(self, obj, q)       yozish     → DATA deskriptor
__delete__(self, obj)       o'chirish  → DATA deskriptor
__set_name__(self, ega, n)  nom (3.6+)

Data deskriptor      → nusxa lug'atidan YUQORI
Non-data deskriptor  → nusxa lug'atidan PAST

Atribut qidiruv

1. data deskriptor (sinf zanjirida)
2. obj.__dict__
3. non-data deskriptor
4. sinf atributi
5. __getattr__
6. AttributeError

cached_property

python
@cached_property        non-data → __dict__ ga yoziladi
del obj.__dict__["x"]   keshni bekor qilish
⚠️ __slots__ bilan ishlamaydi

Meros

python
@Ota.x.getter           setter meros olinadi ✅
@property               ⚠️ YANGI property, setter yo'qoladi
Ota.x.fset(self, q)     ota-sinf setterini chaqirish

4. Batafsil misollar

Misol 1 — property asoslari

python
"""getter, setter, deleter va ular bilan bog'liq holatlar."""

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


class Harorat:
    """Fizik chegaralar bilan harorat."""

    MUTLAQ_NOL = -273.15

    def __init__(self, selsiy=0.0):
        self._selsiy = None
        self.selsiy = selsiy                    # ⭐ setter chaqiriladi

    @property
    def selsiy(self) -> float:
        """Selsiy bo'yicha harorat."""
        print(f"      [getter]")
        return self._selsiy

    @selsiy.setter
    def selsiy(self, q: float) -> None:
        print(f"      [setter: {q!r}]")
        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)

    @selsiy.deleter
    def selsiy(self) -> None:
        print(f"      [deleter]")
        self._selsiy = 0.0

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

    @farengeyt.setter
    def farengeyt(self, q: float) -> None:
        self.selsiy = (q - 32) * 5 / 9          # ⭐ selsiy setterini

    @property
    def kelvin(self) -> float:
        """⭐ Faqat o'qish."""
        return self._selsiy - self.MUTLAQ_NOL

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


print(f"  h = Harorat(25):")
h = Harorat(25)

print(f"\n  h.selsiy:")
q = h.selsiy
print(f"    → {q}")

print(f"\n  h.selsiy = 30:")
h.selsiy = 30

print(f"\n  del h.selsiy:")
del h.selsiy
print(f"    h = {h}")

h.selsiy = 25
print(f"\n  Hosila qiymatlar:")
print(f"    h.farengeyt = {h.farengeyt}")
print(f"    h.kelvin    = {h.kelvin}")

print(f"\n  h.farengeyt = 212:")
h.farengeyt = 212
print(f"    h = {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)),
    ("del h.kelvin",        lambda: delattr(h, "kelvin")),
]:
    try:
        f(); n = "⚠️ O'TDI"
    except (TypeError, ValueError, AttributeError) as e:
        n = f"✅ {type(e).__name__}: {str(e)[:40]}"
    print(f"    {kod:<22} {n}")


print("\n\n=== ⭐ 2. property — obyekt ===\n")

print(f"  Harorat.selsiy      = {Harorat.selsiy}")
print(f"  type(...)           = {type(Harorat.selsiy)}")
print(f"  .fget               = {Harorat.selsiy.fget}")
print(f"  .fset               = {Harorat.selsiy.fset}")
print(f"  .fdel               = {Harorat.selsiy.fdel}")
print(f"  .__doc__            = {Harorat.selsiy.__doc__!r}")

print(f"\n  Deskriptor metodlari:")
for m in ["__get__", "__set__", "__delete__", "__set_name__"]:
    bor = "✅" if hasattr(property, m) else "❌"
    print(f"    property.{m:<16} {bor}")

print(f"\n  ⭐ __set__ bor → DATA deskriptor")

print(f"\n  Qo'lda chaqirish:")
print(f"    Harorat.selsiy.fget(h) = ", end="")
print(f"{Harorat.selsiy.fget(h)}")
print(f"    Harorat.selsiy.__get__(h, Harorat) = ", end="")
print(f"{Harorat.selsiy.__get__(h, Harorat)}")


print("\n=== ⭐ 3. Data deskriptor nusxa lug'atini to'sadi ===\n")

print(f"  h.__dict__ = {h.__dict__}")
print(f"    ⭐ 'selsiy' YO'Q — u property")

h.__dict__["selsiy"] = 999                      # ⭐ to'g'ridan-to'g'ri
print(f"\n  h.__dict__['selsiy'] = 999")
print(f"    h.__dict__ = {h.__dict__}")
qiymat = h.selsiy                              # getter "[getter]" chop etadi
print(f"    h.selsiy   = {qiymat}   ⭐ property g'olib!")

del h.__dict__["selsiy"]

print(f"""
  ⭐ SABAB — atribut qidiruv tartibi:
     1. DATA deskriptor (property)    ← BIRINCHI
     2. obj.__dict__
     3. non-data deskriptor
     4. sinf atributi
""")


print("=== 4. Eski sintaksis ===\n")


class Eski:
    """property() funksiyasi bilan."""

    def __init__(self, x): self._x = x

    def _x_ol(self):
        return self._x

    def _x_orn(self, q):
        if q < 0:
            raise ValueError("Manfiy bo'lmasin")
        self._x = q

    def _x_ochir(self):
        self._x = 0

    x = property(_x_ol, _x_orn, _x_ochir, "X qiymati")


e = Eski(5)
print(f"  e.x = {e.x}")
e.x = 10
print(f"  e.x = 10 → {e.x}")
del e.x
print(f"  del e.x → {e.x}")
print(f"  Eski.x.__doc__ = {Eski.x.__doc__!r}")

try:
    e.x = -1
except ValueError as e2:
    print(f"  e.x = -1 → ✅ ValueError: {e2}")

print(f"""
  ⭐ @property — shunchaki sintaktik shakar:

     @property
     def x(self): ...      ≡  x = property(x)

     @x.setter
     def x(self, q): ...   ≡  x = x.setter(x)
""")


print("=== ⚠️ 5. Rekursiya tuzoqlari ===\n")

print("""  ❌ getter da:

     @property
     def x(self):
         return self.x            # ❌ o'zini chaqiradi

     ✅ return self._x


  ❌ setter da:

     @x.setter
     def x(self, q):
         self.x = q               # ❌ o'zini chaqiradi

     ✅ self._x = q


  ⚠️ __init__ da tartib:

     def __init__(self, x):
         self.x = x               # setter chaqiriladi
         self.maks = 100          # ⚠️ kech!

     @x.setter
     def x(self, q):
         if q > self.maks: ...    # ❌ AttributeError

     ✅ def __init__(self, x):
            self.maks = 100       # ⭐ OLDIN
            self.x = x
""")


class Notogri:
    def __init__(self, x):
        self.x = x
        self.maks = 100

    @property
    def x(self): return self._x

    @x.setter
    def x(self, q):
        if q > self.maks:
            raise ValueError("Katta")
        self._x = q


class Togri:
    def __init__(self, x, maks=100):
        self.maks = maks                        # ⭐ OLDIN
        self.x = x

    @property
    def x(self): return self._x

    @x.setter
    def x(self, q):
        if q > self.maks:
            raise ValueError(f"Katta: {q} > {self.maks}")
        self._x = q


for Sinf in (Notogri, Togri):
    try:
        o = Sinf(50)
        n = f"✅ x={o.x}"
    except AttributeError as e:
        n = f"❌ AttributeError: {e}"
    print(f"  {Sinf.__name__}(50) → {n}")

try:
    Togri(200)
except ValueError as e:
    print(f"  Togri(200) → ✅ ValueError: {e}")


print("\n=== 6. Faqat o'qish naqshlari ===\n")


class Nuqta:
    """Ichki holat to'liq himoyalangan."""

    __slots__ = ("_x", "_y")

    def __init__(self, x, y):
        object.__setattr__(self, "_x", float(x))
        object.__setattr__(self, "_y", float(y))

    @property
    def x(self): return self._x

    @property
    def y(self): return self._y

    @property
    def masofa(self):
        return (self._x ** 2 + self._y ** 2) ** 0.5

    def kochir(self, dx, dy):
        """⭐ Yangi obyekt."""
        return Nuqta(self._x + dx, self._y + dy)

    def __repr__(self):
        return f"Nuqta({self._x}, {self._y})"


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._x = 10",       lambda: setattr(n, "_x", 10)),
    ("n.masofa = 0",    lambda: setattr(n, "masofa", 0)),
    ("n.z = 1",         lambda: setattr(n, "z", 1)),
]:
    try:
        f(); msg = "⚠️ O'TDI"
    except AttributeError as e:
        msg = f"✅ AttributeError"
    print(f"    {kod:<16} {msg}")

print(f"""
  ⭐ IZOH:
     n.x = 10      → property da setter yo'q      ✅ to'sildi
     n.masofa = 0  → property da setter yo'q      ✅ to'sildi
     n.z = 1       → __slots__ da 'z' yo'q        ✅ to'sildi
     n._x = 10     → ⚠️ _x SLOT da — O'TADI!

  ⚠️ __slots__ mavjud slotga yozishni TO'SMAYDI.
     U faqat YANGI atribut qo'shishni to'sadi.
""")

print(f"    n._x = 10 →", end=" ")
n._x = 10.0
print(f"n.x = {n.x}   ⚠️ o'zgardi")
print(f"    ⭐ To'liq himoya uchun __setattr__ kerak:")
print(f"""
       def __setattr__(self, nom, q):
           raise AttributeError(f"O'zgarmas: {{nom}}")
""")

Natijaning muhim qismi:

text
=== 1. Uch qism ===

  h = Harorat(25):
      [setter: 25]

  h.selsiy:
      [getter]
    → 25.0

  Himoya:
    h.selsiy = -300        ✅ ValueError: Mutlaq noldan past: -300
    h.selsiy = True        ✅ TypeError: Son bo'lsin: bool
    h.kelvin = 0           ✅ AttributeError: property 'kelvin' of ...

=== ⭐ 3. Data deskriptor nusxa lug'atini to'sadi ===

  h.__dict__['selsiy'] = 999
    h.__dict__ = {'_selsiy': 100.0, 'selsiy': 999}
    h.selsiy   = 100.0   ⭐ property g'olib!

=== ⚠️ 5. Rekursiya tuzoqlari ===

  Notogri(50) → ❌ AttributeError: 'Notogri' object has no attribute 'maks'
  Togri(50) → ✅ x=50
  Togri(200) → ✅ ValueError: Katta: 200 > 100

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

Misol 2 — Deskriptor protokoli

python
"""property aslida qanday ishlaydi."""

print("=== 1. Data va non-data deskriptorlar ===\n")


class NonData:
    """Faqat __get__ — nusxa lug'atidan PAST."""

    def __init__(self, nom): self.nom = nom

    def __get__(self, obj, ega=None):
        if obj is None: return self
        print(f"      NonData.__get__({self.nom})")
        return f"non-data:{self.nom}"


class Data:
    """__set__ ham bor — nusxa lug'atidan YUQORI."""

    def __init__(self, nom): self.nom = nom

    def __get__(self, obj, ega=None):
        if obj is None: return self
        print(f"      Data.__get__({self.nom})")
        return f"data:{self.nom}"

    def __set__(self, obj, q):
        print(f"      Data.__set__({self.nom}, {q!r})")
        obj.__dict__[f"_{self.nom}"] = q


class Sinov:
    nd = NonData("nd")
    d = Data("d")


s = Sinov()

print(f"  Boshida: s.__dict__ = {s.__dict__}\n")

print(f"  s.nd →")
print(f"    {s.nd!r}")
print(f"  s.d →")
print(f"    {s.d!r}\n")

s.__dict__["nd"] = "NUSXADAN"
s.__dict__["d"] = "NUSXADAN"
print(f"  s.__dict__ ga to'g'ridan-to'g'ri yozdik:")
print(f"    {s.__dict__}\n")

print(f"  s.nd →")
print(f"    {s.nd!r}   ⭐ NUSXA g'olib (non-data)")
print(f"  s.d →")
print(f"    {s.d!r}   ⭐ DESKRIPTOR g'olib (data)")

print(f"""
  ⭐ ATRIBUT QIDIRUV TARTIBI (obj.x):

     1. type(obj).__mro__ da DATA deskriptor    ← eng yuqori
     2. obj.__dict__
     3. type(obj).__mro__ da NON-DATA deskriptor
     4. type(obj).__mro__ da oddiy qiymat
     5. type(obj).__getattr__("x")
     6. AttributeError
""")


print("=== ⭐ 2. Funksiya — non-data deskriptor ===\n")


def erkin_funksiya(self):
    return "erkin"


class A:
    def metod(self):
        return "metod"


a = A()

print(f"  Funksiyada deskriptor metodlari:")
for m in ["__get__", "__set__", "__delete__"]:
    bor = "✅" if hasattr(erkin_funksiya, m) else "❌"
    print(f"    {m:<14} {bor}")

print(f"\n  ⭐ Faqat __get__ → NON-DATA deskriptor")
print(f"\n  a.metod() = {a.metod()!r}")

a.__dict__["metod"] = lambda: "NUSXADAN"
print(f"\n  a.__dict__['metod'] = lambda: 'NUSXADAN'")
print(f"    a.metod() = {a.metod()!r}   ⭐ nusxa g'olib")

print(f"""
  ⭐ ANIQ SHU SABAB metodni nusxada almashtirish mumkin:

     obj.metod = mening_versiyam       ✅ ishlaydi

  ⚠️ property bilan mumkin emas (data deskriptor):

     obj.x = q                         → property.__set__ chaqiriladi
""")


print("=== 3. property ni qo'lda yozish ===\n")


class MeningProperty:
    """property ning sodda amalga oshirilishi."""

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget, self.fset, self.fdel = fget, fset, fdel
        self.__doc__ = doc or (fget.__doc__ if fget else None)
        self._nom = fget.__name__ if fget else "?"

    def __set_name__(self, ega, nom):
        """⭐ Python 3.6+ — sinf yaratilganda chaqiriladi."""
        self._nom = nom
        self._ega = ega.__name__

    def __get__(self, obj, ega=None):
        if obj is None:
            return self                             # ⭐ sinfdan
        if self.fget is None:
            raise AttributeError(
                f"property '{self._nom}' o'qib bo'lmaydi"
            )
        return self.fget(obj)

    def __set__(self, obj, q):
        if self.fset is None:
            raise AttributeError(
                f"property '{self._nom}' yozib bo'lmaydi"
            )
        self.fset(obj, q)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError(
                f"property '{self._nom}' o'chirib bo'lmaydi"
            )
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)


class Sinov2:
    def __init__(self, x): self._x = x

    @MeningProperty
    def x(self):
        """X qiymati."""
        return self._x

    @x.setter
    def x(self, q):
        if q < 0:
            raise ValueError("Manfiy bo'lmasin")
        self._x = q

    @MeningProperty
    def faqat_oqish(self):
        return self._x * 2


s2 = Sinov2(5)

print(f"  s2.x                = {s2.x}")
s2.x = 10
print(f"  s2.x = 10           → {s2.x}")
print(f"  s2.faqat_oqish      = {s2.faqat_oqish}")
print(f"  Sinov2.x.__doc__    = {Sinov2.x.__doc__!r}")
print(f"  type(Sinov2.x)      = {type(Sinov2.x).__name__}")

print(f"\n  Xatolar:")
for kod, f in [
    ("s2.x = -1",           lambda: setattr(s2, "x", -1)),
    ("s2.faqat_oqish = 1",  lambda: setattr(s2, "faqat_oqish", 1)),
    ("del s2.x",            lambda: delattr(s2, "x")),
]:
    try:
        f(); n = "⚠️"
    except (ValueError, AttributeError) as e:
        n = f"✅ {type(e).__name__}: {e}"
    print(f"    {kod:<22} {n}")


print("\n=== ⭐ 4. __set_name__ ===\n")


class Tipli:
    """⭐ Nomni AVTOMATIK biladi (3.6+)."""

    def __init__(self, tur, sukut=None):
        self.tur = tur
        self.sukut = sukut

    def __set_name__(self, ega, nom):
        """Sinf yaratilganda chaqiriladi."""
        self.nom = nom
        self.xususiy = f"_{nom}"
        print(f"      __set_name__({ega.__name__}, {nom!r})")

    def __get__(self, obj, ega=None):
        if obj is None: return self
        return getattr(obj, self.xususiy, self.sukut)

    def __set__(self, obj, q):
        if not isinstance(q, self.tur):
            raise TypeError(
                f"{self.nom}: {self.tur.__name__} kutildi, "
                f"{type(q).__name__} keldi"
            )
        setattr(obj, self.xususiy, q)


print(f"  Sinf yaratilmoqda:")


class Foydalanuvchi:
    ism = Tipli(str, "")
    yosh = Tipli(int, 0)
    faol = Tipli(bool, True)

    def __init__(self, ism, yosh):
        self.ism = ism
        self.yosh = yosh


print(f"\n  f = Foydalanuvchi('Aziz', 30):")
f = Foydalanuvchi("Aziz", 30)
print(f"    f.ism  = {f.ism!r}")
print(f"    f.yosh = {f.yosh}")
print(f"    f.faol = {f.faol}")
print(f"    f.__dict__ = {f.__dict__}")

print(f"\n  Tur tekshiruvi:")
for kod, f2 in [
    ("f.yosh = 'o`ttiz'",   lambda: setattr(f, "yosh", "o'ttiz")),
    ("f.ism = 42",          lambda: setattr(f, "ism", 42)),
    ("f.faol = 1",          lambda: setattr(f, "faol", 1)),
    ("f.yosh = 31",         lambda: setattr(f, "yosh", 31)),
]:
    try:
        f2(); n = "✅ o'tdi"
    except TypeError as e:
        n = f"✅ TypeError: {e}"
    print(f"    {kod:<22} {n}")

print(f"""
  ⭐ __set_name__ SIZ har deskriptorga nomni qo'lda
     berish kerak edi:

       ism = Tipli(str, nom="ism")     ⚠️ takrorlanish

  3.6+ da AVTOMATIK.
""")


print("=== 5. cached_property ===\n")

from functools import cached_property
import time


class Hujjat:
    def __init__(self, matn):
        self.matn = matn

    @property
    def sozlar_property(self):
        print(f"      [property hisoblanmoqda]")
        time.sleep(0.01)
        return self.matn.split()

    @cached_property
    def sozlar_cached(self):
        print(f"      [cached hisoblanmoqda]")
        time.sleep(0.01)
        return self.matn.split()


h = Hujjat("bir ikki uch")

print(f"  @property (3 marta):")
for _ in range(3):
    h.sozlar_property

print(f"\n  @cached_property (3 marta):")
for _ in range(3):
    h.sozlar_cached

print(f"\n  h.__dict__ = {h.__dict__}")
print(f"    ⭐ 'sozlar_cached' NUSXA lug'atida")

print(f"\n  Deskriptor turlari:")
for nom in ["sozlar_property", "sozlar_cached"]:
    d = Hujjat.__dict__[nom]
    tur = "DATA" if hasattr(type(d), "__set__") else "NON-DATA"
    print(f"    {nom:<20} {type(d).__name__:<18} {tur}")

print(f"\n  Keshni bekor qilish:")
del h.__dict__["sozlar_cached"]
print(f"    del h.__dict__['sozlar_cached']")
h.sozlar_cached
print(f"    ✅ qayta hisoblandi")

print(f"\n  ⚠️ Asos o'zgarsa:")
h.matn = "butunlay boshqa matn"
print(f"    h.matn o'zgardi")
print(f"    h.sozlar_cached = {h.sozlar_cached}")
print(f"    ⚠️ Eski qiymat! (kesh bekor qilinmadi)")

print(f"\n  ⚠️ __slots__ bilan:")


class Slotli:
    __slots__ = ("matn",)

    def __init__(self, m): self.matn = m

    @cached_property
    def sozlar(self): return self.matn.split()


try:
    Slotli("a b").sozlar
except TypeError as e:
    print(f"    ❌ TypeError: {str(e)[:60]}")
    print(f"    ⭐ cached_property __dict__ ga yozadi, slots da yo'q")

Natijaning muhim qismi:

text
=== 1. Data va non-data deskriptorlar ===

  s.nd →
    'NUSXADAN'   ⭐ NUSXA g'olib (non-data)
  s.d →
      Data.__get__(d)
    'data:d'   ⭐ DESKRIPTOR g'olib (data)

=== ⭐ 2. Funksiya — non-data deskriptor ===

    __get__        ✅
    __set__        ❌
  a.__dict__['metod'] = lambda: 'NUSXADAN'
    a.metod() = 'NUSXADAN'   ⭐ nusxa g'olib

=== ⭐ 4. __set_name__ ===

  Sinf yaratilmoqda:
      __set_name__(Foydalanuvchi, 'ism')
      __set_name__(Foydalanuvchi, 'yosh')

    f.yosh = 'o`ttiz'      ✅ TypeError: yosh: int kutildi, str keldi
    f.faol = 1             ✅ TypeError: faol: bool kutildi, int keldi

=== 5. cached_property ===

  @property (3 marta):
      [property hisoblanmoqda]
      [property hisoblanmoqda]
      [property hisoblanmoqda]

  @cached_property (3 marta):
      [cached hisoblanmoqda]

    sozlar_property      property           DATA
    sozlar_cached        cached_property    NON-DATA

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

Misol 3 — Meros va tuzoqlar

python
"""property meros va keng tarqalgan xatolar."""

print("=== ⚠️ 1. Getter qayta aniqlash — setter yo'qoladi ===\n")


class Ota:
    def __init__(self, x=0):
        self._x = x

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, q):
        if q < 0:
            raise ValueError("Manfiy bo'lmasin")
        self._x = q


class BolaYomon(Ota):
    """⚠️ setter yo'qoladi."""

    @property
    def x(self):
        return self._x * 2


class BolaGetter(Ota):
    """✅ @Ota.x.getter — setter meros olinadi."""

    @Ota.x.getter
    def x(self):
        return self._x * 2


class BolaToliq(Ota):
    """✅ Ikkalasini qayta yozish."""

    @property
    def x(self):
        return self._x * 2

    @x.setter
    def x(self, q):
        if q < 0:
            raise ValueError("Manfiy bo'lmasin")
        self._x = q


class BolaSuper(Ota):
    """✅ super() bilan."""

    @property
    def x(self):
        return super().x * 2                # ⭐ ota-sinf getteri

    @x.setter
    def x(self, q):
        Ota.x.fset(self, q)                 # ⭐ ota-sinf setteri


SINFLAR = [Ota, BolaYomon, BolaGetter, BolaToliq, BolaSuper]

print(f"  {'Sinf':<14} {'o.x (x=5)':<12} {'o.x = 10':<28} {'setter'}")
print("  " + "─" * 68)
for S in SINFLAR:
    o = S()
    o._x = 5
    oqish = o.x
    fset = "✅" if S.x.fset else "❌"
    try:
        o.x = 10
        yozish = f"✅ → {o.x}"
    except AttributeError as e:
        yozish = f"❌ AttributeError"
    print(f"  {S.__name__:<14} {oqish:<12} {yozish:<28} {fset}")

print(f"""
  ⭐ SABAB: @property yangi property obyekti yaratadi.
     Ota-sinfniki bilan aloqasi yo'q.

     @Ota.x.getter — MAVJUD property dan nusxa oladi
     va faqat fget ni almashtiradi.
""")


print("=== ⚠️ 2. super() bilan yozish ===\n")


class YomonSuper(Ota):
    @property
    def x(self): return super().x

    @x.setter
    def x(self, q):
        super().x = q                       # ❌ ishlamaydi


y = YomonSuper()
try:
    y.x = 5
except AttributeError as e:
    print(f"  super().x = q → ❌ AttributeError: {e}")

print(f"""
  ⭐ super() faqat O'QISH uchun proxy.
     Yozish uchun:

     Ota.x.fset(self, q)          ✅ aniq
     super().__class__.x.fset(self, q)   ⚠️ murakkab
""")


print("=== ⚠️ 3. Rekursiya ===\n")


class Rekursiv1:
    @property
    def x(self):
        return self.x                       # ❌ o'zini


class Rekursiv2:
    def __init__(self): self._x = 0

    @property
    def x(self): return self._x

    @x.setter
    def x(self, q):
        self.x = q                          # ❌ o'zini


for Sinf, kod in [(Rekursiv1, "o.x"), (Rekursiv2, "o.x = 1")]:
    o = Sinf()
    try:
        exec(kod, {"o": o})
        n = "⚠️"
    except RecursionError:
        n = "❌ RecursionError"
    print(f"  {Sinf.__name__:<14} {kod:<12} {n}")

print(f"""
  ✅ TO'G'RI:
     getter: return self._x       (pastki chiziq bilan)
     setter: self._x = q
""")


print("=== ⚠️ 4. hasattr va istisnolar ===\n")


class Yomon:
    @property
    def x(self):
        raise ValueError("Ichki xato")

    @property
    def y(self):
        raise AttributeError("Yo'q")

    @property
    def z(self):
        return self._mavjud_emas               # AttributeError


o = Yomon()

print(f"  {'Atribut':<10} {'hasattr':<12} {'getattr xatosi'}")
print("  " + "─" * 46)
for nom in "xyz":
    try:
        h = hasattr(o, nom)
    except Exception as e:
        h = f"⚠️ {type(e).__name__}"          # ⭐ hasattr O'ZI yiqildi
    try:
        getattr(o, nom)
        xato = "—"
    except Exception as e:
        xato = f"{type(e).__name__}: {e}"
    print(f"  {nom:<10} {h!s:<14} {xato}")

print(f"""
  ⭐ hasattr FAQAT AttributeError ni ushlaydi (3.2+).
     Boshqa istisnolar o'tib ketadi.

  ⚠️ z uchun hasattr False — lekin sabab
     'z yo'q' emas, '_mavjud_emas yo'q'.
     Bu — CHALKASH nosozlik.

  ✅ property ichida istisno tashlamang:
     return self._d.get("x")      ✅
     return self._d["x"]          ⚠️ KeyError
""")


print("=== ⚠️ 5. Yon ta'sirli property ===\n")

import time


class Yomon2:
    """⚠️ property sekin va yon ta'sirli."""

    def __init__(self):
        self._sorovlar = 0

    @property
    def malumot(self):
        self._sorovlar += 1                 # ⚠️ yon ta'sir
        time.sleep(0.01)                    # ⚠️ sekin
        return [1, 2, 3]


class Yaxshi:
    """✅ Metod — qimmat ekani ko'rinadi."""

    def __init__(self):
        self._sorovlar = 0

    def malumot_ol(self):
        self._sorovlar += 1
        time.sleep(0.01)
        return [1, 2, 3]

    @property
    def sorovlar(self):
        """✅ Arzon, yon ta'sirsiz."""
        return self._sorovlar


y2 = Yomon2()

print(f"  Nosozlikni tuzatishda:")
print(f"    >>> y2.malumot        # ⚠️ 10ms + hisoblagich oshdi")
_ = y2.malumot
print(f"    >>> y2.malumot        # ⚠️ yana")
_ = y2.malumot
print(f"    y2._sorovlar = {y2._sorovlar}   ⚠️ debugger ham oshirdi")

print(f"""
  ⚠️ MUAMMOLAR:
     1. Debugger obyektni ko'rsatganda property chaqiriladi
     2. repr() da ishlatilsa — sekin
     3. Yon ta'sir kutilmagan

  ⭐ MEZON:
     Arzon + yon ta'sirsiz  → property
     Qimmat yoki yon ta'sir → metod ()
""")


print("=== 6. property vs metod — qaror ===\n")

print("""
  ┌──────────────────────────────┬──────────────────────┐
  │ Holat                        │ Tanlov               │
  ├──────────────────────────────┼──────────────────────┤
  │ Arzon hisoblash              │ ✅ @property         │
  │ Qimmat, lekin asos o'zgarmas │ ✅ @cached_property  │
  │ Qimmat, asos o'zgaradi       │ ⚠️ metod ()          │
  │ Yon ta'siri bor              │ ⚠️ metod ()          │
  │ Argument oladi               │ ❌ metod (majburiy)  │
  │ Istisno tashlashi mumkin     │ ⚠️ metod ()          │
  │ Ot kabi tuyuladi (yuza)      │ ✅ @property         │
  │ Fe'l kabi (saqla, hisobla)   │ ✅ metod ()          │
  │ Tekshiruv kerak              │ ✅ @property setter  │
  │ Hech narsa qilmaydi          │ ❌ oddiy atribut     │
  └──────────────────────────────┴──────────────────────┘

  ⭐ STANDART KUTUBXONADAN MISOLLAR:

     @property:
       str.__len__ emas, lekin:
       Path.parent, Path.name, Path.suffix
       datetime.year, .month, .day
       complex.real, .imag
       range.start, .stop, .step

     metod:
       str.upper(), list.sort()
       Path.exists(), Path.read_text()     ⭐ IO — metod
       dict.keys()                          ⭐ ko'rinish yaratadi
""")

from pathlib import Path
from datetime import datetime

p = Path("/home/user/hujjat.txt")
print(f"  Path misollari:")
for nom in ["parent", "name", "stem", "suffix", "parts"]:
    print(f"    p.{nom:<10} = {getattr(p, nom)!r}")

print(f"\n  ⭐ Bularning hammasi @property — arzon va yon ta'sirsiz")
print(f"     p.exists() — metod, chunki DISKKA murojaat qiladi")


print("\n=== 7. property ni dinamik yaratish ===\n")


def maydon(nom, tur, sukut=None, tekshir=None):
    """⭐ property fabrikasi."""
    xususiy = f"_{nom}"

    def getter(self):
        return getattr(self, xususiy, sukut)

    def setter(self, q):
        if not isinstance(q, tur):
            raise TypeError(
                f"{nom}: {tur.__name__} kutildi, {type(q).__name__}"
            )
        if tekshir and not tekshir(q):
            raise ValueError(f"{nom}: noto'g'ri qiymat {q!r}")
        setattr(self, xususiy, q)

    return property(getter, setter, doc=f"{nom} ({tur.__name__})")


class Sozlama:
    host = maydon("host", str, "localhost", lambda q: bool(q.strip()))
    port = maydon("port", int, 8000, lambda q: 1 <= q <= 65535)
    debug = maydon("debug", bool, False)

    def __init__(self, **kw):
        for k, v in kw.items():
            setattr(self, k, v)

    def __repr__(self):
        return (f"Sozlama(host={self.host!r}, port={self.port}, "
                f"debug={self.debug})")


s = Sozlama(host="wisar.uz", port=443)
print(f"  {s}")
print(f"  Sozlama.port.__doc__ = {Sozlama.port.__doc__!r}")

print(f"\n  Tekshiruvlar:")
for kod, f in [
    ("s.port = 70000",  lambda: setattr(s, "port", 70000)),
    ("s.port = '80'",   lambda: setattr(s, "port", "80")),
    ("s.host = ''",     lambda: setattr(s, "host", "")),
    ("s.debug = True",  lambda: setattr(s, "debug", True)),
]:
    try:
        f(); n = "✅ o'tdi"
    except (TypeError, ValueError) as e:
        n = f"✅ {type(e).__name__}: {e}"
    print(f"    {kod:<20} {n}")

print(f"""
  ⚠️ Bu naqsh ishlaydi, lekin DESKRIPTOR SINFI
     (misol 2 dagi Tipli) toza yechim:
       • __set_name__ nomni avtomatik oladi
       • Kod qayta ishlatiladi
       • mypy yaxshiroq tushunadi
""")

Natijaning muhim qismi:

text
=== ⚠️ 1. Getter qayta aniqlash — setter yo'qoladi ===

  Sinf           o.x (x=5)    o.x = 10                     setter
  ────────────────────────────────────────────────────────────────
  Ota            5            ✅ → 10                       ✅
  BolaYomon      10           ❌ AttributeError             ❌
  BolaGetter     10           ✅ → 20                       ✅
  BolaToliq      10           ✅ → 20                       ✅

=== ⚠️ 4. hasattr va istisnolar ===

  Atribut    hasattr      getattr xatosi
  ──────────────────────────────────────────────
  x          ⚠️ ValueError  ValueError: Ichki xato
  y          False        AttributeError: Yo'q
  z          False          AttributeError: 'Yomon' object has no attribute '_mavjud_emas'

=== 6. property vs metod — qaror ===

  Path misollari:
    p.parent     = PosixPath('/home/user')
    p.name       = 'hujjat.txt'
    p.suffix     = '.txt'

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

Misol 4 — Amaliy: validatsiya tizimi

python
"""Deskriptorlar bilan model maydonlari."""

from __future__ import annotations

import re
from datetime import date
from typing import Any, Callable

print("=== MODEL MAYDONLARI ===\n")


class Maydon:
    """Asos deskriptor — tekshiruv bilan.

    ⭐ __set_name__ tufayli nomni avtomatik biladi.
    """

    def __init__(self, *, sukut=None, majburiy: bool = False,
                 faqat_oqish: bool = False, doc: str = ""):
        self.sukut = sukut
        self.majburiy = majburiy
        self.faqat_oqish = faqat_oqish
        self.__doc__ = doc

    def __set_name__(self, ega, nom):
        self.nom = nom
        self.xususiy = f"_maydon_{nom}"
        self.ega = ega.__name__

    def __get__(self, obj, ega=None):
        if obj is None:
            return self
        if not hasattr(obj, self.xususiy):
            if self.majburiy:
                raise AttributeError(
                    f"{self.ega}.{self.nom} o'rnatilmagan (majburiy)"
                )
            return self.sukut
        return getattr(obj, self.xususiy)

    def __set__(self, obj, q):
        if self.faqat_oqish and hasattr(obj, self.xususiy):
            raise AttributeError(
                f"{self.ega}.{self.nom} faqat o'qish uchun"
            )
        q = self.tekshir(obj, q)
        setattr(obj, self.xususiy, q)

    def __delete__(self, obj):
        if self.majburiy:
            raise AttributeError(
                f"{self.ega}.{self.nom} majburiy — o'chirib bo'lmaydi"
            )
        if hasattr(obj, self.xususiy):
            delattr(obj, self.xususiy)

    def tekshir(self, obj, q):
        """⭐ Avlodlar qayta aniqlaydi."""
        return q

    def _xato(self, sabab: str) -> ValueError:
        return ValueError(f"{self.ega}.{self.nom}: {sabab}")


class Satr(Maydon):
    def __init__(self, *, eng_kam: int = 0, eng_kop: int | None = None,
                 naqsh: str = "", tozala: bool = True, **kw):
        super().__init__(**kw)
        self.eng_kam, self.eng_kop = eng_kam, eng_kop
        self.naqsh = re.compile(naqsh) if naqsh else None
        self.tozala = tozala

    def tekshir(self, obj, q):
        if not isinstance(q, str):
            raise TypeError(
                f"{self.ega}.{self.nom}: satr kutildi, "
                f"{type(q).__name__} keldi"
            )
        if self.tozala:
            q = q.strip()
        if len(q) < self.eng_kam:
            raise self._xato(f"kamida {self.eng_kam} belgi ({len(q)} keldi)")
        if self.eng_kop and len(q) > self.eng_kop:
            raise self._xato(f"ko'pi bilan {self.eng_kop} belgi")
        if self.naqsh and not self.naqsh.fullmatch(q):
            raise self._xato(f"naqshga mos emas: {q!r}")
        return q


class Butun(Maydon):
    def __init__(self, *, eng_kam: int | None = None,
                 eng_kop: int | None = None, **kw):
        super().__init__(**kw)
        self.eng_kam, self.eng_kop = eng_kam, eng_kop

    def tekshir(self, obj, q):
        if isinstance(q, bool) or not isinstance(q, int):
            raise TypeError(
                f"{self.ega}.{self.nom}: butun son kutildi, "
                f"{type(q).__name__} keldi"
            )
        if self.eng_kam is not None and q < self.eng_kam:
            raise self._xato(f">= {self.eng_kam} bo'lsin ({q} keldi)")
        if self.eng_kop is not None and q > self.eng_kop:
            raise self._xato(f"<= {self.eng_kop} bo'lsin ({q} keldi)")
        return q


class Tanlov(Maydon):
    def __init__(self, variantlar, **kw):
        super().__init__(**kw)
        self.variantlar = frozenset(variantlar)

    def tekshir(self, obj, q):
        if q not in self.variantlar:
            raise self._xato(
                f"{sorted(self.variantlar)} dan biri bo'lsin, "
                f"{q!r} keldi"
            )
        return q


class Sana(Maydon):
    def __init__(self, *, kelajak: bool = True, otmish: bool = True, **kw):
        super().__init__(**kw)
        self.kelajak, self.otmish = kelajak, otmish

    def tekshir(self, obj, q):
        if isinstance(q, str):
            try:
                q = date.fromisoformat(q)
            except ValueError as e:
                raise self._xato(f"sana formati YYYY-MM-DD: {q!r}") from e
        if not isinstance(q, date):
            raise TypeError(
                f"{self.ega}.{self.nom}: date yoki satr kutildi"
            )
        bugun = date.today()
        if not self.kelajak and q > bugun:
            raise self._xato("kelajakda bo'lmasin")
        if not self.otmish and q < bugun:
            raise self._xato("o'tmishda bo'lmasin")
        return q


class Hisoblangan:
    """⭐ Faqat o'qish, hisoblanadigan maydon."""

    def __init__(self, f: Callable):
        self.f = f
        self.__doc__ = f.__doc__

    def __set_name__(self, ega, nom):
        self.nom = nom
        self.ega = ega.__name__

    def __get__(self, obj, ega=None):
        if obj is None:
            return self
        return self.f(obj)

    def __set__(self, obj, q):
        raise AttributeError(
            f"{self.ega}.{self.nom} hisoblanadi — yozib bo'lmaydi"
        )


class Model:
    """Asos model — maydonlarni biladi."""

    def __init__(self, **kw):
        maydonlar = self._maydonlar()
        notogri = set(kw) - set(maydonlar)
        if notogri:
            raise TypeError(f"Noma'lum maydonlar: {sorted(notogri)}")
        for nom, q in kw.items():
            setattr(self, nom, q)
        for nom, m in maydonlar.items():
            if m.majburiy and nom not in kw:
                raise TypeError(f"{nom} majburiy")

    @classmethod
    def _maydonlar(cls) -> dict[str, Maydon]:
        natija = {}
        for S in reversed(cls.__mro__):
            for nom, q in vars(S).items():
                if isinstance(q, Maydon):
                    natija[nom] = q
        return natija

    def lugat(self) -> dict[str, Any]:
        return {nom: getattr(self, nom) for nom in self._maydonlar()}

    def __repr__(self):
        qismlar = ", ".join(f"{k}={v!r}" for k, v in self.lugat().items())
        return f"{type(self).__name__}({qismlar})"


# ═══ MODEL ═══

class Foydalanuvchi(Model):
    ism = Satr(eng_kam=2, eng_kop=50, majburiy=True,
               doc="To'liq ism")
    email = Satr(naqsh=r"[^@\s]+@[^@\s]+\.[a-z]{2,}", majburiy=True,
                 doc="Email manzil")
    yosh = Butun(eng_kam=0, eng_kop=150, sukut=0)
    rol = Tanlov({"admin", "muharrir", "o'quvchi"}, sukut="o'quvchi")
    tugilgan = Sana(kelajak=False, sukut=None)
    login = Satr(naqsh=r"[a-z][a-z0-9_]{2,19}", faqat_oqish=True,
                 majburiy=True)

    @Hisoblangan
    def korinadigan_ism(self) -> str:
        """Ism yoki login."""
        return self.ism or self.login

    @Hisoblangan
    def kattami(self) -> bool:
        """18 yoshdan katta."""
        return self.yosh >= 18


print("1. To'g'ri yaratish:\n")

f = Foydalanuvchi(
    ism="  Aziz Karimov  ",
    email="AZIZ@wisar.uz",
    yosh=30,
    rol="admin",
    login="aziz_k",
    tugilgan="1996-03-15",
)

print(f"  {f}\n")
print(f"  Maydonlar:")
for nom, q in f.lugat().items():
    print(f"    {nom:<14} {q!r}")

print(f"\n  Hisoblangan:")
print(f"    korinadigan_ism = {f.korinadigan_ism!r}")
print(f"    kattami         = {f.kattami}")

print(f"\n  ⭐ ism avtomatik tozalandi: {f.ism!r}")


print("\n\n2. Tekshiruvlar:\n")

XATOLAR = [
    ("ism juda qisqa",      dict(ism="A", email="a@b.uz", login="abc")),
    ("email noto'g'ri",     dict(ism="Aziz", email="notogri", login="abc")),
    ("yosh manfiy",         dict(ism="Aziz", email="a@b.uz", login="abc",
                                 yosh=-5)),
    ("yosh juda katta",     dict(ism="Aziz", email="a@b.uz", login="abc",
                                 yosh=200)),
    ("yosh — satr",         dict(ism="Aziz", email="a@b.uz", login="abc",
                                 yosh="30")),
    ("yosh — bool",         dict(ism="Aziz", email="a@b.uz", login="abc",
                                 yosh=True)),
    ("rol noma'lum",        dict(ism="Aziz", email="a@b.uz", login="abc",
                                 rol="xoja")),
    ("login katta harf",    dict(ism="Aziz", email="a@b.uz", login="Aziz")),
    ("tugilgan kelajakda",  dict(ism="Aziz", email="a@b.uz", login="abc",
                                 tugilgan="2099-01-01")),
    ("noma'lum maydon",     dict(ism="Aziz", email="a@b.uz", login="abc",
                                 xato=1)),
    ("majburiy yo'q",       dict(ism="Aziz", email="a@b.uz")),
]

for tavsif, kw in XATOLAR:
    try:
        Foydalanuvchi(**kw)
        natija = "⚠️ O'TDI"
    except (ValueError, TypeError) as e:
        natija = f"✅ {type(e).__name__}: {str(e)[:52]}"
    print(f"  {tavsif:<22} {natija}")


print("\n\n3. ⭐ Faqat o'qish maydonlar:\n")

print(f"  f.login = {f.login!r}")
for kod, fn in [
    ("f.login = 'yangi'",       lambda: setattr(f, "login", "yangi")),
    ("f.korinadigan_ism = 'X'", lambda: setattr(f, "korinadigan_ism", "X")),
    ("f.kattami = False",       lambda: setattr(f, "kattami", False)),
    ("del f.login",             lambda: delattr(f, "login")),
    ("f.ism = 'Yangi Ism'",     lambda: setattr(f, "ism", "Yangi Ism")),
]:
    try:
        fn(); n = "✅ o'tdi"
    except AttributeError as e:
        n = f"✅ AttributeError: {str(e)[:44]}"
    print(f"    {kod:<26} {n}")

print(f"\n  f.ism = {f.ism!r}")


print("\n\n4. Meros:\n")


class Admin(Foydalanuvchi):
    """⭐ Maydonlar meros olinadi va qo'shiladi."""

    daraja = Butun(eng_kam=1, eng_kop=10, sukut=1)
    bolim = Satr(eng_kop=30, sukut="umumiy")

    @Hisoblangan
    def toliq_huquq(self) -> bool:
        return self.daraja >= 8


a = Admin(ism="Malika Yusupova", email="m@wisar.uz", login="malika",
          rol="admin", yosh=35, daraja=9, bolim="Xavfsizlik")

print(f"  {a}\n")
print(f"  Maydonlar soni:")
print(f"    Foydalanuvchi: {len(Foydalanuvchi._maydonlar())}")
print(f"    Admin:         {len(Admin._maydonlar())}")
print(f"    Qo'shilgan:    "
      f"{sorted(set(Admin._maydonlar()) - set(Foydalanuvchi._maydonlar()))}")

print(f"\n  Hisoblangan:")
print(f"    a.kattami      = {a.kattami}")
print(f"    a.toliq_huquq  = {a.toliq_huquq}")

print(f"\n  Meros olingan tekshiruvlar ham ishlaydi:")
try:
    a.yosh = 200
except ValueError as e:
    print(f"    a.yosh = 200 → ✅ ValueError: {e}")


print("\n\n5. Deskriptorlar tuzilishi:\n")

print(f"  {'Maydon':<18} {'Tur':<10} {'Majburiy':<10} {'Faqat o`qish':<14} "
      f"{'Sukut'}")
print("  " + "─" * 68)
for nom, m in Admin._maydonlar().items():
    print(f"  {nom:<18} {type(m).__name__:<10} {m.majburiy!s:<10} "
          f"{m.faqat_oqish!s:<14} {m.sukut!r}")

print(f"\n  Hisoblangan maydonlar:")
for nom, q in vars(Admin).items():
    if isinstance(q, Hisoblangan):
        print(f"    {nom:<18} {q.__doc__}")
for nom, q in vars(Foydalanuvchi).items():
    if isinstance(q, Hisoblangan):
        print(f"    {nom:<18} {q.__doc__}")

print(f"\n  Nusxa lug'ati (xususiy nomlar bilan):")
for k in sorted(a.__dict__):
    print(f"    {k:<26} {a.__dict__[k]!r}")

print(f"""

  ⭐ BU TIZIMDA:

     Maydon (asos deskriptor):
       __set_name__   nomni avtomatik oladi
       __get__        sukut, majburiylik
       __set__        tekshiruv, faqat o'qish
       __delete__     majburiy maydonni himoyalash
       tekshir()      ⭐ avlodlar qayta aniqlaydi

     Avlodlar: Satr, Butun, Tanlov, Sana

     Hisoblangan:
       Faqat __get__ va __set__ (yozishni to'sadi)
       ⭐ @property ga o'xshash, lekin qayta ishlatiladigan

     Model:
       _maydonlar()   MRO bo'ylab yig'adi
       __init__       majburiy maydonlarni tekshiradi
       lugat()        serializatsiya

  ⭐ AFZALLIKLARI @property GA NISBATAN:
     • Kod QAYTA ISHLATILADI (har maydonga getter/setter yozmaysiz)
     • Tekshiruv mantiqi bir joyda
     • Meros bilan kengaytiriladi
     • Introspeksiya oson (_maydonlar())
     • __set_name__ nomni avtomatik beradi

  ⚠️ NARXI:
     • Murakkabroq
     • mypy to'liq tushunmasligi mumkin
     • Kichik sinf uchun ortiqcha

  ⭐ MUQOBIL: pydantic, attrs, dataclasses + __post_init__
""")

Natijaning muhim qismi:

text
1. To'g'ri yaratish:

  Foydalanuvchi(ism='Aziz Karimov', email='AZIZ@wisar.uz', yosh=30, ...)

  Maydonlar:
    ism            'Aziz Karimov'
    login          'aziz_k'
    tugilgan       datetime.date(1996, 3, 15)

  ⭐ ism avtomatik tozalandi: 'Aziz Karimov'

2. Tekshiruvlar:

  ism juda qisqa         ✅ ValueError: Foydalanuvchi.ism: kamida 2 belgi
  email noto'g'ri        ✅ ValueError: Foydalanuvchi.email: naqshga mos emas
  yosh — bool            ✅ TypeError: Foydalanuvchi.yosh: butun son kutildi
  login katta harf       ✅ ValueError: Foydalanuvchi.login: naqshga mos emas
  majburiy yo'q          ✅ TypeError: login majburiy

3. ⭐ Faqat o'qish maydonlar:

    f.login = 'yangi'          ✅ AttributeError: Foydalanuvchi.login faqat
    f.kattami = False          ✅ AttributeError: Foydalanuvchi.kattami
                                  hisoblanadi

4. Meros:

    Foydalanuvchi: 6
    Admin:         8
    Qo'shilgan:    ['bolim', 'daraja']

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


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

Noto'g'ri fikr To'g'risi
"property — sehr" Oddiy data deskriptor
"Getter/setter yozish kerak" Ochiq atribut → keyin property
"Avlodda getter'ni qayta yozsam yetadi" Setter yo'qoladi
"super().x = q ishlaydi" super() faqat o'qish
"cached_property — property bilan bir xil" Non-data, __dict__ ga yozadi
"property har doim yaxshi" Qimmat/yon ta'sirli — metod
"A.x qiymatni beradi" property obyektini
"hasattr har qanday xatoni ushlaydi" Faqat AttributeError

6. Keng tarqalgan xatolar va yechimlari

1. Rekursiya

python
@property
def x(self): return self.x          # ❌
def x(self): return self._x         # ✅

2. Avlodda setter yo'qolishi

python
class B(A):
    @property                       # ⚠️ setter yo'qoladi
    def x(self): ...
    @A.x.getter                     # ✅
    def x(self): ...

3. __init__ da tartib

python
def __init__(self, x):
    self.x = x                      # ⚠️ setter maks ni izlaydi
    self.maks = 100
                                    # ✅ maks ni oldin

4. Yon ta'sirli property

python
@property
def d(self): return yukla()         # ⚠️ debugger ham chaqiradi
def d_ol(self): return yukla()      # ✅ metod

5. Istisno tashlash

python
@property
def x(self): return self._d["x"]    # ⚠️ KeyError
def x(self): return self._d.get("x") # ✅

6. cached_property + __slots__

python
class A:
    __slots__ = ("x",)
    @cached_property                # ❌ TypeError
    def y(self): ...

7. Ortiqcha property

python
@property
def x(self): return self._x
@x.setter
def x(self, q): self._x = q         # ⚠️ oddiy atribut yetadi

8. Ichki konteynerni qaytarish

python
@property
def el(self): return self._el       # ⚠️ o'zgartiriladi
def el(self): return tuple(self._el) # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 8.2-dars (o'tilgan): atribut qidiruv tartibi
  • 8.3-dars: hosila atributlar
  • 8.10-dars: kapsulatsiya
  • 8.16-dars: staticmethod, classmethod — ular ham deskriptor
  • 8.17-dars: dataclass va property
  • 8.19-dars: __slots__ va cached_property
  • Django ORM, SQLAlchemy: maydonlar — deskriptorlar
  • HOWTO: "Descriptor Guide" (docs.python.org)

8. Eng yaxshi amaliyotlar

  1. Ochiq atributdan boshlang. property — keyin, kerak bo'lganda.

  2. Getter/setter yozmang. Java uslubi Pythonda ortiqcha.

  3. property arzon va yon ta'sirsiz bo'lsin. Aks holda — metod.

  4. Avlodda @Ota.x.getter. Setter meros olinsin.

  5. __init__ da tartibga e'tibor. Setter ishlatadigan atributlar oldin.

  6. Ichki konteynerni nusxa qiling. tuple(), frozenset().

  7. Ko'p bir xil maydon bo'lsa — deskriptor sinfi. Kod qayta ishlatilsin.

  8. cached_property da keshni bekor qilishni o'ylang. Asos o'zgarsa.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
1.  class A:
        @property
        def x(self): return 1
    print(type(A.x).__name__, A().x)
2.  class A:
        @property
        def x(self): return 1
    a = A()
    try: a.x = 2
    except AttributeError: print("xato")
3.  class A:
        @property
        def x(self): return self._x
    a = A()
    a.__dict__["x"] = 99
    a._x = 1
    print(a.x)
4.  class A:
        def f(self): return "metod"
    a = A()
    a.__dict__["f"] = lambda: "nusxa"
    print(a.f())
5.  class A:
        @property
        def x(self): return 1
        @x.setter
        def x(self, q): self._x = q
    class B(A):
        @property
        def x(self): return 2
    print(B.x.fset)
6.  from functools import cached_property
    class A:
        @cached_property
        def x(self): return 42
    a = A()
    a.x
    print("x" in a.__dict__)
7.  class A:
        @property
        def x(self): raise ValueError("xato")
    print(hasattr(A(), "x"))
8.  class A:
        @property
        def x(self): raise AttributeError
    print(hasattr(A(), "x"))
9.  class D:
        def __get__(self, o, e): return "get"
    class A: d = D()
    a = A()
    a.__dict__["d"] = "nusxa"
    print(a.d)
10. class D:
        def __get__(self, o, e): return "get"
        def __set__(self, o, q): pass
    class A: d = D()
    a = A()
    a.__dict__["d"] = "nusxa"
    print(a.d)
11. class A:
        @property
        def x(self): return self._x
    print(A.x.fget.__name__)
12. class A:
        x = property(lambda s: 1, doc="Doc")
    print(A.x.__doc__)
Javoblar
  1. property 1
  2. xato — setter yo'q
  3. 1 — data deskriptor nusxadan yuqori
  4. nusxa — funksiya non-data deskriptor
  5. None — setter yo'qoldi
  6. True — cached_property __dict__ ga yozadi
  7. ValueError chiqadi — hasattr faqat AttributeError ni ushlaydi (3.2+)
  8. False
  9. nusxa — non-data
  10. get — data
  11. x
  12. Doc

Vazifa 2: Xatolarni tuzating

python
1.  @property
    def x(self): return self.x
2.  @x.setter
    def x(self, q): self.x = q
3.  class B(A):
        @property
        def x(self): return super().x * 2
4.  def __init__(self, x):
        self.x = x
        self.maks = 100
5.  @property
    def malumot(self): return requests.get(url).json()
6.  @property
    def x(self): return self._d["x"]
7.  @property
    def elementlar(self): return self._el     # list
8.  class A:
        __slots__ = ("x",)
        @cached_property
        def y(self): return self.x * 2
Javoblar
python
1.  return self._x
2.  self._x = q
3.  @x.setter ham qo'shing (yoki @A.x.getter ishlating)
4.  self.maks = 100  ni OLDIN qo'ying
5.  metod qiling: def malumot_ol(self)
6.  return self._d.get("x")
7.  return tuple(self._el)
8.  __slots__ ga "__dict__" qo'shing yoki oddiy property

Vazifa 3: Doira sinfi

Yozing:

  1. radius — property, musbat bo'lsin
  2. diametr — o'qish va yozish (radiusni o'zgartirsin)
  3. yuza, perimetr — faqat o'qish
  4. radius o'zgarganda kesh bekor qilinsin
  5. cached_property bilan qimmat hisoblash
  6. Barcha holatlarni test qiling

Vazifa 4: Tekshiruvchi deskriptor

  1. Tekshirilgan asos deskriptor sinfi
  2. Musbat, Oraliq, Uzunlik, Naqsh avlodlari
  3. __set_name__ bilan nom
  4. Aniq xato xabarlari (sinf va maydon nomi bilan)
  5. None ni qo'llab-quvvatlash (ixtiyoriy=True)
  6. mypy bilan ishlashini tekshiring

Vazifa 5: Kuzatiladigan atributlar

  1. Har o'zgarishni jurnalga yozadigan deskriptor
  2. Eski va yangi qiymat
  3. on_change callback
  4. Kuzatuvni yoqish/o'chirish
  5. Bir necha atributni birga kuzatish
  6. __set_name__ bilan avtomatik nom

Vazifa 6: Dangasa yuklash

  1. DangasaMaydon deskriptori
  2. Birinchi murojaatda yuklaydi
  3. Keyingi murojaatlar — keshdan
  4. bekor_qil() metodi
  5. Yuklash xatosi qayta urinishga imkon bersin
  6. cached_property bilan solishtiring

Vazifa 7: O'ylash

Nega Python property ni funksiya sifatida emas, deskriptor sifatida amalga oshirdi?

Javob

Chunki deskriptor protokoli — Pythonning atribut kirishini boshqarishning yagona umumiy mexanizmi, va property uning bir holati xolos.

1. Deskriptor protokoli nima beradi

python
obj.x

Bu ifoda ortida bitta mexanizm turadi:

1. type(obj).__mro__ da DATA deskriptor → __get__(obj, type)
2. obj.__dict__["x"]
3. type(obj).__mro__ da NON-DATA deskriptor → __get__
4. sinf atributi
5. __getattr__
6. AttributeError

Va bu mexanizm hamma narsani izohlaydi:

Xususiyat Deskriptor turi
Metod Funksiya (non-data)
@property property (data)
@staticmethod staticmethod (non-data)
@classmethod classmethod (non-data)
@cached_property cached_property (non-data)
__slots__ maydoni member_descriptor (data)
super() maxsus
ORM maydonlari foydalanuvchi deskriptorlari

Bitta mexanizm — sakkiz xil xatti-harakat.

2. Agar property maxsus bo'lsa

Faraz qilaylik, property — kompilyator/interpretator darajasida maxsus:

python
class A:
    @property                       # ⚠️ maxsus sintaksis
    def x(self): ...

Muammolar:

a) Kengaytirib bo'lmasdi

python
# ⭐ Deskriptor bilan MUMKIN:
class Kuzatiladigan:
    def __get__(self, obj, ega): ...
    def __set__(self, obj, q):
        jurnal.yoz(f"{self.nom} = {q}")
        ...

class A:
    x = Kuzatiladigan()             # ✅ o'z xatti-harakati

Maxsus property bilan — faqat Python ishlab chiquvchilari yangi turlarini qo'sha olardi.

b) Metodlar boshqacha ishlardi

python
class A:
    def f(self): ...

A.f                                 # <function>
A().f                               # <bound method>

Bu ham deskriptor ishi:

python
A.__dict__["f"].__get__(obj, A)     # → bound method

Agar deskriptor protokoli bo'lmasa, metod bog'lanishi ham maxsus kod bo'lardi.

c) staticmethod/classmethod mumkin bo'lmasdi

python
class staticmethod:
    def __get__(self, obj, ega=None):
        return self.f               # ⭐ bog'lamaydi

class classmethod:
    def __get__(self, obj, ega=None):
        return MethodType(self.f, ega)  # ⭐ SINFGA bog'laydi

Uchalasi — bir mexanizm, turli __get__.

3. Data vs non-data — nozik dizayn

python
class A:
    def metod(self): return "sinfdan"

a = A()
a.metod = lambda: "nusxadan"        # ✅ ishlaydi (non-data)
python
class B:
    @property
    def x(self): return "sinfdan"

b = B()
b.x = "nusxadan"                    # ❌ AttributeError (data)

Bu farq ataylab:

Non-data Data
Nusxada almashtirish Mumkin To'siladi
Misol Metod property
Sabab Monkey patching foydali Invariant himoyalanishi kerak

Va cached_property — non-data, aynan shuning uchun ikkinchi chaqiruvda deskriptor umuman chaqirilmaydi:

python
1-chaqiruv:  __dict__ da yo'q → __get__ → hisoblaydi → __dict__ ga yozadi
2-chaqiruv:  __dict__ da bor → ⭐ deskriptorga BORMAYDI (non-data)

Bu — nol qo'shimcha xarajat keshi. Agar cached_property data deskriptor bo'lsa, har chaqiruvda __get__ ishlab, __dict__ ni tekshirishi kerak bo'lardi.

4. __slots__ ham deskriptor

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

type(A.x)                           # <class 'member_descriptor'>
A.x.__get__, A.x.__set__            # ⭐ ikkalasi ham bor → data

Shuning uchun __slots__ va cached_property birga ishlamaydi: __dict__ yo'q.

5. Kengaytirilganlik amalda

Django ORM:

python
class Model:
    ism = CharField(max_length=100)     # ⭐ deskriptor

# Aslida:
class DeferredAttribute:
    def __get__(self, instance, cls=None):
        if instance is None: return self
        data = instance.__dict__
        if self.field.attname not in data:
            data[self.field.attname] = self._check_parent_chain(instance)
        return data[self.field.attname]

SQLAlchemy:

python
class Foydalanuvchi(Base):
    ism = Column(String)            # ⭐ deskriptor

Foydalanuvchi.ism == "Aziz"         # ⭐ SQL ifodasi!

Column.__get__ nusxada qiymat, sinfda esa SQL ifodasi generatori qaytaradi:

python
def __get__(self, obj, ega):
    if obj is None:
        return self.ifoda()         # SQL uchun
    return obj.__dict__[self.nom]   # qiymat

Bu — deskriptorsiz mumkin emas.

6. Boshqa tillar

Til Mexanizm
Python Deskriptor protokoli (umumiy)
C# get/set — til darajasida
Kotlin by delegatlar — Pythonga o'xshash
JavaScript Object.defineProperty, Proxy
Ruby attr_accessor, method_missing
Java Yo'q (getter/setter qo'lda)

Kotlin eng yaqin:

kotlin
class A {
    var x: Int by Delegates.observable(0) { _, eski, yangi ->
        println("$eski → $yangi")
    }
    val y: String by lazy { hisobla() }      // ⭐ cached_property
}

// O'z delegatingiz:
class MeningDelegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String)
}

Kotlin by — Python deskriptori bilan deyarli bir xil. Va property(KProperty) argumenti — Pythonning __set_name__ iga o'xshash.

JavaScript Proxy:

javascript
const obj = new Proxy({}, {
    get(target, prop) { ... },
    set(target, prop, value) { ... },
});

JavaScript da bu butun obyekt darajasida, Pythonda esa har atribut darajasida — Python yondashuvi nozikroq.

7. Narxi

Deskriptorlar bepul emas:

python
import timeit

class Oddiy:
    def __init__(self): self.x = 1

class Propertyli:
    def __init__(self): self._x = 1
    @property
    def x(self): return self._x

o, p = Oddiy(), Propertyli()
timeit.timeit(lambda: o.x, number=10**6)    # ~0.05s
timeit.timeit(lambda: p.x, number=10**6)    # ~0.15s  ⚠️ 3× sekin

Sabab: property.__get__ → Python funksiyasi chaqiruvi.

Lekin bu kamdan-kam muhim — 100 nanosekund farq.

8. Nima o'rgandik

property — deskriptor, chunki:

  1. Bitta mexanizm metod, property, staticmethod, classmethod, __slots__, super() ni izohlaydi
  2. Kengaytiriladigan — ORM, validatsiya, kuzatuv, dangasa yuklash
  3. Data/non-data farqi nozik xatti-harakatlarni beradi
  4. Sinf o'zi hech qanday maxsus kod talab qilmaydi

Umumiy naqsh: Python murakkab xususiyatni maxsus sintaksis o'rniga umumiy protokol bilan beradi:

Xususiyat Protokol
for __iter__/__next__
with __enter__/__exit__
+ __add__/__radd__
obj.x Deskriptor
f(x) __call__
x[i] __getitem__

Har birida: siz ham o'z turingizni qo'sha olasiz. Bu — Pythonning "hamma narsa obyekt" falsafasining amaliy ko'rinishi.

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


Xulosa

Bu darsda property va deskriptorlarni o'rgandik.

Eng muhim uch fikr:

  1. property — data deskriptor, sehr emas. U __get__, __set__, __delete__ metodlariga ega oddiy sinf. Data deskriptor bo'lgani uchun u nusxa lug'atidan yuqori turadi — shuning uchun obj.__dict__["x"] = q uni to'sa olmaydi. Funksiya esa non-data deskriptor, shuning uchun obj.metod = fn ishlaydi.

  2. Ochiq atributdan boshlang. Pythonda getter/setter yozilmaydi: self.x = x yozing, tekshiruv kerak bo'lganda @property qo'shasiz va chaqiruvchi kod o'zgarmaydi. property arzon va yon ta'sirsiz bo'lsin — qimmat yoki tarmoqqa murojaat qiladigan amal metod bo'lishi kerak.

  3. Avlodda @property yozish setter'ni yo'qotadi. Yangi property obyekti yaratiladi va ota-sinfnikiga aloqasi bo'lmaydi. @Ota.x.getter ishlating — u mavjud property dan nusxa olib faqat getter'ni almashtiradi. Ko'p bir xil maydon bo'lsa, property o'rniga deskriptor sinfi yozing: __set_name__ nomni avtomatik beradi va kod qayta ishlatiladi.

Keyingi darsda @staticmethod va @classmethod ni chuqur ko'ramiz: ular ham deskriptor, fabrika metodlari va qachon qaysi biri kerakligi.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
8.15-dars: @property — IlmHamroh