OzbekPython / Bob 16

Obyektga yönaltirilgan dasturlaş

Sinf va obyekt tuşunçasi, atributlar, metodlar, maxsus metodlar, meros, property hamda dataclass bilan işlaş.

Yuqori daraja20 daqiqa19 kod misoli16 / 24

Obyekt va sinf nima?

Atrofingizga qarang: telefon, maşina, bank kartasi, televizor... Har bir narsaning xususiyatlari (rangi, markasi, balansi) va qila oladigan işlari (qönğiroq qiliş, yuriş, pul yeçiş) bor.

Obyektga yönaltirilgan dasturlaş (OOP) aynan şu ğoyaga asoslanadi: maʼlumot va u bilan işlaydigan funksiyalarni bitta "quti" içiga birlaştiramiz.

  • Sinf (class) — çizma yoki andoza. Masalan, "Avtomobil" çizmasi: uning markasi, rangi, tezligi böladi, u yura oladi.
  • Obyekt (object, instance) — çizma asosida yasalgan aniq narsa: "oq rangli Cobalt", "qora Malibu".

Bitta sinfdan istalgança obyekt yaratiş mumkin, xuddi bitta çizma asosida köplab uy qurilgani kabi.

Aslida siz OOP bilan allaqaçon tanişsiz: Pythonda hamma narsa obyekt. "salom".upper() yozganingizda str sinfining obyektida upper metodini çaqirasiz.

Python
ism = "dilnoza"
sonlar = [3, 1, 2]

print(type(ism), type(sonlar))
print(ism.capitalize())   # str sinfining metodi
sonlar.sort()              # list sinfining metodi
print(sonlar)

Natija:

Natija
<class 'str'> <class 'list'>
Dilnoza
[1, 2, 3]

Birinçi sinf: class kalit sözi

Sinf class kalit sözi bilan eʼlon qilinadi. Sinf nomlari odatda KattaHarfBilan yoziladi (PascalCase uslubi).

Python
class Mushuk:
    pass


barsik = Mushuk()
mosh = Mushuk()
print(type(barsik))
print(barsik is mosh)  # ikki xil obyekt

Natija:

Natija
<class '__main__.Mushuk'>
False

Mushuk() — sinfni xuddi funksiya kabi çaqiriş yangi obyekt yaratadi. Hozirça bu obyektlar böş. Keling, ularga maʼlumot beramiz.

__init__ va atributlar

__init__ — maxsus metod, u obyekt yaratilgan zahoti avtomatik çaqiriladi. Uning vazifasi — obyektning boşlanğiç holatini örnatiş. Birinçi parametr self — yaratilayotgan obyektning özi.

Python
class Talaba:
    def __init__(self, ism: str, kurs: int):
        self.ism = ism        # atribut
        self.kurs = kurs
        self.baholar = []     # har bir talabaning öz röyxati


aziz = Talaba("Aziz", 2)
malika = Talaba("Malika", 3)

aziz.baholar.append(90)
print(aziz.ism, aziz.kurs, aziz.baholar)
print(malika.ism, malika.kurs, malika.baholar)

Natija:

Natija
Aziz 2 [90]
Malika 3 []

self.ism = ism — bu "şu obyektning ism atributiga qiymat ber" degani. Har bir obyekt öz atributlarini alohida saqlaydi: Azizning baholari Malikaga taʼsir qilmaydi.

Sinf atributlari va obyekt atributlari

self. orqali beriladigan atributlar har bir obyektga tegişli. Sinf tanasida töğridan-töğri yozilgan atributlar esa barça obyektlar uçun umumiy.

Python
class Talaba:
    universitet = "Milliy universitet"   # sinf atributi — hammaga umumiy
    soni = 0

    def __init__(self, ism):
        self.ism = ism                     # obyekt atributi — har biriga alohida
        Talaba.soni += 1


a = Talaba("Aziz")
b = Talaba("Bekzod")
print(a.universitet, "|", b.universitet)
print("Jami talabalar:", Talaba.soni)

Talaba.universitet = "Texnika universiteti"
print(a.universitet)  # özgariş hammaga tegdi

Natija:

Natija
Milliy universitet | Milliy universitet
Jami talabalar: 2
Texnika universiteti

Metodlar

Metod — bu sinf içida yozilgan funksiya. U obyektning maʼlumotlari bilan işlaydi va birinçi parametr sifatida doim self ni oladi.

Python
class Talaba:
    def __init__(self, ism):
        self.ism = ism
        self.baholar = []

    def baho_qosh(self, baho: int):
        self.baholar.append(baho)

    def ortacha(self) -> float:
        if not self.baholar:
            return 0.0
        return sum(self.baholar) / len(self.baholar)

    def tavsif(self) -> str:
        return f"{self.ism}: {len(self.baholar)} ta baho, örtaça {self.ortacha():.1f}"


