3-qism · Professional Python·12-bob
Obyektga yo'naltirilgan dasturlash: asoslar
Klass va obyekt, __init__ va self, instansiya va klass atributlari, metod turlari (oddiy, classmethod, staticmethod), property, inkapsulyatsiya an'analari, meros, super() va MRO, kompozitsiya.
Siz OOP'ni allaqachon ishlatib kelyapsiz: "salom".upper() — str
klassining metodi, lst.append(x) — list klassining metodi. Bu bobda
o'z klasslaringizni yaratishni o'rganamiz. AI/ML uchun bu ayniqsa
muhim: PyTorch'da har bir model — nn.Module vorisi klass, scikit-learn'da
har bir model — fit/predict metodli klass, Pydantic'da har bir ma'lumot
sxemasi — klass. Kutubxonalarni to'g'ri ishlatish uchun ularning OOP
dizaynini tushunish kerak.
Klasslar va obyektlar, __init__ va self — OOP'ning boshlang'ich nuqtasi. Corey Schafer seriyasining keyingi qismlari meros, property va classmethod'larni ham yoritadi.
1. Nega klass kerak?#
Tasavvur qiling, bank hisoblari bilan ishlayapsiz. Klasssiz:
hisob = {"egasi": "Aziz", "balans": 0}
def depozit(hisob, summa):
hisob["balans"] += summa
def yechish(hisob, summa):
if summa > hisob["balans"]:
raise ValueError("Mablag' yetarli emas")
hisob["balans"] -= summaMuammolar: ma'lumot va uni o'zgartiruvchi funksiyalar alohida; hech kim
hisob["balans"] = -1000000 deb yozishga to'sqinlik qilmaydi; "balnas"
deb xato yozsangiz — yangi kalit yaratiladi, xato chiqmaydi.
Klass ma'lumot (holat) va xatti-harakatni (metodlar) bitta tushunchaga birlashtiradi:
class BankAccount:
"""Oddiy bank hisobi."""
def __init__(self, owner: str, balance: float = 0.0) -> None:
self.owner = owner
self.balance = balance
def deposit(self, amount: float) -> None:
if amount <= 0:
raise ValueError("Summa musbat bo'lishi kerak")
self.balance += amount
def withdraw(self, amount: float) -> None:
if amount > self.balance:
raise ValueError("Mablag' yetarli emas")
self.balance -= amount
acc = BankAccount("Aziz") # obyekt (instansiya) yaratish
acc.deposit(500_000)
acc.withdraw(120_000)
print(acc.balance) # 380000.02. Klass, obyekt, __init__ va self#
- Klass — andoza (chizma):
BankAccount. - Obyekt (instansiya) — andoza asosida yaratilgan aniq narsa:
acc. Bitta klassdan istalgancha obyekt yaratish mumkin, har birining o'z holati bor.
BankAccount("Aziz") chaqiriladi
Python avval bo'sh obyekt yaratadi (
__new__).__init__ chaqiriladi
BankAccount.__init__(yangi_obyekt, "Aziz")— obyektni boshlang'ich holatga keltiradi.__init__hech narsa qaytarmaydi (None).Obyekt qaytariladi
Natija
accnomiga bog'lanadi.
self — sehr emas#
acc.deposit(100) aslida shunday chaqiruvning qisqa yozuvi:
BankAccount.deposit(acc, 100)Ya'ni self — metod chaqirilgan obyektning o'zi, birinchi argument
sifatida avtomatik uzatiladi. self — shunchaki an'anaviy nom (kalit so'z
emas), lekin har doim shunday nomlang.
Klassik xato — selfni unutish:
class Counter:
def __init__(self):
count = 0 # ❌ lokal o'zgaruvchi — __init__ tugashi bilan yo'qoladi
self.count = 0 # ✅ obyekt atributi
def inc(): # ❌ self yo'q
...
# Counter().inc() → TypeError: inc() takes 0 positional arguments but 1 was given3. Instansiya atributlari va klass atributlari#
class Model:
registry: list[str] = [] # KLASS atributi — hamma obyektlar uchun umumiy
default_lr = 1e-3 # klass atributi (konstanta sifatida — yaxshi)
def __init__(self, name: str):
self.name = name # INSTANSIYA atributi — har obyektning o'zi
Model.registry.append(name)
a = Model("bert")
b = Model("gpt2")
a.name, b.name # ('bert', 'gpt2') — har biriniki
Model.registry # ['bert', 'gpt2'] — umumiy
a.default_lr # 0.001 — obyektda yo'q, klassdan topiladiAtribut qidirish tartibi: avval obyektning o'zida (a.__dict__), keyin
klassda, keyin ota klasslarda.
Yana bir nozik joy: obyekt orqali klass atributiga tayinlash yangi instansiya atributi yaratadi va klassnikini "yopib qo'yadi":
a.default_lr = 0.1 # faqat a uchun; Model.default_lr va b o'zgarmaydi4. Metod turlari#
from datetime import date
class Experiment:
count = 0
def __init__(self, name: str, started: date):
self.name = name
self.started = started
Experiment.count += 1
# 1. Oddiy metod — obyekt bilan ishlaydi (self)
def describe(self) -> str:
return f"{self.name} ({self.started:%Y-%m-%d})"
# 2. Klass metodi — klass bilan ishlaydi (cls); ko'pincha "muqobil konstruktor"
@classmethod
def from_string(cls, s: str) -> "Experiment":
name, iso = s.split("@")
return cls(name, date.fromisoformat(iso))
# 3. Statik metod — na self, na cls; klassga mantiqan tegishli oddiy funksiya
@staticmethod
def is_valid_name(name: str) -> bool:
return name.isidentifier()
e = Experiment.from_string("baseline@2026-10-01")
e.describe() # 'baseline (2026-10-01)'
Experiment.is_valid_name("run 1") # False| Tur | Birinchi argument | Qachon |
|---|---|---|
| Oddiy metod | self (obyekt) | Obyekt holati bilan ishlash — 90% hollar |
@classmethod | cls (klass) | Muqobil konstruktorlar: from_file, from_dict, from_pretrained |
@staticmethod | yo'q | Klassga mantiqan bog'liq yordamchi funksiya |
from_pretrained — Hugging Face'dagi AutoModel.from_pretrained("bert-base")
aynan classmethod: u fayllarni yuklab, cls(...) ni to'g'ri parametrlar
bilan chaqiradi. cls ishlatilgani uchun voris klasslarda ham to'g'ri
ishlaydi (vorisning obyektini yaratadi).
5. @property — atribut ko'rinishidagi metod#
Ba'zan atributni o'qish/yozishda tekshiruv yoki hisob-kitob kerak bo'ladi.
Java uslubida get_x() / set_x() yozish Python'da qabul qilinmagan —
buning o'rniga property:
class Temperature:
def __init__(self, celsius: float):
self.celsius = celsius # setter orqali o'tadi — tekshiruv ishlaydi
@property
def celsius(self) -> float:
return self._celsius
@celsius.setter
def celsius(self, value: float) -> None:
if value < -273.15:
raise ValueError("Absolyut noldan past bo'lishi mumkin emas")
self._celsius = value
@property
def fahrenheit(self) -> float: # faqat o'qiladigan, hisoblanadigan atribut
return self._celsius * 9 / 5 + 32
t = Temperature(25)
t.fahrenheit # 77.0 — qavssiz, atribut kabi
t.celsius = -300 # ValueError
t.fahrenheit = 100 # AttributeError — setter yo'qKuchli tomoni: oddiy atribut bilan boshlaysiz (self.celsius = c), keyinroq
tekshiruv kerak bo'lsa — propertyga aylantirasiz va klassdan
foydalanuvchi kodni o'zgartirish shart emas.
6. Inkapsulyatsiya: Python uslubi#
Python'da private/public kalit so'zlari yo'q. Buning o'rniga
kelishuvlar bor:
| Nom | Ma'nosi |
|---|---|
name | Ommaviy — foydalanish mumkin |
_name | "Ichki" — klassdan tashqarida ishlatmang (texnik jihatdan mumkin) |
__name | Nom o'zgartiriladi (name mangling): _Klass__name — vorislar bilan to'qnashuvdan himoya |
__name__ | Python'ning maxsus (dunder) nomlari — o'zingiz yangilarini o'ylab topmang |
class Model:
def __init__(self):
self.weights = [] # ommaviy
self._cache = {} # ichki — "tegmang"
self.__secret = 42 # → self._Model__secret
m = Model()
m._cache # ishlaydi, lekin "biz kattalarmiz" — o'z javobgarligingizda
m.__secret # AttributeError
m._Model__secret # 42 — himoya emas, faqat nom o'zgartirishPython falsafasi: "We're all consenting adults here" — himoyani
kompilyator emas, kelishuv ta'minlaydi. _ bilan boshlangan narsalar
klassning ichki tafsiloti: ular keyingi versiyada ogohlantirishsiz
o'zgarishi mumkin. __ ni kamdan-kam ishlating — odatda _ yetarli.
7. Meros (inheritance)#
Klass boshqa klassning atribut va metodlarini meros qilib olishi va ularni kengaytirishi yoki almashtirishi mumkin:
class Classifier:
def __init__(self, n_classes: int):
self.n_classes = n_classes
self.is_fitted = False
def fit(self, X, y) -> "Classifier":
raise NotImplementedError("Voris klass fit()ni amalga oshirishi kerak")
def predict(self, X):
raise NotImplementedError
def score(self, X, y) -> float:
preds = self.predict(X)
return sum(p == t for p, t in zip(preds, y, strict=True)) / len(y)
class MajorityClassifier(Classifier):
"""Har doim eng ko'p uchragan sinfni bashorat qiladi — baseline."""
def fit(self, X, y) -> "MajorityClassifier":
self.majority_ = max(set(y), key=y.count)
self.is_fitted = True
return self
def predict(self, X):
if not self.is_fitted:
raise RuntimeError("Avval fit() chaqiring")
return [self.majority_] * len(X)
clf = MajorityClassifier(n_classes=2).fit([[0], [1], [2]], [1, 1, 0])
clf.score([[5], [6]], [1, 0]) # 0.5 — score() ota klassdan merosBu — scikit-learn API'sining soddalashtirilgan nusxasi. E'tibor bering:
fit self qaytaradi (zanjir uchun: Model().fit(X, y).predict(X_test)),
o'qitishda topilgan atributlar _ bilan tugaydi (majority_) — bu
scikit-learn an'anasi.
super() — ota klassga murojaat#
Voris klass __init__ni qayta aniqlasa, ota klassnikini o'zi chaqirishi
kerak:
class LogisticRegression(Classifier):
def __init__(self, n_classes: int, lr: float = 0.1, epochs: int = 100):
super().__init__(n_classes) # ota klass o'z atributlarini o'rnatsin
self.lr = lr
self.epochs = epochssuper().__init__ unutilsa — self.is_fitted va self.n_classes
yaratilmaydi va keyinroq AttributeError chiqadi. PyTorch'da bu eng
mashhur xato:
import torch.nn as nn
class MyNet(nn.Module):
def __init__(self):
super().__init__() # ← busiz: "cannot assign module before Module.__init__() call"
self.fc = nn.Linear(10, 2)isinstance va issubclass#
isinstance(clf, MajorityClassifier) # True
isinstance(clf, Classifier) # True — voris ham ota klassning instansiyasi
issubclass(bool, int) # True
type(clf) is Classifier # False — type() aniq klassni beradiTip tekshirishda type(x) == ... emas, isinstance ishlating — u
merosni hisobga oladi.
8. Ko'p merosxo'rlik va MRO#
Python'da klass bir nechta ota klassdan meros olishi mumkin. Metod qaysi klassdan olinishini MRO (Method Resolution Order) belgilaydi:
class A:
def hi(self): return "A"
class B(A):
def hi(self): return "B" + super().hi()
class C(A):
def hi(self): return "C" + super().hi()
class D(B, C):
def hi(self): return "D" + super().hi()
D().hi() # 'DBCA'
[k.__name__ for k in D.__mro__] # ['D', 'B', 'C', 'A', 'object']Diqqat: B ichidagi super() Ani emas, Cni chaqirdi! super() —
"ota klass" emas, MRO bo'yicha keyingi klass. Shu tufayli har bir klass
faqat bir marta chaqiriladi ("olmos" muammosi hal bo'ladi).
Amaliyotda ko'p merosxo'rlik asosan mixinlar uchun ishlatiladi — kichik, bitta vazifali qo'shimchalar:
import json
class JsonMixin:
def to_json(self) -> str:
return json.dumps(self.__dict__, ensure_ascii=False, default=str)
class Experiment(JsonMixin):
def __init__(self, name, lr):
self.name, self.lr = name, lr
Experiment("baseline", 0.01).to_json() # '{"name": "baseline", "lr": 0.01}'9. Kompozitsiya merosdan afzal#
Meros — "is-a" ("... hisoblanadi") munosabati: LogisticRegression
is a Classifier. Ko'pincha esa kerakli munosabat — "has-a"
("... ga ega"):
# ❌ Meros noto'g'ri: Pipeline Tokenizer EMAS
class Pipeline(Tokenizer):
...
# ✅ Kompozitsiya: Pipeline'da tokenizer va model BOR
class Pipeline:
def __init__(self, tokenizer, model):
self.tokenizer = tokenizer
self.model = model
def __call__(self, text: str):
ids = self.tokenizer.encode(text)
return self.model.predict(ids)Kompozitsiya afzalliklari: qismlarni almashtirish oson (boshqa tokenizer), test qilishda soxta (mock) obyekt berish oson, chuqur meros zanjirlari yo'q. Qoida: meros zanjiri 2–3 darajadan chuqur bo'lmasin; shubha bo'lsa — kompozitsiya.
10. Amaliyot: LLM suhbat sessiyasi klassi#
from datetime import datetime
class ChatSession:
"""LLM bilan suhbat tarixini boshqaradi."""
MAX_HISTORY = 20
def __init__(self, system: str, model: str = "claude-opus-5-5") -> None:
self.system = system
self.model = model
self._messages: list[dict] = []
self.created_at = datetime.now()
@property
def turns(self) -> int:
"""Foydalanuvchi xabarlari soni."""
return sum(1 for m in self._messages if m["role"] == "user")
@property
def messages(self) -> list[dict]:
"""Tarix nusxasi — tashqaridan o'zgartirib bo'lmasin."""
return [dict(m) for m in self._messages]
def add_user(self, text: str) -> None:
self._add("user", text)
def add_assistant(self, text: str) -> None:
self._add("assistant", text)
def _add(self, role: str, text: str) -> None:
if not text.strip():
raise ValueError("Bo'sh xabar")
if self._messages and self._messages[-1]["role"] == role:
raise ValueError(f"Ketma-ket ikkita '{role}' xabari bo'lishi mumkin emas")
self._messages.append({"role": role, "content": text})
self._messages = self._messages[-self.MAX_HISTORY:]
def to_request(self, max_tokens: int = 1024) -> dict:
"""API so'rovi uchun parametrlar."""
return {"model": self.model, "max_tokens": max_tokens,
"system": self.system, "messages": self.messages}
@classmethod
def from_dict(cls, data: dict) -> "ChatSession":
s = cls(system=data["system"], model=data.get("model", "claude-opus-5-5"))
for m in data.get("messages", []):
s._add(m["role"], m["content"])
return s
chat = ChatSession(system="Sen Python o'qituvchisisan.")
chat.add_user("Dekorator nima?")
chat.add_assistant("Dekorator — funksiyani o'rab oluvchi funksiya...")
chat.add_user("Misol ber")
print(chat.turns) # 2
print(len(chat.to_request()["messages"])) # 3Bu klassda: property (hisoblanadigan turns va himoyalangan
messages), ichki _add metodi va _messages atributi, klass konstantasi
MAX_HISTORY, classmethod muqobil konstruktor va invariantlarni
tekshirish (rollar navbatma-navbat bo'lishi — Messages API talabi).
Nima noto'g'ri ketishi mumkin#
| Xato | Belgisi | Tuzatish |
|---|---|---|
Metodda self yo'q | takes 0 positional arguments but 1 was given | def metod(self, ...) |
__init__da self. unutilgan | Keyinroq AttributeError | self.x = x |
| Mutable klass atributi | Obyektlar holatni "baham ko'radi" | __init__ ichida yaratish |
super().__init__() chaqirilmagan | Ota atributlari yo'q; PyTorch'da xato | Voris __init__ boshida chaqiring |
type(x) == Klass | Vorislar mos kelmaydi | isinstance(x, Klass) |
get_x()/set_x() | Pythonic emas | @property |
| Og'ir property | Atribut o'qish sekin/yon ta'sirli | Metod yoki cached_property |
| Chuqur meros zanjiri | Tushunish va o'zgartirish qiyin | Kompozitsiya |
| Ichki ro'yxatni to'g'ridan-to'g'ri qaytarish | Tashqaridan o'zgartirib yuborishadi | Nusxa qaytaring yoki tuple |
1. Vector klassi
Vector klassini yozing: values ro'yxatini saqlasin, norm (property —
uzunligi), dot(other) (skalyar ko'paytma), cosine(other) (kosinus
o'xshashlik) metodlari bo'lsin, nol vektor uchun cosine tushunarli xato
bersin. 13-bobda bu klassga +, *, == operatorlarini qo'shamiz.
Maslahatni ko'rsatish
__init__ da self.values = list(values); norm uchun math.sqrt(sum(x*x ...)); dot uchun zip(..., strict=True).
2. Baseline klassifikator
Classifier ota klassidan StratifiedRandomClassifier yarating: fit
sinflarning taqsimotini eslab qolsin, predict shu taqsimotga mos
tasodifiy bashorat bersin (seed parametri bilan). MajorityClassifier
bilan bir xil dataset'da scorelarini solishtiring.
Maslahatni ko'rsatish
Classifier'dan meros oling; fit ichida Counter(y) — har sinf ehtimoli n/len(y); predict ichida random.Random(seed).choices(classes, weights).
3. Property bilan validatsiya
GenerationConfig klassini yozing: temperature (0–1), max_tokens
(1–128000, butun son), top_p (0–1 yoki None) — hammasi property
orqali tekshirilsin. Noto'g'ri qiymat berilganda xabar aniq bo'lsin:
qaysi parametr, qanday diapazon, nima berildi.
Maslahatni ko'rsatish
Har bir parametr uchun @property + @x.setter; setter'da diapazonni tekshiring; __init__da self.x = x (setter orqali o'tadi).
4. Muqobil konstruktorlar
ChatSessionga from_jsonl(path) classmethod'ini va save_jsonl(path)
metodini qo'shing (11-bob). Ikkalasini birga sinab ko'ring: saqlang,
qayta yuklang, messages bir xil ekanini assert bilan tekshiring.
- 01Klass — andoza, obyekt — uning nusxasi; __init__ boshlang'ich holatni o'rnatadi.
- 02self — metod chaqirilgan obyekt: obj.m(x) == Klass.m(obj, x).
- 03Klass atributi — umumiy, instansiya atributi — har obyektniki; mutable holatni __init__da yarating.
- 04classmethod — muqobil konstruktorlar (from_*), staticmethod — klassga bog'liq yordamchi.
- 05@property — tekshiruvli yoki hisoblanadigan atribut; get_/set_ yozmang.
- 06_name — ichki (kelishuv), __name — name mangling; himoya kelishuvga asoslangan.
- 07Voris __init__da super().__init__() ni chaqiring; super() — MRO bo'yicha keyingi klass.
- 08is-a — meros, has-a — kompozitsiya; shubha bo'lsa kompozitsiya.
Mustahkamlash
Test va amaliy masalalar
Avval testni ishlang — har javobdan keyin izoh chiqadi. Keyin masalalarni shu yerning o'zida yechib, brauzerdagi haqiqiy Python bilan avtomatik tekshiring. Natijalaringiz kurs progressi sahifasida jamlanadi.
Bilimni tekshirish
Bob bo'yicha test
12 ta savol · har javobdan keyin to'g'ri yoki xatoligi va izoh ko'rsatiladi · oxirida natija foizda.
Amaliy masalalar
Kodni yozing va «Tekshirish»ni bosing (yoki Ctrl/⌘ + Enter). Kodingiz shu brauzerda saqlanib turadi.
01BankAccount
BankAccount(owner, balance=0) klassini yozing: deposit(amount) (musbat bo'lmasa ValueError), withdraw(amount) (mablag' yetmasa ValueError), balance — faqat o'qiladigan property (to'g'ridan-to'g'ri o'zgartirib bo'lmasin), history — amallar ro'yxati [("deposit", 100), ("withdraw", 30)].
02Majority baseline (sklearn API)
MajorityClassifier klassi: fit(X, y) eng ko'p uchragan sinfni self.majority_ ga saqlab `self` qaytarsin; predict(X) har element uchun shu sinfni qaytarsin; fitdan oldin predict → RuntimeError; score(X, y) — accuracy.
03Muqobil konstruktor
Experiment(name, lr) klassi va @classmethod from_string(cls, s) — "baseline@0.001" satridan obyekt yaratsin. Voris MyExperiment(Experiment) uchun MyExperiment.from_string(...) MyExperiment obyektini qaytarishi kerak. __repr__ → Experiment(name='baseline', lr=0.001) (klass nomi dinamik).