Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Uch qism
- 2.2. Deskriptor protokoli
- 2.3. property ni qo'lda yozish
- 2.4. cached_property
- 2.5. Meros va property
- 2.6. property tuzoqlari
- 2.7. Qachon property
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — property asoslari
- Misol 2 — Deskriptor protokoli
- Misol 3 — Meros va tuzoqlar
- Misol 4 — Amaliy: validatsiya tizimi
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
8.15-dars: @property
8-QISM — OBYEKTGA YO'NALTIRILGAN DASTURLASH · 15-dars
1. Kirish va motivatsiya
Java dasturchi Pythonda shunday yozadi:
class Harorat:
def __init__(self, c):
self._c = c
def get_selsiy(self): # ⚠️
return self._c
def set_selsiy(self, q): # ⚠️
self._c = qPythonda esa — oddiy atribut:
class Harorat:
def __init__(self, c):
self.selsiy = cVa keyin, tekshiruv kerak bo'lganda — chaqiruvchi kod o'zgarmaydi:
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 xilBu — "Uniform Access Principle": atributmi yoki hisoblanadimi — chaqiruvchi bilmasligi kerak.
Bu darsda:
-
@property—getter,setter,deleter - Deskriptor protokoli —
propertyaslida qanday ishlaydi - Data va non-data deskriptorlar
functools.cached_property- Meros va
propertytuzoqlari - O'z deskriptorlaringizni yozish
2. Nazariya — chuqur tushuntirish
2.1. Uch qism
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._xNomlar bir xil bo'lishi shart:
@property
def x(self): ...
@x.setter # ⭐ x, property nomi
def x(self, q): ... # ⭐ x, bir xil nomFaqat o'qish:
@property
def yuza(self):
return self.a * self.b
# setter yo'q → yozishga urinish AttributeErrorEski sintaksis (hali ishlaydi):
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:
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 bilishIkki 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
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:
A.x # <property object at 0x...>
A.x.fget # <function A.x>
A().x # haqiqiy qiymat2.4. cached_property
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 CHAQIRILMAYDIFarqlari:
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:
del h.__dict__["sozlar"] # yoki
h.__dict__.pop("sozlar", None)2.5. Meros va property
Getter'ni qayta aniqlash — setter yo'qoladi:
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 # ❌ AttributeErrorTo'g'ri usullar:
# 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:
@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:
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 = qBog'liq atributlarni oldin o'rnating.
3. Yon ta'sirli property:
@property
def malumot(self):
return tarmoqdan_yukla() # ⚠️ obj.malumot sekin! Metod qiling: obj.malumot_yukla().
4. Istisno tashlaydigan property:
@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:
A.x # <property object>, qiymat emas
A.x.fget(obj) # ⭐ qo'lda chaqirish2.7. Qachon property
To'g'ri:
# 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:
# 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
@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 AttributeErrorDeskriptor protokoli
__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 PASTAtribut qidiruv
1. data deskriptor (sinf zanjirida)
2. obj.__dict__
3. non-data deskriptor
4. sinf atributi
5. __getattr__
6. AttributeErrorcached_property
@cached_property non-data → __dict__ ga yoziladi
del obj.__dict__["x"] keshni bekor qilish
⚠️ __slots__ bilan ishlamaydiMeros
@Ota.x.getter setter meros olinadi ✅
@property ⚠️ YANGI property, setter yo'qoladi
Ota.x.fset(self, q) ota-sinf setterini chaqirish4. Batafsil misollar
Misol 1 — property asoslari
"""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:
=== 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 > 100Nima ko'rsatdi: 2.1, 2.2, 2.6-bo'limlar.
Misol 2 — Deskriptor protokoli
"""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:
=== 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-DATANima ko'rsatdi: 2.2, 2.3, 2.4-bo'limlar.
Misol 3 — Meros va tuzoqlar
"""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:
=== ⚠️ 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
"""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:
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
@property
def x(self): return self.x # ❌
def x(self): return self._x # ✅2. Avlodda setter yo'qolishi
class B(A):
@property # ⚠️ setter yo'qoladi
def x(self): ...
@A.x.getter # ✅
def x(self): ...3. __init__ da tartib
def __init__(self, x):
self.x = x # ⚠️ setter maks ni izlaydi
self.maks = 100
# ✅ maks ni oldin4. Yon ta'sirli property
@property
def d(self): return yukla() # ⚠️ debugger ham chaqiradi
def d_ol(self): return yukla() # ✅ metod5. Istisno tashlash
@property
def x(self): return self._d["x"] # ⚠️ KeyError
def x(self): return self._d.get("x") # ✅6. cached_property + __slots__
class A:
__slots__ = ("x",)
@cached_property # ❌ TypeError
def y(self): ...7. Ortiqcha property
@property
def x(self): return self._x
@x.setter
def x(self, q): self._x = q # ⚠️ oddiy atribut yetadi8. Ichki konteynerni qaytarish
@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:
dataclassvaproperty - 8.19-dars:
__slots__vacached_property - Django ORM, SQLAlchemy: maydonlar — deskriptorlar
- HOWTO: "Descriptor Guide" (docs.python.org)
8. Eng yaxshi amaliyotlar
Ochiq atributdan boshlang.
property— keyin, kerak bo'lganda.Getter/setter yozmang. Java uslubi Pythonda ortiqcha.
propertyarzon va yon ta'sirsiz bo'lsin. Aks holda — metod.Avlodda
@Ota.x.getter. Setter meros olinsin.__init__da tartibga e'tibor. Setter ishlatadigan atributlar oldin.Ichki konteynerni nusxa qiling.
tuple(),frozenset().Ko'p bir xil maydon bo'lsa — deskriptor sinfi. Kod qayta ishlatilsin.
cached_propertyda keshni bekor qilishni o'ylang. Asos o'zgarsa.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
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
property 1xato— setter yo'q1— data deskriptor nusxadan yuqorinusxa— funksiya non-data deskriptorNone— setter yo'qoldiTrue—cached_property__dict__ga yozadi-
ValueErrorchiqadi —hasattrfaqatAttributeErrorni ushlaydi (3.2+) Falsenusxa— non-dataget— dataxDoc
Vazifa 2: Xatolarni tuzating
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 * 2Javoblar
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 propertyVazifa 3: Doira sinfi
Yozing:
radius—property, musbat bo'lsindiametr— o'qish va yozish (radiusni o'zgartirsin)yuza,perimetr— faqat o'qishradiuso'zgarganda kesh bekor qilinsincached_propertybilan qimmat hisoblash- Barcha holatlarni test qiling
Vazifa 4: Tekshiruvchi deskriptor
Tekshirilganasos deskriptor sinfiMusbat,Oraliq,Uzunlik,Naqshavlodlari__set_name__bilan nom- Aniq xato xabarlari (sinf va maydon nomi bilan)
Noneni qo'llab-quvvatlash (ixtiyoriy=True)mypybilan ishlashini tekshiring
Vazifa 5: Kuzatiladigan atributlar
- Har o'zgarishni jurnalga yozadigan deskriptor
- Eski va yangi qiymat
on_changecallback- Kuzatuvni yoqish/o'chirish
- Bir necha atributni birga kuzatish
__set_name__bilan avtomatik nom
Vazifa 6: Dangasa yuklash
DangasaMaydondeskriptori- Birinchi murojaatda yuklaydi
- Keyingi murojaatlar — keshdan
bekor_qil()metodi- Yuklash xatosi qayta urinishga imkon bersin
cached_propertybilan 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
obj.xBu 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. AttributeErrorVa 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:
class A:
@property # ⚠️ maxsus sintaksis
def x(self): ...Muammolar:
a) Kengaytirib bo'lmasdi
# ⭐ 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-harakatiMaxsus property bilan — faqat Python ishlab chiquvchilari yangi turlarini qo'sha olardi.
b) Metodlar boshqacha ishlardi
class A:
def f(self): ...
A.f # <function>
A().f # <bound method>Bu ham deskriptor ishi:
A.__dict__["f"].__get__(obj, A) # → bound methodAgar deskriptor protokoli bo'lmasa, metod bog'lanishi ham maxsus kod bo'lardi.
c) staticmethod/classmethod mumkin bo'lmasdi
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
class A:
def metod(self): return "sinfdan"
a = A()
a.metod = lambda: "nusxadan" # ✅ ishlaydi (non-data)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:
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
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:
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:
class Foydalanuvchi(Base):
ism = Column(String) # ⭐ deskriptor
Foydalanuvchi.ism == "Aziz" # ⭐ SQL ifodasi! Column.__get__ nusxada qiymat, sinfda esa SQL ifodasi generatori qaytaradi:
def __get__(self, obj, ega):
if obj is None:
return self.ifoda() # SQL uchun
return obj.__dict__[self.nom] # qiymatBu — 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:
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:
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:
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:
- Bitta mexanizm metod,
property,staticmethod,classmethod,__slots__,super()ni izohlaydi - Kengaytiriladigan — ORM, validatsiya, kuzatuv, dangasa yuklash
- Data/non-data farqi nozik xatti-harakatlarni beradi
- 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:
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 uchunobj.__dict__["x"] = quni to'sa olmaydi. Funksiya esa non-data deskriptor, shuning uchunobj.metod = fnishlaydi.Ochiq atributdan boshlang. Pythonda getter/setter yozilmaydi:
self.x = xyozing, tekshiruv kerak bo'lganda@propertyqo'shasiz va chaqiruvchi kod o'zgarmaydi.propertyarzon va yon ta'sirsiz bo'lsin — qimmat yoki tarmoqqa murojaat qiladigan amal metod bo'lishi kerak.Avlodda
@propertyyozish setter'ni yo'qotadi. Yangipropertyobyekti yaratiladi va ota-sinfnikiga aloqasi bo'lmaydi.@Ota.x.getterishlating — u mavjudpropertydan nusxa olib faqat getter'ni almashtiradi. Ko'p bir xil maydon bo'lsa,propertyo'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.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!