16-bo‘lim

OOP - meros, inkapsulyatsiya, polimorfizm

Obyektga yo'naltirilgan dasturlashning uchta ustuni: meros olish, ma'lumotni yashirish, bir xil interfeys va abstrakt klasslar.

🕑 10 daqiqa o‘qish 📄 425 so‘z 👁 5 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Meros (inheritance)
  2. super() nima qiladi?
  3. Polimorfizm
  4. Inkapsulyatsiya
  5. Abstrakt klasslar
  6. Ko'p martalik meros
  7. isinstance va issubclass
  8. Kompozitsiya - merosga muqobil
  9. Xulosa

O'tgan bo'limda klass yaratishni o'rgandik. Endi OOP ning haqiqiy kuchini beradigan uchta tamoyilni ko'ramiz.

Meros (inheritance) #

Meros - mavjud klass asosida yangisini yaratish. Takrorlanuvchi kodni yozmaslikning eng samarali usuli.

Xodim ism, oylik malumot(), oylik_hisobla() Dasturchi + til + tajriba oylik_hisobla() qayta Menejer + jamoa_soni + bonus oylik_hisobla() qayta Amaliyotchi + oquv_muddati + murabbiy oylik_hisobla() qayta
Uchala klass ham "Xodim" dan meros oladi, lekin oylikni har biri o'zicha hisoblaydi
Python
class Xodim:
    """Barcha xodimlar uchun asosiy klass."""

    def __init__(self, ism, oylik):
        self.ism = ism
        self.oylik = oylik

    def malumot(self):
        return f"{self.ism} - {self.oylik:,} so'm"

    def oylik_hisobla(self):
        return self.oylik


class Dasturchi(Xodim):
    """Xodim klassidan meros olgan dasturchi."""

    def __init__(self, ism, oylik, til, tajriba_yil):
        super().__init__(ism, oylik)     # ota klass konstruktorini chaqiramiz
        self.til = til
        self.tajriba_yil = tajriba_yil

    def oylik_hisobla(self):
        """Har bir tajriba yili uchun 5% ustama."""
        ustama = self.oylik * 0.05 * self.tajriba_yil
        return self.oylik + ustama

    def kod_yozish(self):
        return f"{self.ism} {self.til} tilida kod yozmoqda..."


dasturchi = Dasturchi("Husanboy", 12_000_000, "Python", 3)

print(dasturchi.malumot())            # ota klassdan meros
print(dasturchi.kod_yozish())         # o'ziga xos metod
print(f"{dasturchi.oylik_hisobla():,.0f} so'm")   # qayta yozilgan metod
Natija
Husanboy - 12,000,000 so'm
Husanboy Python tilida kod yozmoqda...
13,800,000 so'm

super() nima qiladi? #

super() ota klassga murojaat qiladi. Uni chaqirish odatda majburiy:

Python
class Menejer(Xodim):
    def __init__(self, ism, oylik, jamoa_soni):
        super().__init__(ism, oylik)      # ism va oylik ota klassda o'rnatiladi
        self.jamoa_soni = jamoa_soni      # bu esa faqat menejerda bor

    def oylik_hisobla(self):
        """Jamoadagi har bir odam uchun 500 000 bonus."""
        bonus = self.jamoa_soni * 500_000
        return super().oylik_hisobla() + bonus     # ota metodni ham ishlatamiz


menejer = Menejer("Sardor", 15_000_000, 8)
print(f"{menejer.oylik_hisobla():,} so'm")
Natija
19,000,000 so'm

Polimorfizm #

Polimorfizm - "ko'p shakllilik". Bir xil metod nomi turli klasslarda turlicha ishlaydi.

Python
class Amaliyotchi(Xodim):
    def __init__(self, ism, oylik, murabbiy):
        super().__init__(ism, oylik)
        self.murabbiy = murabbiy

    def oylik_hisobla(self):
        return self.oylik * 0.5      # amaliyotchi yarim stavka oladi


jamoa = [
    Dasturchi("Husanboy", 12_000_000, "Python", 3),
    Menejer("Sardor", 15_000_000, 8),
    Amaliyotchi("Malika", 5_000_000, "Husanboy"),
    Xodim("Bekzod", 8_000_000),
]

print("=" * 50)
print(f"{'OYLIK VEDOMOSTI':^50}")
print("=" * 50)
print(f"{'Xodim':<16}{'Lavozim':<16}{'Oylik':>18}")
print("-" * 50)

jami = 0
for xodim in jamoa:
    # Bir xil metod - har bir klass o'zicha bajaradi
    summa = xodim.oylik_hisobla()
    jami += summa
    lavozim = type(xodim).__name__
    print(f"{xodim.ism:<16}{lavozim:<16}{summa:>18,.0f}")