t = Talaba("Gulnora")
for b in [88, 95, 79]:
    t.baho_qosh(b)
print(t.tavsif())

Natija:

Natija
Gulnora: 3 ta baho, örtaça 87.3

t.baho_qosh(88) yozganingizda Python uni aslida Talaba.baho_qosh(t, 88) deb çaqiradi — self örniga t ning özi uzatiladi.

Amaliyot: bank hisobi

Endi foydali sinf yozamiz. Hisob pul qöyiş va yeçişga ruxsat beradi, lekin balansdan köp pul yeçişga yöl qöymaydi.

Python
class BankHisobi:
    def __init__(self, egasi: str, balans: int = 0):
        self.egasi = egasi
        self.balans = balans
        self.tarix = []

    def qoyish(self, summa: int):
        if summa <= 0:
            raise ValueError("Summa musbat bölişi kerak")
        self.balans += summa
        self.tarix.append(f"+{summa}")

    def yechish(self, summa: int):
        if summa > self.balans:
            raise ValueError(f"Mablağ yetarli emas (balans: {self.balans})")
        self.balans -= summa
        self.tarix.append(f"-{summa}")


hisob = BankHisobi("Sardor", 100_000)
hisob.qoyish(250_000)
hisob.yechish(80_000)
try:
    hisob.yechish(1_000_000)
except ValueError as e:
    print("Rad etildi:", e)

print(f"{hisob.egasi}ning balansi: {hisob.balans:,} söm")
print("Amallar tarixi:", hisob.tarix)

Natija:

Natija
Rad etildi: Mablağ yetarli emas (balans: 270000)
Sardorning balansi: 270,000 söm
Amallar tarixi: ['+250000', '-80000']

Sinf içidagi tekşiruvlar tufayli balans heç qaçon manfiy bölmaydi. Maʼlumot va qoidalarni bitta joyda saqlaş — inkapsulyatsiya deb ataladi va bu OOP ning asosiy foydalaridan biri.

Maxsus (dunder) metodlar

Nomi ikki pastki çiziq bilan boşlanib tugaydigan metodlar (__init__, __str__...) maxsus yoki dunder (double underscore) metodlar deyiladi. Python ularni maʼlum vaziyatlarda özi çaqiradi:

MetodQaçon çaqiriladi
__str__print(obj) va str(obj) — foydalanuvçi uçun çiroyli matn
__repr__Konsolda va röyxat içida — dasturçi uçun aniq tavsif
__len__len(obj)
__eq__obj1 == obj2
__add__obj1 + obj2
__lt__obj1 < obj2 (va saralaş)
Python
class Savat:
    def __init__(self, egasi):
        self.egasi = egasi
        self.mahsulotlar = {}

    def qosh(self, nomi, narx):
        self.mahsulotlar[nomi] = narx

    def __len__(self):
        return len(self.mahsulotlar)

    def __str__(self):
        jami = sum(self.mahsulotlar.values())
        return f"{self.egasi}ning savati: {len(self)} ta mahsulot, jami {jami} söm"

    def __repr__(self):
        return f"Savat({self.egasi!r}, {self.mahsulotlar!r})"


s = Savat("Nilufar")
s.qosh("non", 4000)
s.qosh("qatiq", 9000)
print(s)
print(len(s))
print([s])   # röyxat içida __repr__ işlatiladi

Natija:

Natija
Nilufarning savati: 2 ta mahsulot, jami 13000 söm
2
[Savat('Nilufar', {'non': 4000, 'qatiq': 9000})]

__eq__ va __add__

Standart holatda == ikki obyekt bir xil obyekt ekanini tekşiradi. Agar qiymatlar böyiça soliştirmoqçi bölsangiz, __eq__ ni yozing. __add__ esa + belgisiga maʼno beradi.

Python
class Pul:
    def __init__(self, miqdor: int, valyuta: str = "UZS"):
        self.miqdor = miqdor
        self.valyuta = valyuta

    def __add__(self, boshqa):
        if self.valyuta != boshqa.valyuta:
            raise ValueError("Turli valyutalarni qöşib bölmaydi")
        return Pul(self.miqdor + boshqa.miqdor, self.valyuta)

    def __eq__(self, boshqa):
        return self.miqdor == boshqa.miqdor and self.valyuta == boshqa.valyuta

    def __repr__(self):
        return f"{self.miqdor:,} {self.valyuta}"


