aidoc.uz

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.

▶ Video dars· Corey Schafer· 15:24· ingliz tilida
Python OOP Tutorial 1: Classes and Instances

Klasslar va obyektlar, __init__ va self — OOP'ning boshlang'ich nuqtasi. Corey Schafer seriyasining keyingi qismlari meros, property va classmethod'larni ham yoritadi.

YouTube'da ochish ↗

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"] -= summa

Muammolar: 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.0

2. 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.
  1. BankAccount("Aziz") chaqiriladi

    Python avval bo'sh obyekt yaratadi (__new__).

  2. __init__ chaqiriladi

    BankAccount.__init__(yangi_obyekt, "Aziz") — obyektni boshlang'ich holatga keltiradi. __init__ hech narsa qaytarmaydi (None).

  3. Obyekt qaytariladi

    Natija acc nomiga 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 given

3. 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 topiladi

Atribut 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'zgarmaydi

4. 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
TurBirinchi argumentQachon
Oddiy metodself (obyekt)Obyekt holati bilan ishlash — 90% hollar
@classmethodcls (klass)Muqobil konstruktorlar: from_file, from_dict, from_pretrained
@staticmethodyo'qKlassga 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'q

Kuchli 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:

NomMa'nosi
nameOmmaviy — foydalanish mumkin
_name"Ichki" — klassdan tashqarida ishlatmang (texnik jihatdan mumkin)
__nameNom 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'zgartirish

Python 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 meros

Bu — 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 = epochs

super().__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 beradi

Tip 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"]))  # 3

Bu 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#

XatoBelgisiTuzatish
Metodda self yo'qtakes 0 positional arguments but 1 was givendef metod(self, ...)
__init__da self. unutilganKeyinroq AttributeErrorself.x = x
Mutable klass atributiObyektlar holatni "baham ko'radi"__init__ ichida yaratish
super().__init__() chaqirilmaganOta atributlari yo'q; PyTorch'da xatoVoris __init__ boshida chaqiring
type(x) == KlassVorislar mos kelmaydiisinstance(x, Klass)
get_x()/set_x()Pythonic emas@property
Og'ir propertyAtribut o'qish sekin/yon ta'sirliMetod yoki cached_property
Chuqur meros zanjiriTushunish va o'zgartirish qiyinKompozitsiya
Ichki ro'yxatni to'g'ridan-to'g'ri qaytarishTashqaridan o'zgartirib yuborishadiNusxa qaytaring yoki tuple
Amaliy topshiriq

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).

Amaliy topshiriq

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).

Amaliy topshiriq

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).

Amaliy topshiriq

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.

Yodda saqlang
  • 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.

Test: topshirilmaganMasalalar: 0/3

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).