print("-" * 50)
print(f"{'JAMI:':<32}{jami:>18,.0f}")
print("=" * 50)
Natija
==================================================
                 OYLIK VEDOMOSTI                  
==================================================
Xodim           Lavozim                      Oylik
--------------------------------------------------
Husanboy        Dasturchi               13,800,000
Sardor          Menejer                 19,000,000
Malika          Amaliyotchi              2,500,000
Bekzod          Xodim                    8,000,000
--------------------------------------------------
JAMI:                                   43,300,000
==================================================
Polimorfizmning kuchi shu yerda

Siklda if isinstance(xodim, Dasturchi): ... kabi tekshiruvlar yo'q. Yangi xodim turi qo'shsangiz, sikl kodini umuman o'zgartirmaysiz. Bu OOP ning eng katta amaliy foydasi.

Inkapsulyatsiya #

Inkapsulyatsiya - ichki tafsilotlarni yashirish. Python'da bu kelishuv orqali amalga oshiriladi:

YozuvMa'nosiTashqaridan kirish
ismOchiq (public)Mumkin
_ismHimoyalangan (protected)Texnik jihatdan mumkin, lekin tegmang
__ismYopiq (private)Nomi o'zgartiriladi, kirish qiyin
Python
class Hisob:
    def __init__(self, egasi, balans):
        self.egasi = egasi              # ochiq
        self._bank_kodi = "SFRM"        # ichki foydalanish uchun
        self.__pin = "1234"             # yopiq

    def pin_tekshir(self, kiritilgan):
        return self.__pin == kiritilgan

    def pin_ozgartir(self, eski, yangi):
        if not self.pin_tekshir(eski):
            raise ValueError("Eski PIN noto'g'ri")
        if len(yangi) != 4 or not yangi.isdigit():
            raise ValueError("PIN 4 ta raqamdan iborat bo'lishi kerak")
        self.__pin = yangi
        return "PIN o'zgartirildi"


hisob = Hisob("Husanboy", 500_000)

print(hisob.egasi)              # ochiq - muammosiz
print(hisob._bank_kodi)         # ishlaydi, lekin yaxshi amaliyot emas

try:
    print(hisob.__pin)          # yopiq - xato
except AttributeError as xato:
    print(f"Xato: {xato}")

print(hisob.pin_tekshir("1234"))
print(hisob.pin_ozgartir("1234", "9876"))
Natija
Husanboy
SFRM
Xato: 'Hisob' object has no attribute '__pin'
True
PIN o'zgartirildi
Python'da to'liq yopiqlik yo'q

__pin aslida _Hisob__pin nomiga o'zgartiriladi (name mangling) va uni hisob._Hisob__pin orqali o'qish mumkin. Python falsafasi: "biz hammamiz kattalarmiz" - kodda niyat ko'rsatiladi, majburlash emas. Pastki chiziq - "bunga tegmang" degan belgi.

Abstrakt klasslar #

Ba'zan asosiy klassdan obyekt yaratish mantiqsiz. "Xodim" - mavhum tushuncha, aniq lavozim bo'lishi kerak.

Python
from abc import ABC, abstractmethod


class Shakl(ABC):
    """Barcha geometrik shakllar uchun abstrakt asos."""

    def __init__(self, nomi):
        self.nomi = nomi

    @abstractmethod
    def yuza(self):
        """Har bir shakl buni o'zi amalga oshirishi SHART."""
        pass

    @abstractmethod
    def perimetr(self):
        pass

    def hisobot(self):
        """Bu metod barcha shakllar uchun umumiy."""
        return (f"{self.nomi:<14} yuza: {self.yuza():>9.2f}   "
                f"perimetr: {self.perimetr():>9.2f}")


class Togriturtburchak(Shakl):
    def __init__(self, eni, boyi):
        super().__init__("To'rtburchak")
        self.eni = eni
        self.boyi = boyi

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

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


class Aylana(Shakl):
    PI = 3.14159

    def __init__(self, radius):
        super().__init__("Aylana")
        self.radius = radius

    def yuza(self):
        return self.PI * self.radius ** 2

    def perimetr(self):
        return 2 * self.PI * self.radius


class Uchburchak(Shakl):
    def __init__(self, a, b, c):
        super().__init__("Uchburchak")
        self.a, self.b, self.c = a, b, c

    def yuza(self):
        yarim = self.perimetr() / 2
        return (yarim * (yarim - self.a) * (yarim - self.b) * (yarim - self.c)) ** 0.5

    def perimetr(self):
        return self.a + self.b + self.c


shakllar = [
    Togriturtburchak(10, 5),
    Aylana(7),
    Uchburchak(3, 4, 5),
]