oylik = Pul(6_500_000)
bonus = Pul(1_500_000)
print(oylik + bonus)
print(Pul(100, "USD") == Pul(100, "USD"))
print(Pul(100, "USD") == Pul(100, "EUR"))

Natija:

Natija
8,000,000 UZS
True
False

Amaliyot: televizor pulti

Obyekt öz holatini eslab qoladi. Televizorni olaylik: u yoqilgan yoki öçirilgan, hozirgi kanal va ovoz balandligi bor. Pult tugmalari esa şu holatni özgartiradi.

Python
class Televizor:
    MAX_OVOZ = 100

    def __init__(self, kanallar: list[str]):
        self.kanallar = kanallar
        self.yoqilgan = False
        self.kanal = 0
        self.ovoz = 20

    def quvvat(self):
        self.yoqilgan = not self.yoqilgan
        print("📺 Yoqildi" if self.yoqilgan else "📺 Öçirildi")

    def keyingi_kanal(self):
        if self.yoqilgan:
            self.kanal = (self.kanal + 1) % len(self.kanallar)

    def ovoz_qosh(self, qadam: int = 10):
        if self.yoqilgan:
            self.ovoz = min(self.ovoz + qadam, self.MAX_OVOZ)

    def __str__(self):
        if not self.yoqilgan:
            return "Televizor öçiq"
        return f"Kanal: {self.kanallar[self.kanal]} | Ovoz: {self.ovoz}"


tv = Televizor(["Yangiliklar", "Sport", "Kino", "Bolalar"])
tv.keyingi_kanal()          # öçiq paytda heç narsa bölmaydi
print(tv)
tv.quvvat()
for _ in range(5):
    tv.keyingi_kanal()      # 4 ta kanal: oxiridan keyin boşiga qaytadi
for _ in range(10):
    tv.ovoz_qosh()          # 100 dan oşmaydi
print(tv)
tv.quvvat()

Natija:

Natija
Televizor öçiq
📺 Yoqildi
Kanal: Sport | Ovoz: 100
📺 Öçirildi

Meros (inheritance)

Baʼzan bir sinf boşqasining "kengaytirilgan" körinişi böladi. Masalan, menejer ham xodim: uning ismi, maoşi bor, lekin qöşimça ravişda jamoasi bor. Hamma narsani qaytadan yoziş örniga, meros olamiz.

Python
class Xodim:
    def __init__(self, ism: str, maosh: int):
        self.ism = ism
        self.maosh = maosh

    def tanishtir(self):
        return f"Men {self.ism}, maoşim {self.maosh:,} söm"

    def yillik_daromad(self):
        return self.maosh * 12


class Menejer(Xodim):        # Menejer — Xodim ning vorisi
    pass


m = Menejer("Kamola", 12_000_000)
print(m.tanishtir())                 # metod ota sinfdan keldi
print(isinstance(m, Xodim))

Natija:

Natija
Men Kamola, maoşim 12,000,000 söm
True

Menejer(Xodim) yozuvi "Menejer sinfi Xodim dan meros oladi" degani. Xodim — ota sinf (parent), Menejer — voris sinf (child).

Metodni qayta yoziş va super()

Voris sinf ota sinf metodini qayta yozişi (override) mumkin. Ota sinfning asl versiyasini çaqiriş uçun super() işlatiladi — bu takrorlanişdan saqlaydi.

Python
class Xodim:
    def __init__(self, ism: str, maosh: int):
        self.ism = ism
        self.maosh = maosh

    def tanishtir(self):
        return f"Men {self.ism}, maoşim {self.maosh:,} söm"

    def yillik_daromad(self):
        return self.maosh * 12


class Menejer(Xodim):
    def __init__(self, ism: str, maosh: int, bonus_foiz: int):
        super().__init__(ism, maosh)       # ota sinf işini bajaradi
        self.bonus_foiz = bonus_foiz
        self.jamoa: list[Xodim] = []

    def jamoaga_qosh(self, xodim: Xodim):
        self.jamoa.append(xodim)

    def tanishtir(self):                   # qayta yozilgan metod
        asosiy = super().tanishtir()
        return f"{asosiy}. Jamoamda {len(self.jamoa)} kişi bor"

    def yillik_daromad(self):
        return super().yillik_daromad() * (100 + self.bonus_foiz) // 100


ali = Xodim("Ali", 7_000_000)
vali = Xodim("Vali", 8_000_000)
kamola = Menejer("Kamola", 12_000_000, bonus_foiz=20)
kamola.jamoaga_qosh(ali)
kamola.jamoaga_qosh(vali)

for odam in [ali, vali, kamola]:
    print(odam.tanishtir())
    print(f"  Yillik: {odam.yillik_daromad():,} söm")

