Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Nima qiladi
- 2.2. Sintaksis
- 2.3. Beshta cheklov
- 2.4. Meros qoidalari
- 2.5. Xotira o'lchovlari
- 2.6. Tezlik
- 2.7. Amaliy naqshlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Nima qiladi va qancha tejaydi
- Misol 2 — Meros qoidalari
- Misol 3 — Qachon kerak, qachon kerak emas
- Misol 4 — Amaliy: geometrik dvigatel
- 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.19-dars: __slots__
8-QISM — OBYEKTGA YO'NALTIRILGAN DASTURLASH · 19-dars
1. Kirish va motivatsiya
Bir million nuqta — ~122 MB:
class Nuqta:
def __init__(self, x, y):
self.x, self.y = x, y
nuqtalar = [Nuqta(i, i) for i in range(1_000_000)] # ~122 MBBir qator qo'shdik — ~84 MB:
class Nuqta:
__slots__ = ("x", "y") # ⭐
def __init__(self, x, y):
self.x, self.y = x, y
nuqtalar = [Nuqta(i, i) for i in range(1_000_000)] # ~84 MB ~31% kam xotira (Python 3.14, tracemalloc bilan o'lchangan; bu raqamga int obyektlari va ro'yxatning o'zi ham kiradi).
Tezlik bo'yicha esa zamonaviy Pythonda deyarli farq yo'q — __slots__ ning asosiy foydasi xotira (2.6-bo'lim).
Lekin narxi bor:
n = Nuqta(1, 2)
n.z = 3 # ❌ AttributeError
n.__dict__ # ❌ AttributeError
n.__weakref__ # ❌ (agar qo'shilmasa)
@cached_property # ❌ ishlamaydi
def masofa(self): ...Bu darsda:
-
__slots__nima qiladi va qanday tejaydi - Xotira va tezlik o'lchovlari
- Beshta cheklov va ularni chetlab o'tish
- Meros bilan murakkab qoidalar
__weakref__,__dict__ni qaytarish- Qachon kerak emas
2. Nazariya — chuqur tushuntirish
2.1. Nima qiladi
__slots__ siz:
Nuqta obyekti (56 bayt)
├── ob_refcnt, ob_type (16 bayt)
├── __dict__ ko'rsatkichi (8 bayt) → dict (104+ bayt)
└── __weakref__ ko'rsatkichi (8 bayt) ├── 'x' → 1
└── 'y' → 2__slots__ bilan:
Nuqta obyekti (48 bayt)
├── ob_refcnt, ob_type (16 bayt)
├── x qiymati ko'rsatkichi (8 bayt) ⭐ to'g'ridan-to'g'ri
└── y qiymati ko'rsatkichi (8 bayt)Lug'at o'rniga — massiv. Har atribut aniq o'rin (offset) da saqlanadi.
Amalga oshirish:
class Nuqta:
__slots__ = ("x", "y")
type(Nuqta.x) # <class 'member_descriptor'>
Nuqta.x.__get__, Nuqta.x.__set__ # ⭐ data deskriptor Har slot uchun member_descriptor yaratiladi — u obyektning belgilangan baytidan o'qiydi.
2.2. Sintaksis
class A:
__slots__ = ("x", "y") # ✅ tuple (tavsiya)
__slots__ = ["x", "y"] # ✅ list
__slots__ = "x" # ✅ bitta satr
__slots__ = {"x": "X hujjati", # ✅ dict — hujjat bilan
"y": "Y hujjati"} dict shakli — help() da ko'rinadi.
Sukut qiymat berish mumkin emas:
class A:
__slots__ = ("x",)
x = 5 # ❌ ValueErrorValueError: 'x' in __slots__ conflicts with class variable Sabab: x = 5 sinf atributi member_descriptor ni to'sib qo'yardi.
2.3. Beshta cheklov
1. Yangi atribut qo'shib bo'lmaydi
n.z = 3 # ❌ AttributeError2. __dict__ yo'q
n.__dict__ # ❌ AttributeError
vars(n) # ❌ TypeError Buzilgan narsalar: pickle (eski protokol), copy, json, ba'zi kutubxonalar.
3. __weakref__ yo'q
weakref.ref(n) # ❌ TypeError Qo'shish: __slots__ = ("x", "y", "__weakref__").
4. cached_property ishlamaydi
@cached_property # ❌ TypeError — __dict__ kerak
def masofa(self): ...5. Ko'p merosda cheklov
class A: __slots__ = ("x",)
class B: __slots__ = ("y",)
class C(A, B): ... # ❌ TypeErrorTypeError: multiple bases have instance lay-out conflict2.4. Meros qoidalari
A) Avlodda __slots__ yo'q → __dict__ qaytadi:
class A:
__slots__ = ("x",)
class B(A):
pass # ⚠️ __slots__ yo'q
b = B()
b.z = 1 # ✅ ishlaydi
b.__dict__ # {'z': 1} ⚠️ tejash yo'qoldi Har avlodda __slots__ yozing — hatto bo'sh bo'lsa ham:
class B(A):
__slots__ = () # ✅ tejash saqlanadiB) Takrorlamang:
class A:
__slots__ = ("x",)
class B(A):
__slots__ = ("x", "y") # ⚠️ 'x' ikki marta — xotira behuda
__slots__ = ("y",) # ✅ faqat yangisiC) __dict__ bo'lgan ota-sinf:
class A:
pass # __dict__ bor
class B(A):
__slots__ = ("x",) # ⚠️ foydasiz — __dict__ meros olinadiD) Ko'p meros — faqat bittasida bo'sh bo'lmagan __slots__:
class A: __slots__ = ("x",)
class B: __slots__ = () # ⭐ bo'sh
class C(A, B): __slots__ = () # ✅ ishlaydi2.5. Xotira o'lchovlari
Bitta obyekt egallaydigan xotira (Python 3.14, 64-bit, tracemalloc bilan o'lchangan):
| Atribut soni | __dict__ bilan |
__slots__ bilan |
Tejash |
|---|---|---|---|
| 1 | ~80 bayt | ~40 bayt | 50% |
| 3 | ~96 bayt | ~56 bayt | 42% |
| 5 | ~112 bayt | ~72 bayt | 36% |
| 10 | ~160 bayt | ~112 bayt | 30% |
| 20 | ~248 bayt | ~192 bayt | 23% |
Python 3.11+ da farq ancha kamaydi. Endi oddiy sinf nusxasi ham atribut qiymatlarini alohida lug'atda emas, obyektning o'zida ("inline values") saqlaydi, kalitlar esa sinfning barcha nusxalari orasida ulashiladi. Haqiqiy lug'at faqat kerak bo'lganda yaratiladi.
O'lchash tuzog'i: sys.getsizeof(obj) + sys.getsizeof(obj.__dict__) usuli 3.11+ da noto'g'ri natija beradi — obj.__dict__ ga murojaat qilishning o'zi lug'atni yaratib qo'yadi:
# 200 000 ta ikki atributli obyekt, har biri uchun:
# __dict__ ga murojaatdan oldin: ~88 bayt
# har biriga o.__dict__ dan keyin: ~152 bayt ⚠️ o'lchov o'lchanayotganni o'zgartirdi Ishonchli usul — ko'p obyekt yaratib, tracemalloc bilan umumiy o'sishni o'lchash.
Amaliy qoida: 100 000 dan kam obyekt bo'lsa — __slots__ kerak emas.
2.6. Tezlik
Python 3.14 da timeit bilan o'lchangan (bitta amal):
# Atribut o'qish
obj.x (__dict__) ~35 ns
obj.x (__slots__) ~34 ns ≈ farq yo'q
# Atribut yozish
obj.x = 1 (__dict__) ~37 ns
obj.x = 1 (__slots__) ~40 ns ⚠️ hatto biroz sekinroq
# Obyekt yaratish (2 atribut)
A(1, 2) (__dict__) ~210 ns
A(1, 2) (__slots__) ~193 ns ~8% tezroq Eski maqolalarda "__slots__ atributga kirishni 20% tezlashtiradi" degan gapni uchratasiz — bu eski Python versiyalariga tegishli. 3.11+ dagi ixtisoslashgan bayt-kod (PEP 659) ikkala holatni ham deyarli bir xil tezlikka keltirdi. __slots__ ni tezlik uchun emas, xotira uchun ishlating. Raqamlar mashina va versiyaga qarab farq qiladi — muhim qarorlardan oldin o'zingiz o'lchang.
2.7. Amaliy naqshlar
__dict__ ni qaytarish (aralash):
class A:
__slots__ = ("x", "y", "__dict__") # ⭐ ikkalasi ham
a = A()
a.x = 1 # slot — tez
a.qoshimcha = 2 # __dict__ — moslashuvchanBu tejashning katta qismini yo'qotadi.
dataclass bilan:
@dataclass(slots=True) # ⭐ 3.10+
class Nuqta:
x: float
y: float 3.10 gacha: __slots__ ni qo'lda yozish kerak, lekin sukut qiymatlar bilan konflikt bo'ladi.
NamedTuple — muqobil:
class Nuqta(NamedTuple):
x: float
y: float Ko'pchilik "tuple asosida — demak eng kam xotira" deb o'ylaydi, lekin bu noto'g'ri. 1 000 000 ta ikki maydonli obyekt (Python 3.14):
__slots__ / dataclass(slots=True) ~84 MB ⭐ eng kam
tuple ~100 MB
NamedTuple ~107 MB
oddiy sinf ~122 MB NamedTuple ni xotira uchun emas, o'zgarmaslik, indeks bo'yicha kirish va ochib olish (x, y = nuqta) kerak bo'lganda tanlang.
Pickle:
# ⭐ __slots__ bilan pickle ishlaydi (protokol 2+)
import pickle
pickle.dumps(obj, protocol=2) # ✅
# ⚠️ Maxsus holat kerak bo'lsa:
def __getstate__(self):
return {s: getattr(self, s) for s in self.__slots__}
def __setstate__(self, holat):
for k, v in holat.items():
setattr(self, k, v)3. Tez ma'lumotnoma
Asosiy
class A:
__slots__ = ("x", "y") tuple (tavsiya)
__slots__ = "x" bitta satr
__slots__ = {"x": "doc"} hujjat bilan
⭐ __dict__ o'rniga MASSIV
Har slot → member_descriptor (data deskriptor)Cheklovlar
1. Yangi atribut ❌ AttributeError
2. __dict__ ❌ (vars() ham)
3. __weakref__ ❌ → __slots__ ga qo'shing
4. cached_property ❌ (__dict__ kerak)
5. Ko'p meros ❌ ikkalasida ham bo'sh bo'lmagan slots
6. Sinf atributi bilan ❌ ValueError (x = 5)Meros
class B(A):
__slots__ = () ⭐ SHART — aks holda __dict__ qaytadi
__slots__ = ("y",) ✅ faqat YANGI nomlar
__slots__ = ("x", "y") ⚠️ x takrorlandi — behudaQachon
✅ 100k+ obyekt
✅ Ma'lumot obyektlari (Nuqta, Yozuv)
✅ Atributlarni cheklash kerak
❌ Kam obyekt (< 100k)
❌ Dinamik atributlar kerak
❌ cached_property, weakref (qo'shmasangiz)
❌ "Ehtiyot uchun" — avval O'LCHANG4. Batafsil misollar
Misol 1 — Nima qiladi va qancha tejaydi
"""__slots__ mexanizmi va o'lchovlar."""
import sys
import timeit
from dataclasses import dataclass
print("=== ⭐ 1. Mexanizm ===\n")
class Oddiy:
def __init__(self, x, y):
self.x, self.y = x, y
class Slotli:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x, self.y = x, y
o, s = Oddiy(1, 2), Slotli(1, 2)
print(f" Oddiy:")
print(f" o.__dict__ = {o.__dict__}")
print(f" type(Oddiy.x) = ", end="")
try:
print(type(Oddiy.x).__name__)
except AttributeError:
print("❌ AttributeError (sinf atributi yo'q)")
print(f"\n Slotli:")
print(f" s.__dict__ = ", end="")
try:
print(s.__dict__)
except AttributeError:
print("❌ AttributeError")
print(f" Slotli.__slots__ = {Slotli.__slots__}")
print(f" type(Slotli.x) = {type(Slotli.x).__name__}")
print(f" Slotli.x = {Slotli.x}")
print(f"\n ⭐ member_descriptor — DATA deskriptor:")
for m in ["__get__", "__set__", "__delete__"]:
print(f" {m:<14} {hasattr(type(Slotli.x), m)}")
print(f"\n Qo'lda chaqirish:")
print(f" Slotli.x.__get__(s, Slotli) = {Slotli.x.__get__(s, Slotli)}")
Slotli.x.__set__(s, 99)
print(f" Slotli.x.__set__(s, 99) → s.x = {s.x}")
s.x = 1
print(f"""
⭐ TUZILISH:
__dict__ bilan:
obyekt → __dict__ ko'rsatkichi → dict → {{'x': 1, 'y': 2}}
(ikki bosqichli qidiruv + dict ustama xarajati)
__slots__ bilan:
obyekt → [x_ko'rsatkichi, y_ko'rsatkichi]
(to'g'ridan-to'g'ri OFFSET bo'yicha)
""")
print("=== 2. Xotira o'lchovi ===\n")
def olcham(obj, n: int = 20_000) -> int:
"""Bitta obyektning haqiqiy hajmi (bayt) — tracemalloc bilan.
⚠️ sys.getsizeof(obj) + sys.getsizeof(obj.__dict__) Python 3.11+ da
NOTO'G'RI: obj.__dict__ ga murojaat qilishning o'zi lug'atni
yaratadi. Shuning uchun obyekt holatidan n ta yangi nusxa yasab,
o'rtacha o'sishni o'lchaymiz (nusxalarning __dict__ iga tegmaymiz).
"""
import tracemalloc
cls = type(obj)
holat = dict(getattr(obj, "__dict__", {}))
for S in cls.__mro__:
slotlar = getattr(S, "__slots__", ())
if isinstance(slotlar, str):
slotlar = (slotlar,)
for nom in slotlar:
if nom not in ("__dict__", "__weakref__") and hasattr(obj, nom):
holat[nom] = getattr(obj, nom)
joy = [None] * n
tracemalloc.start()
asos = tracemalloc.get_traced_memory()[0]
for i in range(n):
o = cls.__new__(cls)
for k, v in holat.items():
object.__setattr__(o, k, v)
joy[i] = o
jami = tracemalloc.get_traced_memory()[0] - asos
tracemalloc.stop()
return round(jami / n)
def sinf_yasa(n: int, slotli: bool):
nomlar = tuple(f"a{i}" for i in range(n))
tana = {"__init__": lambda self, *a: [
setattr(self, nom, q) for nom, q in zip(nomlar, a)
] and None}
if slotli:
tana["__slots__"] = nomlar
return type(f"{'S' if slotli else 'O'}{n}", (), tana)
print(f" {'Atribut':<10} {'__dict__':<14} {'__slots__':<14} "
f"{'Tejash':<10} {'1M nusxa farqi'}")
print(" " + "─" * 68)
for n in [1, 2, 3, 5, 10, 20]:
O = sinf_yasa(n, False)
S = sinf_yasa(n, True)
args = tuple(range(n))
oo, ss = olcham(O(*args)), olcham(S(*args))
tejash = (1 - ss / oo) * 100
farq = (oo - ss) * 1_000_000 / 1_048_576
print(f" {n:<10} {oo:<14} {ss:<14} {tejash:>6.0f}% "
f"{farq:>8.0f} MB")
print(f"""
⚠️ Python 3.11+ da "key-sharing dictionaries":
Bir sinfning barcha nusxalari KALITLARNI ulashadi
→ __dict__ ustama xarajati kamaydi
→ __slots__ foydasi ham kamaydi
Python versiyasi: {sys.version_info.major}.{sys.version_info.minor}
""")
print("=== 3. Haqiqiy ro'yxatda ===\n")
import tracemalloc
def olcha(Sinf, n: int = 100_000) -> float:
tracemalloc.start()
obyektlar = [Sinf(i, i * 2) for i in range(n)]
joriy, cho_qqi = tracemalloc.get_traced_memory()
tracemalloc.stop()
del obyektlar
return joriy / 1_048_576
@dataclass
class DcOddiy:
x: int
y: int
@dataclass(slots=True)
class DcSlotli:
x: int
y: int
from typing import NamedTuple
class NtNuqta(NamedTuple):
x: int
y: int
SINFLAR = [
("Oddiy sinf", Oddiy),
("__slots__", Slotli),
("dataclass", DcOddiy),
("dataclass(slots)", DcSlotli),
("NamedTuple", NtNuqta),
("tuple", lambda x, y: (x, y)),
]
print(f" 100 000 obyekt (2 atribut):\n")
print(f" {'Tur':<20} {'Xotira':>10} {'Nisbat'}")
print(" " + "─" * 42)
asos = None
for nom, Sinf in SINFLAR:
mb = olcha(Sinf)
if asos is None:
asos = mb
print(f" {nom:<20} {mb:>8.1f} MB {mb / asos:>8.2f}×")
print("\n\n=== 4. Tezlik ===\n")
o, s = Oddiy(1, 2), Slotli(1, 2)
N = 2_000_000
OLCHOVLAR = [
("Atribut o'qish",
lambda: o.x, lambda: s.x),
("Atribut yozish",
lambda: setattr(o, "x", 1), lambda: setattr(s, "x", 1)),
("Obyekt yaratish",
lambda: Oddiy(1, 2), lambda: Slotli(1, 2)),
]
print(f" {N:,} amal:\n")
print(f" {'Amal':<20} {'__dict__':>12} {'__slots__':>12} {'Farq'}")
print(" " + "─" * 56)
for nom, f_o, f_s in OLCHOVLAR:
t1 = timeit.timeit(f_o, number=N)
t2 = timeit.timeit(f_s, number=N)
farq = (t1 / t2 - 1) * 100
print(f" {nom:<20} {t1:>10.3f}s {t2:>10.3f}s "
f"{farq:>+6.0f}%")
print(f"""
⭐ Nazariyada __slots__ tezroq bo'lishi kerak:
• dict qidiruvi (xesh) o'rniga belgilangan OFFSET
• dict yaratilmaydi
⚠️ Amalda Python 3.11+ da farq bir necha foiz va ikki tomonga
ham chiqishi mumkin: ixtisoslashgan bayt-kod (PEP 659) oddiy
sinf atributlarini ham xuddi shunday tez o'qiydi. O'lchovni
bir necha marta takrorlasangiz, belgisi ham o'zgarishi mumkin.
__slots__ ni tezlik uchun tanlamang.
""")
print("=== ⚠️ 5. Cheklovlar ===\n")
s = Slotli(1, 2)
CHEKLOVLAR = [
("s.z = 3", lambda: setattr(s, "z", 3)),
("s.__dict__", lambda: s.__dict__),
("vars(s)", lambda: vars(s)),
("s.__weakref__", lambda: s.__weakref__),
]
for kod, f in CHEKLOVLAR:
try:
n = f"⚠️ {f()!r}"
except (AttributeError, TypeError) as e:
n = f"✅ {type(e).__name__}: {str(e)[:44]}"
print(f" {kod:<20} {n}")
import weakref
print(f"\n weakref:")
try:
weakref.ref(s)
print(f" weakref.ref(s) → ⚠️ o'tdi")
except TypeError as e:
print(f" weakref.ref(s) → ✅ TypeError: {e}")
class SlotliWeakref:
__slots__ = ("x", "y", "__weakref__") # ⭐
def __init__(self, x, y): self.x, self.y = x, y
sw = SlotliWeakref(1, 2)
print(f" __slots__ ga '__weakref__' qo'shildi:")
print(f" weakref.ref(sw) → ✅ {weakref.ref(sw)}")
from functools import cached_property
print(f"\n cached_property:")
class SlotliKesh:
__slots__ = ("x",)
def __init__(self, x): self.x = x
@cached_property
def kvadrat(self): return self.x ** 2
try:
SlotliKesh(5).kvadrat
except TypeError as e:
print(f" ❌ TypeError: {str(e)[:60]}")
class SlotliKeshTogri:
__slots__ = ("x", "__dict__") # ⭐ ikkalasi
def __init__(self, x): self.x = x
@cached_property
def kvadrat(self): return self.x ** 2
k = SlotliKeshTogri(5)
print(f"\n __dict__ qo'shilgan: kvadrat = {k.kvadrat}")
print(f" k.__dict__ = {k.__dict__}")
print(f" ⚠️ Lekin tejash yo'qoldi: "
f"{olcham(k)} vs {olcham(Slotli(1, 2))} bayt")
print(f"\n Sinf atributi bilan konflikt:")
try:
class Konflikt:
__slots__ = ("x",)
x = 5
except ValueError as e:
print(f" ❌ ValueError: {e}")
print(f"""
⭐ SABAB: x = 5 sinf atributi member_descriptor ni
TO'SIB QO'YARDI — slot ishlamay qolardi.
""")Natijaning muhim qismi:
=== ⭐ 1. Mexanizm ===
Slotli:
s.__dict__ = ❌ AttributeError
type(Slotli.x) = member_descriptor
⭐ member_descriptor — DATA deskriptor:
__get__ True
__set__ True
=== 2. Xotira o'lchovi ===
Atribut __dict__ __slots__ Tejash 1M nusxa farqi
────────────────────────────────────────────────────────────────────
1 80 40 50% 38 MB
3 96 56 42% 38 MB
10 160 112 30% 46 MB
=== 3. Haqiqiy ro'yxatda ===
Tur Xotira Nisbat
──────────────────────────────────────────
Oddiy sinf 15.3 MB 1.00×
__slots__ 11.4 MB 0.75×
dataclass(slots) 11.4 MB 0.75×
NamedTuple 13.7 MB 0.90×
=== ⚠️ 5. Cheklovlar ===
s.z = 3 ✅ AttributeError: 'Slotli' object has no
attribute 'z'
weakref.ref(s) → ✅ TypeError: cannot create weak referenceNima ko'rsatdi: 2.1, 2.3, 2.5, 2.6-bo'limlar.
Misol 2 — Meros qoidalari
"""__slots__ va meros — murakkab qoidalar."""
import sys
print("=== ⚠️ 1. Avlodda __slots__ yo'q ===\n")
class Asos:
__slots__ = ("x", "y")
def __init__(self, x, y): self.x, self.y = x, y
class AvlodSlotsiz(Asos):
"""⚠️ __slots__ YO'Q → __dict__ qaytadi."""
pass
class AvlodBosh(Asos):
"""✅ Bo'sh __slots__ — tejash saqlanadi."""
__slots__ = ()
class AvlodYangi(Asos):
"""✅ Faqat YANGI nomlar."""
__slots__ = ("z",)
def __init__(self, x, y, z):
super().__init__(x, y)
self.z = z
def olcham(obj, n: int = 20_000) -> int:
"""Bitta obyektning haqiqiy hajmi (bayt) — tracemalloc bilan.
⚠️ sys.getsizeof(obj) + sys.getsizeof(obj.__dict__) Python 3.11+ da
NOTO'G'RI: obj.__dict__ ga murojaat qilishning o'zi lug'atni
yaratadi. Shuning uchun obyekt holatidan n ta yangi nusxa yasab,
o'rtacha o'sishni o'lchaymiz (nusxalarning __dict__ iga tegmaymiz).
"""
import tracemalloc
cls = type(obj)
holat = dict(getattr(obj, "__dict__", {}))
for S in cls.__mro__:
slotlar = getattr(S, "__slots__", ())
if isinstance(slotlar, str):
slotlar = (slotlar,)
for nom in slotlar:
if nom not in ("__dict__", "__weakref__") and hasattr(obj, nom):
holat[nom] = getattr(obj, nom)
joy = [None] * n
tracemalloc.start()
asos = tracemalloc.get_traced_memory()[0]
for i in range(n):
o = cls.__new__(cls)
for k, v in holat.items():
object.__setattr__(o, k, v)
joy[i] = o
jami = tracemalloc.get_traced_memory()[0] - asos
tracemalloc.stop()
return round(jami / n)
print(f" {'Sinf':<18} {'__dict__':<12} {'Yangi atribut':<18} "
f"{'Bayt'}")
print(" " + "─" * 62)
for Sinf, args in [(Asos, (1, 2)), (AvlodSlotsiz, (1, 2)),
(AvlodBosh, (1, 2)), (AvlodYangi, (1, 2, 3))]:
o = Sinf(*args)
d = "✅ bor" if hasattr(o, "__dict__") else "❌ yo'q"
try:
o.qoshimcha = 1
y = "⚠️ mumkin"
except AttributeError:
y = "✅ to'sildi"
print(f" {Sinf.__name__:<18} {d:<12} {y:<18} {olcham(o)}")
print(f"""
⭐ QOIDA: HAR avlodda __slots__ yozing.
Bo'sh bo'lsa ham: __slots__ = ()
⚠️ Aks holda __dict__ qaytadi va tejash YO'QOLADI.
""")
print("=== ⚠️ 2. Takrorlash — behuda xotira ===\n")
class Takrorli(Asos):
__slots__ = ("x", "y", "z") # ⚠️ x, y takror
def __init__(self, x, y, z):
super().__init__(x, y)
self.z = z
class Togri(Asos):
__slots__ = ("z",) # ✅ faqat yangi
def __init__(self, x, y, z):
super().__init__(x, y)
self.z = z
print(f" {'Sinf':<14} {'__slots__':<20} {'Jami slotlar':<14} {'Bayt'}")
print(" " + "─" * 62)
for Sinf in (Togri, Takrorli):
o = Sinf(1, 2, 3)
jami = sum(len(getattr(S, "__slots__", ()))
for S in Sinf.__mro__)
print(f" {Sinf.__name__:<14} {str(Sinf.__slots__):<20} "
f"{jami:<14} {olcham(o)}")
print(f"\n ⭐ Takrorli da x va y IKKI marta joy egallaydi")
print(f" (ota-sinfda va avlodda)")
t = Takrorli(1, 2, 3)
print(f"\n ⚠️ Asos.__init__ ichidagi self.x = x qaysi slotga yozdi?")
print(f" Takrorli.x.__get__(t, Takrorli) = "
f"{Takrorli.x.__get__(t, Takrorli)} ⭐ avlod sloti")
try:
qiymat = Asos.x.__get__(t, Takrorli)
except AttributeError as e:
qiymat = f"AttributeError: {e}"
print(f" Asos.x.__get__(t, Takrorli) = {qiymat}")
print(f" t.x = {t.x}")
print(f"""
⭐ self.x = x MRO bo'yicha BIRINCHI topilgan deskriptorga yozadi —
bu Takrorli.x. Ota-sinfning Asos.x sloti hech qachon to'ldirilmaydi:
u xotirada joy egallaydi, lekin doim BO'SH qoladi.
""")
print("\n\n=== ⚠️ 3. __dict__ bo'lgan ota-sinf ===\n")
class OtaDict:
"""__slots__ YO'Q → __dict__ bor."""
def __init__(self, a): self.a = a
class BolaSlots(OtaDict):
"""⚠️ __slots__ FOYDASIZ — __dict__ meros olinadi."""
__slots__ = ("b",)
def __init__(self, a, b):
super().__init__(a)
self.b = b
b = BolaSlots(1, 2)
print(f" class OtaDict: (slots yo'q)")
print(f" class BolaSlots(OtaDict): __slots__ = ('b',)\n")
print(f" b.__dict__ = {b.__dict__}")
print(f" b.qoshimcha = 1 → ", end="")
b.qoshimcha = 1
print(f"✅ mumkin (⚠️)")
print(f" b.__dict__ = {b.__dict__}")
print(f" Bayt: {olcham(b)}")
print(f"""
⚠️ __slots__ FAQAT butun ierarxiya bo'ylab ishlaganda foydali.
object → A(slots) → B(slots) → C(slots) ✅ tejash
object → A(dict) → B(slots) ❌ foydasiz
""")
print("=== ⚠️ 4. Ko'p meros ===\n")
class A1:
__slots__ = ("x",)
class A2:
__slots__ = ("y",)
class B1:
__slots__ = () # ⭐ bo'sh
class B2:
__slots__ = ()
HOLATLAR = [
("class C(A1, A2)", (A1, A2)),
("class C(A1, B1)", (A1, B1)),
("class C(B1, B2)", (B1, B2)),
("class C(A1, B1, B2)", (A1, B1, B2)),
]
for tavsif, otalar in HOLATLAR:
try:
C = type("C", otalar, {"__slots__": ()})
n = "✅ yaratildi"
except TypeError as e:
n = f"❌ TypeError: {str(e)[:44]}"
print(f" {tavsif:<24} {n}")
print(f"""
⭐ QOIDA: ko'p merosda FAQAT BITTA ota-sinfda
bo'sh bo'lmagan __slots__ bo'lishi mumkin.
⚠️ SABAB: har slot obyektda ANIQ OFFSET egallaydi.
Ikki ota-sinf bir offsetni talab qilsa — konflikt.
✅ MIXIN lar uchun __slots__ = () yozing:
""")
class JurnalMixin:
__slots__ = () # ⭐ mixin
def yoz(self, x): print(f" [jurnal] {x}")
class KeshMixin:
__slots__ = () # ⭐
def kesh_ol(self, k): return None
class Xizmat(JurnalMixin, KeshMixin):
__slots__ = ("nom", "port")
def __init__(self, nom, port):
self.nom, self.port = nom, port
x = Xizmat("api", 8000)
print(f" Xizmat(JurnalMixin, KeshMixin): ✅ yaratildi")
print(f" x.nom = {x.nom}, bayt = {olcham(x)}")
print(f" __dict__ bormi: {hasattr(x, '__dict__')}")
x.yoz("test")
print("\n\n=== 5. Chuqur ierarxiya ===\n")
class D1:
__slots__ = ("a",)
class D2(D1):
__slots__ = ("b",)
class D3(D2):
__slots__ = ("c",)
class D4(D3):
__slots__ = ("d",)
d = D4()
d.a, d.b, d.c, d.d = 1, 2, 3, 4
print(f" D1(a) → D2(b) → D3(c) → D4(d)\n")
print(f" d.a, d.b, d.c, d.d = {d.a}, {d.b}, {d.c}, {d.d}")
print(f" __dict__ bormi: {hasattr(d, '__dict__')}")
print(f" Bayt: {olcham(d)}")
print(f"\n Barcha slotlar:")
for S in D4.__mro__:
sl = getattr(S, "__slots__", None)
if sl is not None:
print(f" {S.__name__:<10} {sl}")
def barcha_slotlar(Sinf) -> tuple[str, ...]:
"""⭐ MRO bo'ylab barcha slotlarni yig'adi."""
natija = []
for S in reversed(Sinf.__mro__):
sl = getattr(S, "__slots__", ())
if isinstance(sl, str):
sl = (sl,)
natija.extend(s for s in sl if s not in natija
and not s.startswith("__"))
return tuple(natija)
print(f"\n barcha_slotlar(D4) = {barcha_slotlar(D4)}")
print("\n\n=== 6. Pickle va copy ===\n")
import pickle
import copy
class Slotli2:
__slots__ = ("x", "y")
def __init__(self, x, y): self.x, self.y = x, y
def __repr__(self): return f"Slotli2({self.x}, {self.y})"
def __eq__(self, b):
return (isinstance(b, Slotli2)
and (self.x, self.y) == (b.x, b.y))
s = Slotli2(1, [2, 3])
print(f" s = {s}\n")
AMALLAR = [
("pickle (protokol 2)",
lambda: pickle.loads(pickle.dumps(s, protocol=2))),
("pickle (sukut)",
lambda: pickle.loads(pickle.dumps(s))),
("copy.copy", lambda: copy.copy(s)),
("copy.deepcopy", lambda: copy.deepcopy(s)),
]
for nom, f in AMALLAR:
try:
n = f()
print(f" {nom:<24} ✅ {n} (teng: {n == s})")
except Exception as e:
print(f" {nom:<24} ❌ {type(e).__name__}: {str(e)[:36]}")
print(f"\n ⭐ __getstate__/__setstate__ bilan nazorat:")
class Nazoratli:
__slots__ = ("x", "_kesh")
def __init__(self, x):
self.x = x
self._kesh = {"qimmat": "hisoblangan"}
def __getstate__(self):
"""⭐ _kesh saqlanmaydi."""
return {"x": self.x}
def __setstate__(self, holat):
self.x = holat["x"]
self._kesh = {} # ⭐ qayta yaratiladi
def __repr__(self):
return f"Nazoratli({self.x}, kesh={self._kesh})"
n = Nazoratli(42)
print(f" Asl: {n}")
n2 = pickle.loads(pickle.dumps(n))
print(f" Pickle'dan: {n2}")
print(f" ⭐ _kesh saqlanmadi, qayta yaratildi")
print("\n\n=== 7. Tashxis vositasi ===\n")
def slots_tahlil(Sinf: type) -> dict:
"""__slots__ holatini tahlil qiladi."""
ozi = getattr(Sinf, "__slots__", None)
if isinstance(ozi, str):
ozi = (ozi,)
# __dict__ bormi
dict_bor = any(
"__dict__" in getattr(S, "__slots__", ())
or (S is not object and not hasattr(S, "__slots__"))
for S in Sinf.__mro__
)
# Takrorlar
korilgan: dict[str, list[str]] = {}
for S in Sinf.__mro__:
sl = getattr(S, "__slots__", ())
if isinstance(sl, str):
sl = (sl,)
for s in sl:
korilgan.setdefault(s, []).append(S.__name__)
takrorlar = {k: v for k, v in korilgan.items() if len(v) > 1}
muammolar = []
if ozi is None:
muammolar.append("__slots__ yo'q")
if dict_bor and ozi is not None:
muammolar.append("__dict__ bor — tejash yo'q")
if takrorlar:
muammolar.append(f"takrorlangan slotlar: {sorted(takrorlar)}")
if "__weakref__" not in korilgan and ozi is not None and not dict_bor:
muammolar.append("weakref ishlamaydi")
return {
"ozi": ozi,
"jami": sorted(korilgan),
"dict_bor": dict_bor,
"takrorlar": takrorlar,
"muammolar": muammolar,
}
SINFLAR = [Asos, AvlodSlotsiz, AvlodBosh, Takrorli, BolaSlots, D4,
Xizmat, SlotliWeakref if "SlotliWeakref" in dir() else Asos]
for Sinf in SINFLAR[:7]:
n = slots_tahlil(Sinf)
print(f" {Sinf.__name__}:")
print(f" O'z slotlari: {n['ozi']}")
print(f" Jami: {[s for s in n['jami'] if not s.startswith('__')]}")
if n["muammolar"]:
for m in n["muammolar"]:
print(f" ⚠️ {m}")
else:
print(f" ✅ muammo yo'q")
print()Natijaning muhim qismi:
=== ⚠️ 1. Avlodda __slots__ yo'q ===
Sinf __dict__ Yangi atribut Bayt
──────────────────────────────────────────────────────────────
Asos ❌ yo'q ✅ to'sildi 48
AvlodSlotsiz ✅ bor ⚠️ mumkin 96
AvlodBosh ❌ yo'q ✅ to'sildi 48
AvlodYangi ❌ yo'q ✅ to'sildi 56
=== ⚠️ 4. Ko'p meros ===
class C(A1, A2) ❌ TypeError: multiple bases have instance lay-out conflic
class C(A1, B1) ✅ yaratildi
class C(B1, B2) ✅ yaratildi
=== 6. Pickle va copy ===
pickle (protokol 2) ✅ Slotli2(1, [2, 3]) (teng: True)
copy.deepcopy ✅ Slotli2(1, [2, 3]) (teng: True)Nima ko'rsatdi: 2.3, 2.4, 2.7-bo'limlar.
Misol 3 — Qachon kerak, qachon kerak emas
"""__slots__ ni to'g'ri qo'llash."""
from __future__ import annotations
import sys
import time
import tracemalloc
from dataclasses import dataclass, field
from typing import NamedTuple
print("=== ✅ 1. Kerak: ko'p ma'lumot obyekti ===\n")
class Yozuv:
"""⭐ Jurnal yozuvi — millionlab bo'lishi mumkin."""
__slots__ = ("vaqt", "daraja", "xabar", "manba")
def __init__(self, vaqt: float, daraja: str, xabar: str,
manba: str = ""):
self.vaqt = vaqt
self.daraja = daraja
self.xabar = xabar
self.manba = manba
def __repr__(self):
return f"Yozuv({self.daraja}, {self.xabar[:20]!r})"
class YozuvOddiy:
def __init__(self, vaqt, daraja, xabar, manba=""):
self.vaqt = vaqt
self.daraja = daraja
self.xabar = xabar
self.manba = manba
def olcha(Sinf, n: int) -> tuple[float, float]:
tracemalloc.start()
b = time.perf_counter()
obyektlar = [
Sinf(i * 0.001, "INFO", f"Xabar raqami {i}", "modul.py")
for i in range(n)
]
vaqt = time.perf_counter() - b
joriy, _ = tracemalloc.get_traced_memory()
tracemalloc.stop()
del obyektlar
return joriy / 1_048_576, vaqt
N = 200_000
print(f" {N:,} jurnal yozuvi:\n")
print(f" {'Tur':<20} {'Xotira':>10} {'Vaqt':>10} {'Nisbat'}")
print(" " + "─" * 52)
m1, t1 = olcha(YozuvOddiy, N)
m2, t2 = olcha(Yozuv, N)
print(f" {'Oddiy sinf':<20} {m1:>8.1f} MB {t1:>8.3f}s 1.00×")
print(f" {'__slots__':<20} {m2:>8.1f} MB {t2:>8.3f}s {m2/m1:>4.2f}×")
print(f"\n ⭐ Tejash: {m1 - m2:.1f} MB ({(1 - m2/m1) * 100:.0f}%)")
print("\n\n=== ❌ 2. Kerak emas: kam obyekt ===\n")
print(""" ⚠️ Bu holatlarda __slots__ FOYDASIZ:
class Sozlama: # 1 nusxa
__slots__ = (...) ⚠️ 100 bayt tejaldi
class Xizmat: # 5-10 nusxa
__slots__ = (...) ⚠️ 1 KB tejaldi
class Ulanish: # 50 nusxa
__slots__ = (...) ⚠️ 5 KB tejaldi
⭐ MEZON: 100 000 dan kam obyekt bo'lsa —
tejash sezilmaydi, lekin cheklovlar qoladi.
""")
print(f" Tejash miqdori (~40 bayt/obyekt, 2.5-bo'lim jadvali):\n")
print(f" {'Obyektlar soni':<20} {'Tejash'}")
print(" " + "─" * 36)
for n, izoh in [(1, "sozlama"), (10, "xizmatlar"),
(1_000, "foydalanuvchilar"),
(100_000, "yozuvlar"), (10_000_000, "nuqtalar")]:
tejash = n * 40
if tejash < 1024:
s = f"{tejash} bayt"
elif tejash < 1_048_576:
s = f"{tejash / 1024:.1f} KB"
else:
s = f"{tejash / 1_048_576:.1f} MB"
belgi = "⚠️" if n < 100_000 else "✅"
print(f" {f'{n:,} ({izoh})':<20} {s:<12} {belgi}")
print("\n\n=== ✅ 3. Kerak: atributlarni cheklash ===\n")
class SozlamaQatiy:
"""⭐ __slots__ — xato nomni to'sadi."""
__slots__ = ("host", "port", "debug", "vaqt_chegarasi")
def __init__(self, host="localhost", port=8000, debug=False,
vaqt_chegarasi=30.0):
self.host = host
self.port = port
self.debug = debug
self.vaqt_chegarasi = vaqt_chegarasi
class SozlamaOddiy:
def __init__(self, host="localhost", port=8000, debug=False,
vaqt_chegarasi=30.0):
self.host = host
self.port = port
self.debug = debug
self.vaqt_chegarasi = vaqt_chegarasi
print(f" Xato yozilgan atribut nomi:\n")
for Sinf in (SozlamaOddiy, SozlamaQatiy):
s = Sinf()
try:
s.pott = 9000 # ⚠️ port emas, pott
n = "⚠️ o'tdi — xato JIMGINA"
except AttributeError as e:
n = f"✅ AttributeError: {e}"
print(f" {Sinf.__name__:<16} s.pott = 9000 → {n}")
s = SozlamaOddiy()
s.pott = 9000
print(f"\n SozlamaOddiy: s.port = {s.port} ⚠️ o'zgarmadi!")
print(f" s.pott = {s.pott} ⚠️ yangi atribut")
print(f"""
⭐ __slots__ — XATO NOMLARDAN himoya:
• Konfiguratsiya sinflari
• Ma'lumot modellari
• API javoblari
• Holat mashinalari
⚠️ Muqobil: __setattr__ yoki frozen dataclass
""")
print("=== ⚠️ 4. Ishlamaydigan holatlar ===\n")
from functools import cached_property
import weakref
MUAMMOLAR = [
("cached_property", """
class A:
__slots__ = ("x",)
@cached_property
def y(self): ... ❌ TypeError
✅ __slots__ = ("x", "__dict__")
yoki oddiy @property"""),
("weakref", """
weakref.ref(obj) ❌ TypeError
✅ __slots__ = ("x", "__weakref__")
yoki @dataclass(slots=True, weakref_slot=True)"""),
("Dinamik atributlar", """
obj.yangi = 1 ❌ AttributeError
✅ __slots__ = ("x", "__dict__")
yoki umuman __slots__ ishlatmang"""),
("Ko'p meros", """
class C(A_slots, B_slots) ❌ TypeError
✅ Mixin larda __slots__ = ()"""),
("Sinf atributi sukuti", """
class A:
__slots__ = ("x",)
x = 5 ❌ ValueError
✅ __init__ da o'rnating"""),
("Monkey patching", """
obj.yangi_metod = f ❌ AttributeError
✅ Sinfga qo'shing: A.yangi_metod = f"""),
]
for nom, izoh in MUAMMOLAR:
print(f" ⚠️ {nom}:{izoh}\n")
print("=== 5. Muqobillar ===\n")
@dataclass
class DcOddiy:
x: int
y: int
z: int
@dataclass(slots=True)
class DcSlots:
x: int
y: int
z: int
@dataclass(frozen=True, slots=True)
class DcFrozen:
x: int
y: int
z: int
class NtNuqta(NamedTuple):
x: int
y: int
z: int
class SlotsQolda:
__slots__ = ("x", "y", "z")
def __init__(self, x, y, z):
self.x, self.y, self.z = x, y, z
VARIANTLAR = [
("dataclass", DcOddiy),
("dataclass(slots)", DcSlots),
("dataclass(frozen+slots)", DcFrozen),
("NamedTuple", NtNuqta),
("Qo'lda __slots__", SlotsQolda),
("tuple", lambda x, y, z: (x, y, z)),
("dict", lambda x, y, z: {"x": x, "y": y, "z": z}),
]
def olcha2(Sinf, n=100_000):
tracemalloc.start()
o = [Sinf(i, i, i) for i in range(n)]
joriy, _ = tracemalloc.get_traced_memory()
tracemalloc.stop()
del o
return joriy / 1_048_576
print(f" 100 000 obyekt (3 atribut):\n")
print(f" {'Variant':<26} {'Xotira':>10} {'O`zgaruvchan':<14} "
f"{'Nom bilan'}")
print(" " + "─" * 64)
for nom, Sinf in VARIANTLAR:
mb = olcha2(Sinf)
o = Sinf(1, 2, 3)
try:
if isinstance(o, dict):
o["x"] = 9
else:
o.x = 9
ozg = "✅"
except (AttributeError, TypeError):
ozg = "❌"
nomli = "✅" if hasattr(o, "x") or isinstance(o, dict) else "❌"
print(f" {nom:<26} {mb:>8.1f} MB {ozg:<14} {nomli}")
print(f"""
⭐ TANLASH:
Eng kam xotira → dataclass(slots=True) / __slots__
O'zgarmas, indeks/ochib olish → NamedTuple (xotira uchun EMAS)
Kam xotira + hashable → dataclass(frozen=True, slots=True)
Moslashuvchan → oddiy dataclass
Dinamik kalitlar → dict
""")
print("=== ⭐ 6. Amaliy: zarrachalar tizimi ===\n")
class Zarracha:
"""⭐ Millionlab zarracha — __slots__ zarur."""
__slots__ = ("x", "y", "vx", "vy", "umr", "rang")
def __init__(self, x: float, y: float, vx: float = 0.0,
vy: float = 0.0, umr: float = 1.0,
rang: int = 0xFFFFFF):
self.x, self.y = x, y
self.vx, self.vy = vx, vy
self.umr = umr
self.rang = rang
def yangila(self, dt: float, tortishish: float = -9.81) -> bool:
"""Holatni yangilaydi. Tirikligini qaytaradi."""
self.x += self.vx * dt
self.y += self.vy * dt
self.vy += tortishish * dt
self.umr -= dt
return self.umr > 0
def __repr__(self):
return (f"Zarracha({self.x:.1f}, {self.y:.1f}, "
f"umr={self.umr:.2f})")
class ZarrachaTizimi:
"""⭐ O'zi ko'p emas — __slots__ shart emas, lekin foydali."""
__slots__ = ("_zarrachalar", "tortishish", "_yaratilgan")
def __init__(self, tortishish: float = -9.81):
self._zarrachalar: list[Zarracha] = []
self.tortishish = tortishish
self._yaratilgan = 0
def qosh(self, n: int, **kw) -> None:
import random
for _ in range(n):
self._zarrachalar.append(Zarracha(
x=kw.get("x", 0.0),
y=kw.get("y", 0.0),
vx=random.uniform(-5, 5),
vy=random.uniform(0, 10),
umr=random.uniform(0.5, 2.0),
))
self._yaratilgan += n
def yangila(self, dt: float) -> int:
"""Barchasini yangilaydi, o'lganlarni olib tashlaydi."""
self._zarrachalar = [
z for z in self._zarrachalar if z.yangila(dt, self.tortishish)
]
return len(self._zarrachalar)
def __len__(self):
return len(self._zarrachalar)
def statistika(self) -> dict:
if not self._zarrachalar:
return {"tirik": 0, "yaratilgan": self._yaratilgan}
return {
"tirik": len(self._zarrachalar),
"yaratilgan": self._yaratilgan,
"ortacha_umr": sum(z.umr for z in self._zarrachalar)
/ len(self._zarrachalar),
"eng_baland": max(z.y for z in self._zarrachalar),
}
import random
random.seed(42)
t = ZarrachaTizimi()
t.qosh(500_000)
print(f" 500 000 zarracha yaratildi\n")
tracemalloc.start()
t2 = ZarrachaTizimi()
t2.qosh(200_000)
joriy, _ = tracemalloc.get_traced_memory()
tracemalloc.stop()
print(f" 200 000 zarracha xotirasi: {joriy / 1_048_576:.1f} MB")
print(f"\n Simulyatsiya:")
print(f" {'Qadam':<8} {'Tirik':<12} {'Vaqt':<10} {'Eng baland'}")
print(" " + "─" * 46)
for qadam in range(1, 6):
b = time.perf_counter()
tirik = t.yangila(0.2)
vaqt = time.perf_counter() - b
s = t.statistika()
baland = s.get("eng_baland", 0)
print(f" {qadam:<8} {tirik:<12,} {vaqt:>7.3f}s "
f"{baland:>8.2f}")
print(f"\n Yakuniy: {t.statistika()}")
print(f"""
⭐ NEGA BU YERDA __slots__ ZARUR:
• 500 000 zarracha × 6 atribut
• __dict__ bilan: ~57 MB (har biri ~120 bayt)
• __slots__ bilan: ~38 MB (har biri ~80 bayt)
• Atribut kirishi tez-tez (har qadamda 6 × N)
⭐ ZarrachaTizimi da ham __slots__ bor, lekin
bu tejash uchun emas — XATO NOMLARDAN himoya uchun.
""")Natijaning muhim qismi:
=== ✅ 1. Kerak: ko'p ma'lumot obyekti ===
200,000 jurnal yozuvi:
Tur Xotira Vaqt Nisbat
────────────────────────────────────────────────────
Oddiy sinf 37.3 MB 0.764s 1.00×
__slots__ 29.7 MB 0.775s 0.80×
⭐ Tejash: 7.6 MB (20%)
=== ✅ 3. Kerak: atributlarni cheklash ===
SozlamaOddiy s.pott = 9000 → ⚠️ o'tdi — xato JIMGINA
SozlamaQatiy s.pott = 9000 → ✅ AttributeError
=== 5. Muqobillar ===
Variant Xotira O`zgaruvchan Nom bilan
────────────────────────────────────────────────────────────────
dataclass 13.0 MB ✅ ✅
dataclass(slots) 9.1 MB ✅ ✅
NamedTuple 11.4 MB ❌ ✅
tuple 10.7 MB ❌ ❌Nima ko'rsatdi: 2.3, 2.5, 2.7-bo'limlar.
Misol 4 — Amaliy: geometrik dvigatel
"""__slots__ bilan ishlash tizimi."""
from __future__ import annotations
import math
import sys
import time
import tracemalloc
import weakref
from dataclasses import dataclass, field
from typing import Iterator, ClassVar
print("=== GEOMETRIK DVIGATEL ===\n")
class Vektor:
"""⭐ Eng ko'p yaratiladigan obyekt — __slots__ zarur."""
__slots__ = ("x", "y")
def __init__(self, x: float = 0.0, y: float = 0.0):
self.x = float(x)
self.y = float(y)
# ── Arifmetika (yangi obyektlar) ──
def __add__(self, b):
if not isinstance(b, Vektor): return NotImplemented
return Vektor(self.x + b.x, self.y + b.y)
def __sub__(self, b):
if not isinstance(b, Vektor): return NotImplemented
return Vektor(self.x - b.x, self.y - b.y)
def __mul__(self, n):
if isinstance(n, Vektor):
return self.x * n.x + self.y * n.y # skalyar ko'paytma
if isinstance(n, (int, float)):
return Vektor(self.x * n, self.y * n)
return NotImplemented
__rmul__ = __mul__
def __truediv__(self, n):
if not isinstance(n, (int, float)): return NotImplemented
if n == 0: raise ZeroDivisionError("Vektor nolga bo'lindi")
return Vektor(self.x / n, self.y / n)
def __neg__(self): return Vektor(-self.x, -self.y)
def __abs__(self): return math.hypot(self.x, self.y)
# ── Joyida o'zgartirish (tez) ──
def __iadd__(self, b):
"""⭐ Yangi obyekt yaratmaydi."""
if not isinstance(b, Vektor): return NotImplemented
self.x += b.x
self.y += b.y
return self
def __imul__(self, n):
if not isinstance(n, (int, float)): return NotImplemented
self.x *= n
self.y *= n
return self
# ── Boshqalar ──
def __eq__(self, b):
return (isinstance(b, Vektor)
and math.isclose(self.x, b.x)
and math.isclose(self.y, b.y))
def __hash__(self): return hash((round(self.x, 9), round(self.y, 9)))
def __iter__(self): return iter((self.x, self.y))
def __getitem__(self, i): return (self.x, self.y)[i]
def __len__(self): return 2
def __bool__(self): return bool(self.x or self.y)
def __repr__(self): return f"Vektor({self.x:g}, {self.y:g})"
def __str__(self): return f"({self.x:g}, {self.y:g})"
def __format__(self, spec):
if spec == "u": return f"{abs(self):.4f}"
if spec == "burchak": return f"{math.degrees(self.burchak):.1f}°"
if not spec: return str(self)
return f"({format(self.x, spec)}, {format(self.y, spec)})"
# ── Xususiyatlar (property — cached_property ISHLAMAYDI) ──
@property
def uzunlik(self) -> float:
return math.hypot(self.x, self.y)
@property
def burchak(self) -> float:
return math.atan2(self.y, self.x)
def normalla(self) -> "Vektor":
u = self.uzunlik
if u == 0: raise ValueError("Nol vektorni normallashtirib bo'lmaydi")
return Vektor(self.x / u, self.y / u)
def aylantir(self, burchak: float) -> "Vektor":
c, s = math.cos(burchak), math.sin(burchak)
return Vektor(self.x * c - self.y * s, self.x * s + self.y * c)
# ── Fabrikalar ──
@classmethod
def qutbdan(cls, uzunlik: float, burchak: float) -> "Vektor":
return cls(uzunlik * math.cos(burchak), uzunlik * math.sin(burchak))
@classmethod
def nol(cls) -> "Vektor": return cls(0, 0)
# ── Pickle uchun ──
def __getstate__(self): return (self.x, self.y)
def __setstate__(self, holat): self.x, self.y = holat
class Jism:
"""⭐ Fizik jism — ko'p bo'ladi."""
__slots__ = ("joy", "tezlik", "massa", "_id", "__weakref__")
_keyingi_id: ClassVar[int] = 1
_tiriklar: ClassVar[weakref.WeakSet] = weakref.WeakSet()
def __init__(self, joy: Vektor, tezlik: Vektor | None = None,
massa: float = 1.0):
if massa <= 0:
raise ValueError(f"Massa musbat bo'lsin: {massa}")
self.joy = joy
self.tezlik = tezlik if tezlik is not None else Vektor.nol()
self.massa = float(massa)
self._id = Jism._keyingi_id
Jism._keyingi_id += 1
Jism._tiriklar.add(self) # ⭐ __weakref__ kerak
@property
def id(self) -> int: return self._id
@property
def impuls(self) -> Vektor: return self.tezlik * self.massa
@property
def kinetik_energiya(self) -> float:
return 0.5 * self.massa * self.tezlik.uzunlik ** 2
def kuch_qoll(self, kuch: Vektor, dt: float) -> None:
"""⭐ Joyida o'zgartirish — yangi obyekt yaratilmaydi."""
self.tezlik += kuch / self.massa * dt
def yangila(self, dt: float) -> None:
self.joy += self.tezlik * dt
def __repr__(self):
return (f"Jism(#{self._id}, {self.joy}, m={self.massa:g})")
class Dunyo:
"""Simulyatsiya konteyneri."""
__slots__ = ("_jismlar", "tortishish", "_qadam")
def __init__(self, tortishish: Vektor | None = None):
self._jismlar: list[Jism] = []
self.tortishish = tortishish or Vektor(0, -9.81)
self._qadam = 0
def qosh(self, *jismlar: Jism) -> "Dunyo":
self._jismlar.extend(jismlar)
return self
def yangila(self, dt: float) -> None:
for j in self._jismlar:
j.kuch_qoll(self.tortishish * j.massa, dt)
j.yangila(dt)
if j.joy.y < 0: # yerga urildi
j.joy.y = 0.0
j.tezlik.y = -j.tezlik.y * 0.7 # elastiklik
self._qadam += 1
def __len__(self): return len(self._jismlar)
def __iter__(self) -> Iterator[Jism]: return iter(self._jismlar)
@property
def jami_energiya(self) -> float:
return sum(j.kinetik_energiya
+ j.massa * abs(self.tortishish.y) * j.joy.y
for j in self._jismlar)
def statistika(self) -> dict:
if not self._jismlar:
return {"jismlar": 0}
return {
"jismlar": len(self._jismlar),
"qadam": self._qadam,
"energiya": round(self.jami_energiya, 2),
"eng_baland": round(max(j.joy.y for j in self._jismlar), 2),
"eng_tez": round(max(j.tezlik.uzunlik
for j in self._jismlar), 2),
}
print("1. Vektor amallari:\n")
v1, v2 = Vektor(3, 4), Vektor(1, 2)
AMALLAR = [
("v1", v1), ("v2", v2),
("v1 + v2", v1 + v2),
("v1 - v2", v1 - v2),
("v1 * 2", v1 * 2),
("2 * v1", 2 * v1),
("v1 * v2", v1 * v2),
("v1 / 2", v1 / 2),
("-v1", -v1),
("abs(v1)", abs(v1)),
("v1.uzunlik", v1.uzunlik),
("v1.normalla()", v1.normalla()),
("v1.aylantir(pi/2)", v1.aylantir(math.pi / 2)),
("Vektor.qutbdan(5, 0)", Vektor.qutbdan(5, 0)),
("tuple(v1)", tuple(v1)),
("v1[0]", v1[0]),
("len(v1)", len(v1)),
("bool(Vektor.nol())", bool(Vektor.nol())),
("f'{v1:u}'", f"{v1:u}"),
("f'{v1:burchak}'", f"{v1:burchak}"),
("f'{v1:.2f}'", f"{v1:.2f}"),
]
for kod, natija in AMALLAR:
print(f" {kod:<24} → {natija!r}")
print("\n\n2. ⭐ Xotira:\n")
class VektorOddiy:
def __init__(self, x, y): self.x, self.y = x, y
def olcha(Sinf, n=300_000):
tracemalloc.start()
o = [Sinf(i, i) for i in range(n)]
joriy, _ = tracemalloc.get_traced_memory()
tracemalloc.stop()
del o
return joriy / 1_048_576
N = 300_000
m1, m2 = olcha(VektorOddiy, N), olcha(Vektor, N)
print(f" {N:,} vektor:")
print(f" Oddiy sinf: {m1:.1f} MB")
print(f" __slots__: {m2:.1f} MB")
print(f" ⭐ Tejash: {m1 - m2:.1f} MB ({(1 - m2/m1)*100:.0f}%)")
print(f"\n Bitta obyekt:")
print(f" VektorOddiy: ~{m1 * 1_048_576 / N:.0f} bayt (ro'yxat ko'rsatkichi bilan)")
print(f" Vektor: ~{m2 * 1_048_576 / N:.0f} bayt")
print("\n\n3. Tezlik: += vs +\n")
v = Vektor(0, 0)
d = Vektor(1, 1)
N2 = 500_000
t1 = time.perf_counter()
a = Vektor(0, 0)
for _ in range(N2):
a = a + d # yangi obyekt
t_yangi = time.perf_counter() - t1
t2 = time.perf_counter()
b = Vektor(0, 0)
for _ in range(N2):
b += d # ⭐ joyida
t_joyida = time.perf_counter() - t2
print(f" {N2:,} qo'shish:")
print(f" a = a + d (yangi): {t_yangi:.3f}s")
print(f" b += d (joyida): {t_joyida:.3f}s")
print(f" ⭐ {t_yangi / t_joyida:.1f}× tezroq")
print(f" Natijalar teng: {a == b}")
print("\n\n4. ⭐ __weakref__ bilan kuzatuv:\n")
j1 = Jism(Vektor(0, 10), Vektor(5, 0), massa=2.0)
j2 = Jism(Vektor(0, 20), Vektor(-3, 0), massa=1.5)
j3 = Jism(Vektor(10, 5), massa=0.5)
print(f" Yaratildi: {len(Jism._tiriklar)} jism")
for j in sorted(Jism._tiriklar, key=lambda x: x.id):
print(f" {j} impuls={j.impuls}, Ek={j.kinetik_energiya:.2f}")
del j3
import gc; gc.collect()
print(f"\n del j3 → {len(Jism._tiriklar)} jism ⚠️ tozalanmadi!")
print(f" Sabab: tsikl o'zgaruvchisi j hali ham oxirgi obyektga —")
print(f" aynan j3 ga — ishora qiladi (tsikldan keyin ham yashaydi).")
del j
gc.collect()
print(f"\n del j → {len(Jism._tiriklar)} jism ✅ WeakSet tozalandi")
print(f"\n ⚠️ __slots__ ga '__weakref__' qo'shilmasa:")
print(f" Jism.__slots__ = {Jism.__slots__}")
print(f" → weakref.WeakSet ishlamasdi")
print("\n\n5. Simulyatsiya:\n")
import random
random.seed(7)
d = Dunyo()
for _ in range(50_000):
d.qosh(Jism(
joy=Vektor(random.uniform(-50, 50), random.uniform(10, 100)),
tezlik=Vektor(random.uniform(-10, 10), random.uniform(-5, 5)),
massa=random.uniform(0.5, 5.0),
))
print(f" {len(d):,} jism\n")
print(f" {'Qadam':<8} {'Vaqt':<10} {'Energiya':<16} {'Eng baland':<12} "
f"{'Eng tez'}")
print(" " + "─" * 58)
for qadam in range(1, 6):
b = time.perf_counter()
d.yangila(0.05)
vaqt = time.perf_counter() - b
s = d.statistika()
print(f" {qadam:<8} {vaqt:>7.3f}s {s['energiya']:>14,.0f} "
f"{s['eng_baland']:>11.2f} {s['eng_tez']:>10.2f}")
print("\n\n6. Slots tahlili:\n")
def barcha_slotlar(Sinf) -> list[str]:
natija = []
for S in reversed(Sinf.__mro__):
sl = getattr(S, "__slots__", ())
if isinstance(sl, str):
sl = (sl,)
natija.extend(s for s in sl if s not in natija)
return natija
print(f" {'Sinf':<12} {'__slots__':<44} {'__dict__'}")
print(" " + "─" * 68)
for Sinf in (Vektor, Jism, Dunyo):
sl = str(barcha_slotlar(Sinf))
o = (Vektor(1, 2) if Sinf is Vektor else
Jism(Vektor(0, 0)) if Sinf is Jism else Dunyo())
d_bor = "✅" if hasattr(o, "__dict__") else "❌"
print(f" {Sinf.__name__:<12} {sl[:42]:<44} {d_bor}")
print(f"""
⭐ BU LOYIHADA __slots__ QARORLARI:
Vektor:
__slots__ = ("x", "y")
⭐ 300 000+ nusxa → ~19% xotira tejaldi (yuqoridagi o'lchov)
⚠️ cached_property o'rniga @property
⚠️ __weakref__ kerak emas (kuzatilmaydi)
Jism:
__slots__ = (..., "__weakref__")
⭐ 50 000+ nusxa
⭐ __weakref__ QO'SHILDI — WeakSet kuzatuvi uchun
Dunyo:
__slots__ = ("_jismlar", "tortishish", "_qadam")
⚠️ Nusxa 1-2 ta — tejash uchun emas
⭐ XATO NOMLARDAN himoya uchun
⭐ QO'SHIMCHA OPTIMIZATSIYA:
__iadd__ / __imul__ — joyida o'zgartirish
→ tsiklda yangi obyekt yaratilmaydi
→ {t_yangi / t_joyida:.1f}× tezroq
""")Natijaning muhim qismi:
2. ⭐ Xotira:
300,000 vektor:
Oddiy sinf: 36.8 MB
__slots__: 29.9 MB
⭐ Tejash: 6.9 MB (19%)
Bitta obyekt:
VektorOddiy: ~129 bayt (ro'yxat ko'rsatkichi bilan)
Vektor: ~105 bayt
3. Tezlik: += vs +
500,000 qo'shish:
a = a + d (yangi): 0.343s
b += d (joyida): 0.175s
⭐ 2.0× tezroq
4. ⭐ __weakref__ bilan kuzatuv:
Yaratildi: 3 jism
del j → 2 jism ✅ WeakSet tozalandiNima ko'rsatdi: 2.3, 2.6, 2.7-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
"__slots__ har doim tejaydi" |
3.11+ da farq kamaydi; kam obyektda sezilmaydi |
"__slots__ obyektni o'zgarmas qiladi" |
Mavjud slotga yozish mumkin |
"Avlodda __slots__ shart emas" |
__dict__ qaytadi, tejash yo'qoladi |
"__slots__ ni takrorlash zararsiz" |
Xotira ikki marta egallanadi |
"pickle ishlamaydi" |
Protokol 2+ da ishlaydi |
"__slots__ — tezlik uchun" |
Xotira uchun; 3.11+ da tezlik farqi deyarli yo'q |
| "Ko'p merosda ishlaydi" | Faqat bittasida bo'sh bo'lmagan |
"__slots__ — optimizatsiya" |
Atributlarni cheklash ham foydali |
6. Keng tarqalgan xatolar va yechimlari
1. Avlodda __slots__ unutish
class B(A):
pass # ⚠️ __dict__ qaytadi
__slots__ = () # ✅2. Takrorlash
class B(A):
__slots__ = ("x", "y") # ⚠️ A da ham bor
__slots__ = ("y",) # ✅3. __weakref__ unutish
__slots__ = ("x",) # ⚠️ weakref ishlamaydi
__slots__ = ("x", "__weakref__") # ✅
@dataclass(slots=True, weakref_slot=True) # ✅ 3.11+4. cached_property bilan
__slots__ = ("x",)
@cached_property # ❌ TypeError
@property # ✅ yoki
__slots__ = ("x", "__dict__") # ✅ (lekin tejash yo'q)5. Sinf atributi konflikti
__slots__ = ("x",)
x = 5 # ❌ ValueError
# ✅ __init__ da o'rnating6. Ko'p meros
class C(A_slots, B_slots): ... # ❌ TypeError
class Mixin: __slots__ = () # ✅ mixin larda bo'sh7. __dict__ bo'lgan ota-sinf
class A: pass # __dict__ bor
class B(A): __slots__ = ("x",) # ⚠️ foydasiz8. Erta optimizatsiya
class Sozlama: # 1 nusxa
__slots__ = (...) # ⚠️ 100 bayt tejaldi, cheklovlar qoldi7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8.2-dars (o'tilgan):
__dict__va atribut qidiruvi - 8.15-dars: deskriptorlar —
member_descriptor - 8.17-dars:
@dataclass(slots=True) - 8.5-dars: xotira solishtiruvi
sys.getsizeof,tracemalloc— o'lchash- NumPy, pandas: massiv asosidagi muqobil
- CPython:
Objects/typeobject.c,type_new_slots
8. Eng yaxshi amaliyotlar
Avval o'lchang.
tracemallocyokimemory_profiler.100 000+ obyekt bo'lsagina. Kamroq bo'lsa — sezilmaydi.
Har avlodda
__slots__yozing. Bo'sh bo'lsa ham:().Takrorlamang. Faqat yangi nomlar.
__weakref__ni o'ylang. Kuzatuv kerak bo'lsa qo'shing.Mixin larda
__slots__ = (). Ko'p meros ishlashi uchun.dataclass(slots=True)ishlating. Qo'lda yozishdan oson.Taxmin qilmang — o'lchang.
tracemallocbilan;NamedTuplevatupleham slotli sinfdan ko'proq xotira olishi mumkin.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
1. class A: __slots__ = ("x",)
a = A(); a.x = 1
try: a.y = 2
except AttributeError: print("xato")
2. class A: __slots__ = ("x",)
print(hasattr(A(), "__dict__"))
3. class A: __slots__ = ("x",)
class B(A): pass
print(hasattr(B(), "__dict__"))
4. class A: __slots__ = ("x",)
class B(A): __slots__ = ()
print(hasattr(B(), "__dict__"))
5. class A: __slots__ = ("x",)
print(type(A.x).__name__)
6. class A:
__slots__ = ("x",)
x = 5
# ?
7. import weakref
class A: __slots__ = ("x",)
try: weakref.ref(A())
except TypeError: print("xato")
8. class A: __slots__ = "x"
a = A(); a.x = 1
print(a.x)
9. class A: __slots__ = ("x",)
class B: __slots__ = ("y",)
try:
class C(A, B): pass
except TypeError: print("xato")
10. class A: __slots__ = ("x", "__dict__")
a = A(); a.x = 1; a.y = 2
print(a.__dict__)
11. from dataclasses import dataclass
@dataclass(slots=True)
class A: x: int
print(hasattr(A(1), "__dict__"))
12. class A: __slots__ = ("x",)
print(A.__slots__, sorted(A.__dict__.keys())[:2])Javoblar
xatoFalseTrue—__slots__yo'qFalse—member_descriptorValueError: 'x' in __slots__ conflicts with class variablexato1— satr ham ishlaydixato— layout conflict{'y': 2}—xslotdaFalse('x',) ['__doc__', '__firstlineno__'](3.13+; oldin['__doc__', '__module__']) — sinf lug'atida__slots__,x(member_descriptor) va dunder'lar bor
Vazifa 2: Xatolarni tuzating
1. class B(A): # A da __slots__ bor
pass
2. class B(A):
__slots__ = ("x", "y", "z") # A: __slots__ = ("x", "y")
3. class A:
__slots__ = ("x",)
@cached_property
def y(self): ...
4. class A: __slots__ = ("x",)
weakref.ref(A())
5. class Mixin: pass
class C(Mixin, SlotliSinf): ...
6. class A:
__slots__ = ("x",)
x = 0
7. class Sozlama: # 1 nusxa
__slots__ = (...)
8. class A(Oddiy): # Oddiy da __slots__ yo'q
__slots__ = ("x",)Javoblar
1. __slots__ = () qo'shing
2. __slots__ = ("z",) — faqat yangi
3. @property yoki __slots__ = ("x", "__dict__")
4. __slots__ = ("x", "__weakref__")
5. class Mixin: __slots__ = ()
6. x = 0 ni olib tashlang, __init__ da o'rnating
7. __slots__ kerak emas (yoki faqat cheklash uchun)
8. Oddiy ga ham __slots__ qo'shing (aks holda foydasiz)Vazifa 3: Piksel sinfi
Yozing:
__slots__ = ("r", "g", "b", "a")- Arifmetika: aralashtirish, yorug'lik
__iadd__bilan joyida o'zgartirish- 1 million piksel xotirasini o'lchang
NamedTuplebilan solishtiringarraymoduli bilan ham solishtiring
Vazifa 4: __slots__ tekshiruvchisi
Vosita yozing:
__slots__bor sinflarni topsin- Avlodda
__slots__yo'qligini - Takrorlangan slotlarni
__dict__bor-yo'qligini (tejash yo'qmi)__weakref__kerakligini (weakrefishlatilsa)- Tejash miqdorini hisoblasin
Vazifa 5: Aralash yondashuv
- Asosiy atributlar
__slots__da - Kamdan-kam ishlatiladiganlar
__dict__da - Xotira farqini o'lchang
- Qaysi atributlar tez-tez ishlatilishini aniqlang
__getattr__bilan kechiktirilgan yuklash- Uch variantni solishtiring
Vazifa 6: Migratsiya
Mavjud sinfni __slots__ ga o'tkazing:
- Barcha atributlarni toping (
__init__va boshqa metodlar) cached_propertylarnipropertygaweakrefishlatilishini tekshiring- Avlodlarni yangilang
pickle/copytestlarini o'tkazing- Xotira farqini hujjatlang
Vazifa 7: O'ylash
Nega __slots__ sukut bo'yicha yoqilmagan — agar u xotira tejasa va tezroq bo'lsa?
Javob
Chunki Pythonning dinamikligi — uning asosiy kuchi, va __slots__ uni cheklaydi.
1. Dinamik atributlar — Pythonning asosiy xususiyati
class A: pass
a = A()
a.istalgan = 1 # ✅ ishlaydi
a.yana = "matn"
a.__dict__ # {'istalgan': 1, 'yana': 'matn'}Bu — xususiyat, kamchilik emas. Undan foydalanadigan narsalar:
a) Monkey patching:
import kutubxona
kutubxona.Sinf.tuzatilgan_metod = mening_versiyam
obj.vaqtinchalik_bayroq = Trueb) Kutubxonalar:
# Flask
app.config["KALIT"] = "qiymat"
g.foydalanuvchi = current_user # ⭐ dinamik kontekst
# pytest
def test_a(request):
request.mening_malumotim = ...
# unittest.mock
mock.istalgan_atribut.istalgan_metod()c) ORM va serializatorlar:
# SQLAlchemy
obj._sa_instance_state = ... # ⭐ ichki holat
# Django
obj._state = ModelState()
obj._prefetched_objects_cache = {} __slots__ sukut bo'yicha bo'lsa — bularning hammasi buzilardi.
2. Orqaga moslik
__slots__ Python 2.2 (2001) da qo'shildi. O'sha paytda millionlab qator kod bor edi:
class A: pass
a = A()
a.qoshimcha = 1 # ⚠️ endi buzilardiSukut bo'yicha yoqish — butun ekotizimni buzardi.
3. Meros bilan murakkablik
class A:
__slots__ = ("x",)
class B(A):
# ⚠️ Har avlodda __slots__ yozish kerak
# ⚠️ Takrorlash — xotira behuda
# ⚠️ Ko'p meros — layout conflictSukut bo'yicha bo'lsa, har sinf yozgan dasturchi bu qoidalarni bilishi kerak bo'lardi.
4. Foyda har doim ham yo'q
Python 3.11+ da "key-sharing dictionaries":
Python 3.2 dan (PEP 412):
Bir sinfning barcha nusxalari KALITLARNI ulashadi
1000 ta Nuqta obyekti:
Eski: 1000 × (kalitlar + qiymatlar)
Yangi: 1 × kalitlar + 1000 × qiymatlar Natijada __dict__ ustama xarajati ancha kamaydi, va __slots__ foydasi ham.
O'lchov (Python 3.14, 2 atribut, tracemalloc):
__dict__ |
__slots__ |
Tejash | |
|---|---|---|---|
| Bitta obyekt | ~88 bayt | ~48 bayt | ~45% |
Python 3.3 gacha (PEP 412 dan oldin) har nusxaning to'liq alohida lug'ati bo'lgani uchun farq bundan ancha katta edi. 3.11+ dagi "inline values" uni yana kamaytirdi — lekin ko'p obyektda baribir sezilarli.
5. Cheklovlar zanjiri
__slots__ sukut bo'yicha yoqilsa, quyidagilar ishlamasdi:
# 1. cached_property
@cached_property
def x(self): ... # ❌
# 2. weakref
weakref.ref(obj) # ❌
# 3. Dinamik atributlar
obj.yangi = 1 # ❌
# 4. Ko'p meros
class C(A, B): ... # ❌ layout conflict
# 5. Ba'zi kutubxonalar
pickle (eski protokol) # ❌
copy (maxsus holatlarda) # ⚠️Har biri uchun maxsus yechim kerak bo'lardi.
6. Boshqa tillar
| Til | Yondashuv |
|---|---|
| Python | __dict__ sukut, __slots__ ixtiyoriy |
| JavaScript | Dinamik, lekin "hidden classes" (V8) |
| Ruby | Dinamik (instance_variable_set) |
| Java | Statik maydonlar (majburiy) |
| C# | Statik + dynamic (ixtiyoriy) |
| Go, Rust | Statik struct |
JavaScript eng qiziq:
class Nuqta {
constructor(x, y) { this.x = x; this.y = y; }
}V8 dvigateli avtomatik "hidden class" (shape) yaratadi — bu __slots__ ga o'xshash optimizatsiya, lekin dasturchi ko'rmaydi.
Lekin dinamik atribut qo'shsangiz:
const p = new Nuqta(1, 2);
p.z = 3; // ⚠️ yangi hidden class — sekinlashadiV8 "shape transition" qiladi — ishlaydi, lekin sekinroq.
7. Nega Python bunday qilmadi
CPython da "hidden classes" ni amalga oshirish mumkin edi, lekin:
- Murakkab — V8 da bu minglab qator C++ kod
- CPython sodda bo'lishi kerak — o'qish va hissa qo'shish oson
- Boshqa optimizatsiyalar muhimroq — 3.11 dagi "Specializing Adaptive Interpreter"
Lekin Python qisman shu yo'ldan bordi:
PEP 412 3.3-bob — "Key-Sharing Dictionary":
Bir sinfning nusxalari kalit jadvalini ULASHADI
→ V8 hidden class g'oyasining soddalashtirilgan versiyasi3.11 — "Lazy __dict__ creation":
__dict__ FAQAT kerak bo'lganda yaratiladi
→ atributsiz obyektlar kam joy egallaydi8. __slots__ — ixtiyoriy vosita
Bu — Pythonning odatiy naqshi:
| Muammo | Sukut | Ixtiyoriy optimizatsiya |
|---|---|---|
| Xotira | __dict__ |
__slots__ |
| Tur xavfsizligi | Duck typing | mypy |
| Tezlik | Sof Python | C kengaytma, Cython |
| Validatsiya | Yo'q | pydantic |
| Ma'lumot sinfi | Qo'lda | dataclass |
Siz to'laysiz faqat kerak bo'lganda.
9. Amaliy tavsiya
# ⭐ 1. Sukut — __slots__ SIZ
class Xizmat:
def __init__(self, nom): self.nom = nom
# ⭐ 2. O'lchang
import tracemalloc
tracemalloc.start()
obyektlar = [Xizmat(i) for i in range(1_000_000)]
print(tracemalloc.get_traced_memory()[0] / 1_048_576, "MB")
# ⭐ 3. Muammo bo'lsa — __slots__
@dataclass(slots=True)
class Xizmat:
nom: str
# ⭐ 4. Yoki butunlay boshqa yondashuv
import numpy as np
nomlar = np.array([...]) # massiv — eng kam xotira10. Xulosa
__slots__ sukut bo'yicha yoqilmagan, chekki:
- Dinamiklik — Pythonning asosiy kuchi
- Orqaga moslik — millionlab qator kod buzilardi
- Cheklovlar —
cached_property,weakref, ko'p meros - Meros murakkabligi — har avlodda yozish kerak
- Foyda kamaydi — key-sharing dict (3.3+)
Va bu — Pythonning umumiy falsafasi: sukut bo'yicha moslashuvchan, kerak bo'lganda optimallashtiriladi.
Donald Knuth:
"Premature optimization is the root of all evil." (Erta optimizatsiya — barcha yomonlikning ildizi.)
__slots__ — o'lchagandan keyin qo'llaniladigan vosita, "har ehtimolga qarshi" emas.
Nimani mustahkamlaydi: 2.3, 2.5, 2.7-bo'limlar.
Xulosa
Bu darsda __slots__ ni o'rgandik.
Eng muhim uch fikr:
__slots__lug'atni massiv bilan almashtiradi. Har atribut uchunmember_descriptor(data deskriptor) yaratiladi va qiymat obyektning belgilangan o'rnida saqlanadi. Natija — Python 3.14 da har obyektga ~20–50% kam xotira (atributlar qancha kam bo'lsa, ulush shuncha katta). Tezlik farqi zamonaviy Pythonda deyarli yo'q. Foyda faqat 100 000 dan ko'p obyekt bo'lganda seziladi, va unitracemallocbilan o'lchang —sys.getsizeof(obj.__dict__)3.11+ da lug'atni o'zi yaratib, natijani buzadi.Har avlodda
__slots__yozish shart. Avlodda__slots__yo'q bo'lsa —__dict__qaytadi va butun tejash yo'qoladi. Bo'sh bo'lsa ham__slots__ = ()yozing. Ota-sinfdagi nomlarni takrorlamang — ular ikki marta joy egallaydi. Mixin larda__slots__ = ()— ko'p meros ishlashi uchun.Beshta cheklovni ongli qabul qiling. Yangi atribut,
__dict__,__weakref__,cached_propertyva ko'p meros — hammasi cheklanadi.__weakref__ni__slots__ga qo'shish mumkin,cached_propertyo'rniga@propertyishlatiladi. Eng oson yo'l —@dataclass(slots=True).NamedTupleesa o'zgarmaslik uchun yaxshi, lekin xotirada slotli sinfdan ko'proq joy oladi.
Keyingi dars — 8-qismning yakuni: kompozitsiya va meros orasidagi tanlov, va butun OOP bilimini bir arxitekturaviy qarorga bog'lash.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!