for shakl in shakllar:
    print(shakl.hisobot())

print()
print(f"Jami yuza: {sum(s.yuza() for s in shakllar):.2f}")

# Abstrakt klassdan obyekt yaratib bo'lmaydi
try:
    Shakl("Noaniq")
except TypeError as xato:
    print(f"\nXato: {xato}")
Natija
To'rtburchak   yuza:     50.00   perimetr:     30.00
Aylana         yuza:    153.94   perimetr:     43.98
Uchburchak     yuza:      6.00   perimetr:     12.00

Jami yuza: 209.94

Xato: Can't instantiate abstract class Shakl without an implementation for abstract methods 'perimetr', 'yuza'

Ko'p martalik meros #

Python bir nechta klassdan meros olishga ruxsat beradi:

Python
class Yozuvchi:
    def yozish(self):
        return "Faylga yozilmoqda..."


class Oquvchi:
    def oqish(self):
        return "Fayldan o'qilmoqda..."


class FaylBoshqaruvchi(Yozuvchi, Oquvchi):
    """Ikkala imkoniyatni ham oladi."""
    def __init__(self, nomi):
        self.nomi = nomi


boshqaruvchi = FaylBoshqaruvchi("hisobot.txt")
print(boshqaruvchi.yozish())
print(boshqaruvchi.oqish())
print(FaylBoshqaruvchi.__mro__)
Natija
Faylga yozilmoqda...
Fayldan o'qilmoqda...
(<class '__main__.FaylBoshqaruvchi'>, <class '__main__.Yozuvchi'>, <class '__main__.Oquvchi'>, <class 'object'>)
Ko'p martalik merosdan ehtiyot bo'ling

Ikkala ota klassda bir xil nomli metod bo'lsa, Python MRO (Method Resolution Order) qoidasiga ko'ra chapdagisini tanlaydi. Bu chalkashlikka olib keladi. Amalda ko'pincha kompozitsiya (obyektni atribut sifatida saqlash) yaxshiroq yechim bo'ladi.

isinstance va issubclass #

Python
dasturchi = Dasturchi("Husanboy", 12_000_000, "Python", 3)

print(isinstance(dasturchi, Dasturchi))     # aynan shu klassmi
print(isinstance(dasturchi, Xodim))         # ota klass ham hisoblanadi
print(isinstance(dasturchi, Menejer))
print(issubclass(Dasturchi, Xodim))
Natija
True
True
False
True

Kompozitsiya - merosga muqobil #

Python
class Dvigatel:
    def __init__(self, quvvat):
        self.quvvat = quvvat

    def ishga_tushir(self):
        return f"Dvigatel ishga tushdi ({self.quvvat} ot kuchi)"


class Mashina:
    """Mashina dvigateldan meros olmaydi - u dvigatelga EGA."""

    def __init__(self, model, dvigatel_quvvati):
        self.model = model
        self.dvigatel = Dvigatel(dvigatel_quvvati)     # kompozitsiya

    def haydash(self):
        return f"{self.model}: {self.dvigatel.ishga_tushir()}"


mashina = Mashina("Cobalt", 106)
print(mashina.haydash())
Natija
Cobalt: Dvigatel ishga tushdi (106 ot kuchi)
"is-a" va "has-a" qoidasi
  • Dasturchi bu xodim (is-a) → meros.
  • Mashinada bor dvigatel (has-a) → kompozitsiya.

Shubhalansangiz, kompozitsiyani tanlang - u moslashuvchanroq.

Amaliy topshiriq

Transport abstrakt klassini yarating:

  1. Abstrakt metodlar: tezlik_hisobla() va yoqilgi_sarfi().
  2. Umumiy metod: malumot().
  3. Undan Avtomobil, Velosiped va Samolyot klasslarini meros oling.
  4. Har biri abstrakt metodlarni o'zicha bajarsin.
  5. Uchala obyektni ro'yxatga solib, siklda malumot() ni chaqiring - polimorfizmni ko'ring.

Xulosa #

  • Meros kodni takrorlamaslik imkonini beradi; super() ota klassga murojaat qiladi.
  • Polimorfizm - bir xil metod turli klasslarda turlicha ishlaydi, if tekshiruvlari kerak emas.
  • Inkapsulyatsiya Python'da kelishuv orqali: _himoyalangan, __yopiq.
  • Abstrakt klass (ABC) avlodlarni ma'lum metodlarni yozishga majbur qiladi.
  • "is-a" bo'lsa meros, "has-a" bo'lsa kompozitsiya tanlang.

Keyingi bo'limda Python'ning ilg'or imkoniyatlarini - generatorlar va dekoratorlarni ko'ramiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.