Natija:

Natija
Men Ali, maoşim 7,000,000 söm
  Yillik: 84,000,000 söm
Men Vali, maoşim 8,000,000 söm
  Yillik: 96,000,000 söm
Men Kamola, maoşim 12,000,000 söm. Jamoamda 2 kişi bor
  Yillik: 172,800,000 söm

Eʼtibor bering: for tsiklida biz tanishtir() ni bir xil çaqirdik, lekin Python har bir obyekt uçun öz sinfidagi versiyani tanladi. Bu polimorfizm deb ataladi.

@property — metodni atribut kabi işlatiş

Baʼzan atributga qiymat berişni nazorat qiliş kerak (masalan, harorat absolyut noldan past bölmasin) yoki qiymat boşqa atributlardan hisoblanadi. @property dekoratori metodni oddiy atribut kabi körsatadi.

Python
class Harorat:
    def __init__(self, selsiy: float):
        self.selsiy = selsiy           # bu yerda ham setter işlaydi

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

    @selsiy.setter
    def selsiy(self, qiymat):
        if qiymat < -273.15:
            raise ValueError("Absolyut noldan past harorat bölmaydi")
        self._selsiy = qiymat

    @property
    def farengeyt(self):               # faqat öqiş uçun, hisoblanadigan
        return self.selsiy * 9 / 5 + 32


h = Harorat(36.6)
print(h.selsiy, "°C =", round(h.farengeyt, 1), "°F")
h.selsiy = 40                          # qavssiz — xuddi atribut kabi
print(h.farengeyt)
try:
    h.selsiy = -300
except ValueError as e:
    print("Xato:", e)

Natija:

Natija
36.6 °C = 97.9 °F
104.0
Xato: Absolyut noldan past harorat bölmaydi

Nomning boşidagi pastki çiziq (_selsiy) — "bu içki atribut, taşqaridan bevosita özgartirmang" degan kelişuv.

@classmethod va @staticmethod

  • @classmethod — birinçi parametr sifatida obyektni emas, sinfning özini (cls) oladi. Köpinça obyekt yaratişning muqobil usullari uçun işlatiladi.
  • @staticmethod — na obyektni, na sinfni oladi. Bu şunçaki sinf içiga mantiqan joylaştirilgan oddiy funksiya.
Python
class Sana:
    def __init__(self, kun: int, oy: int, yil: int):
        self.kun, self.oy, self.yil = kun, oy, yil

    @classmethod
    def matndan(cls, matn: str):
        kun, oy, yil = map(int, matn.split("."))
        return cls(kun, oy, yil)

    @staticmethod
    def kabisa(yil: int) -> bool:
        return yil % 4 == 0 and (yil % 100 != 0 or yil % 400 == 0)

    def __str__(self):
        return f"{self.yil}-{self.oy:02}-{self.kun:02}"


navruz = Sana.matndan("21.03.2024")
print(navruz)
print("2024 kabisa yilimi?", Sana.kabisa(2024))
print("1900 kabisa yilimi?", Sana.kabisa(1900))

Natija:

Natija
2024-03-21
2024 kabisa yilimi? True
1900 kabisa yilimi? False

dataclass — kamroq kod, köproq foyda

Köp sinflar asosan maʼlumot saqlaş uçun yoziladi. Ular uçun har safar __init__, __repr__, __eq__ yoziş zerikarli. dataclasses moduli bu metodlarni avtomatik yaratadi — siz faqat maydonlar va ularning turlarini körsatasiz.

Python
from dataclasses import dataclass, field


@dataclass
class Kitob:
    nomi: str
    muallif: str
    narx: int
    teglar: list[str] = field(default_factory=list)

    def chegirma(self, foiz: int) -> int:
        return self.narx * (100 - foiz) // 100


k1 = Kitob("Ufq", "Said Ahmad", 92_000, ["roman"])
k2 = Kitob("Ufq", "Said Ahmad", 92_000, ["roman"])

print(k1)                 # __repr__ avtomatik
print(k1 == k2)           # __eq__ avtomatik
print("Çegirma bilan:", k1.chegirma(15))

Natija:

Natija
Kitob(nomi='Ufq', muallif='Said Ahmad', narx=92000, teglar=['roman'])
True
Çegirma bilan: 78200

field(default_factory=list) — har bir obyekt uçun yangi böş röyxat yaratadi. Oddiy teglar: list = [] yoziş xato, çunki röyxat barça obyektlar orasida umumiy bölib qolardi.

Xulosa

  • Sinf — andoza, obyekt — şu andoza asosida yaratilgan aniq nusxa; class bilan eʼlon qilinadi.
  • __init__ obyektning boşlanğiç holatini örnatadi; self — joriy obyekt.
  • Obyekt atributlari har bir nusxaga alohida, sinf atributlari esa hammaga umumiy.
  • Maxsus metodlar (__str__, __repr__, __len__, __eq__, __add__) obyektingizni Pythonning öz turlari kabi işlatişga imkon beradi.
  • Meros kodni qayta işlatadi; super() ota sinf metodini çaqiradi, qayta yozilgan metodlar polimorfizmni taʼminlaydi.
  • @property atributni nazorat qiladi, @classmethod/@staticmethod sinf darajasidagi metodlar uçun, @dataclass esa maʼlumot sinflarini qisqa yoziş uçun.

Maşqlar

  1. Törtburçakni ifodalovçi Turtburchak sinfini yozing: eni va boyi atributlari, yuza() va perimetr() metodlari hamda __str__ bölsin. 3 x 5 uçun sinab köring.
Yeçim
Python
class Turtburchak:
    def __init__(self, eni: float, boyi: float):
        self.eni = eni
        self.boyi = boyi

    def yuza(self):
        return self.eni * self.boyi

    def perimetr(self):
        return 2 * (self.eni + self.boyi)

    def __str__(self):
        return f"Törtburçak {self.eni}x{self.boyi}"


t = Turtburchak(3, 5)
print(t, "| yuza:", t.yuza(), "| perimetr:", t.perimetr())
  1. BankHisobi sinfiga otkazma(boshqa_hisob, summa) metodini qöşing: u bu hisobdan pul yeçib, boşqa hisobga qöyadi. Ikki hisob orasida 50 000 söm ötkazib, balanslarni çiqaring.
Yeçim
Python
class BankHisobi:
    def __init__(self, egasi, balans=0):
        self.egasi = egasi
        self.balans = balans

    def qoyish(self, summa):
        self.balans += summa

    def yechish(self, summa):
        if summa > self.balans:
            raise ValueError("Mablağ yetarli emas")
        self.balans -= summa

    def otkazma(self, boshqa, summa):
        self.yechish(summa)
        boshqa.qoyish(summa)


a = BankHisobi("Anvar", 200_000)
b = BankHisobi("Barno", 30_000)
a.otkazma(b, 50_000)
print(a.egasi, a.balans, "|", b.egasi, b.balans)
  1. Vaqt sinfini yozing (soat, daqiqa). __add__ ikki vaqtni qöşsin (daqiqa 60 dan oşsa soatga ötsin), __str__ esa "02:05" körinişida çiqarsin. 1:45 + 2:30 ni hisoblang.
Yeçim
Python
class Vaqt:
    def __init__(self, soat: int, daqiqa: int):
        jami = soat * 60 + daqiqa
        self.soat, self.daqiqa = divmod(jami, 60)

    def __add__(self, boshqa):
        return Vaqt(self.soat + boshqa.soat, self.daqiqa + boshqa.daqiqa)

    def __str__(self):
        return f"{self.soat:02}:{self.daqiqa:02}"


print(Vaqt(1, 45) + Vaqt(2, 30))
  1. Hayvon ota sinfini (ism, ovoz() metodi "..." qaytaradi) va undan meros oluvçi It hamda Mushuk sinflarini yozing. Har biri ovoz() ni qayta yozsin. Röyxatdagi hayvonlarni aylanib, har birining ovozini çiqaring.
Yeçim
Python
class Hayvon:
    def __init__(self, ism):
        self.ism = ism

    def ovoz(self):
        return "..."


class It(Hayvon):
    def ovoz(self):
        return "Vov-vov!"


class Mushuk(Hayvon):
    def ovoz(self):
        return "Miyov!"


for h in [It("Böribosar"), Mushuk("Momiq"), Hayvon("Baliq")]:
    print(f"{h.ism}: {h.ovoz()}")
  1. @dataclass yordamida Mahsulot (nomi, narx, soni) sinfini yarating va jami degan @property qöşing (narx * soni). Bir neçta mahsulotni jami böyiça kamayiş tartibida saralab çiqaring.
Yeçim
Python
from dataclasses import dataclass


@dataclass
class Mahsulot:
    nomi: str
    narx: int
    soni: int

    @property
    def jami(self) -> int:
        return self.narx * self.soni


ombor = [Mahsulot("guruç", 18_000, 10), Mahsulot("yoğ", 25_000, 3), Mahsulot("çoy", 30_000, 7)]
for m in sorted(ombor, key=lambda m: m.jami, reverse=True):
    print(f"{m.nomi:<8} {m.jami:>8}")