Leksiya 7 · 2-qism

Pythonda OOP

Polimorfizm, inkapsulyatsiya va uzilishlarga chidamli bank tizimlarini loyihalash

Bugun biz moslashuvchan va himoyalangan arxitekturalarni qanday loyihalashni koʻrib chiqamiz. Fintex sohasidan olingan haqiqiy misollarda - murakkab maʼlumotlar oqimini qanday boshqarish, parollarni loglarga tushishdan himoya qilish va oʻnlab toʻlov shlyuzlarini yagona interfeys orqali ishini tashkil etish.

6-leksiya takrori

Asosiy tushunchalar

Oʻtgan darsda biz poydevorni qoʻydik: holat va xatti-harakatni klasslarga joylashni oʻrgandik, hamda vorislik bilan tanishdik.

Murakkabroq tushunchalarga - dinamik polimorfizm va inkapsulyatsiyaga - oʻtishdan oldin terminologiyani bazaviy misollarda mustahkamlab olish zarur. Bu poydevorsiz MRO va Name Mangling mexanizmlarida osongina chalkashib ketish mumkin.

Takror

Klasslarning vazifasi

Bank hisoblarini yuritish misolida maʼlumotlarni boshqarishning ikki yondashuvini solishtiramiz.

Protsedura yondashuvi
# maʼlumot va mantiq bir-biriga bogʻlanmagan; # masshtablashda - qiyin acc1_number = "44001122" acc1_balance = 50000.0 acc2_number = "55003344" acc2_balance = 1200.0
OOP yondashuvi
class BankAccount: def __init__(self, number: str, balance: float): self.number = number self.balance = balance acc1 = BankAccount("44001122", 50000.0)

Klass - moliyaviy holat va u bilan ishlash mantigʻini yagona tuzilmaga bogʻlovchi foydalanuvchi maʼlumot turi.

Takror

Klass va nusxa

Klass

Blueprint

Tuzilmaning dasturiy tavsifi. Oʻzi mijozning aniq maʼlumotlarini saqlamaydi va maʼlumot uchun xotira egallamaydi.

Nusxa

Instance

Xotiradagi aniq obyekt - masalan, haqiqiy balansga ega mijoz hisobi. Bitta klassdan millionlab izolyatsiyalangan obyektlar yaratiladi.

Takror

Atributlar va metodlar

1

Atributlar

Obyekt ichidagi, uning joriy holatini aks ettiruvchi oʻzgaruvchilar: account_number, currency, is_blocked.

2

Metodlar

Klass ichidagi, obyekt holatiga kirish huquqiga ega funksiyalar: withdraw(), block_account().

💡 Yaxshi loyihalangan klass oʻz atributlarini mustaqil boshqaradi, tashqi kod esa u bilan faqat metod chaqiruvlari orqali muloqot qiladi.

Takror

__init__ metodi va self argumenti

class BankAccount: def __init__(self, account_number: str, balance: float): print(f"Xotira ajratildi. self quyidagi manzilga ishora qiladi: {id(self)}") self.account_number = account_number self.balance = balance my_acc = BankAccount("44001122", 1000.0)
Xotira ajratildi. self quyidagi manzilga ishora qiladi: 140237482390224

__init__ - obyekt konstruktori, xotira ajratilgandan soʻng avtomatik chaqiriladi. self - joriy nusxaga oshkora havola.

Kontekst

Python self vs Java this

Python

Oshkora eʼlon

def deposit(self, amount: float): self.balance += amount

self har bir metodning birinchi argumenti sifatida yoziladi. "Oshkora yashirinidan yaxshiroq".

Java

Yashirin uzatish

public void deposit(double amount) { this.balance += amount; }

JVM havolani avtomatik uzatadi, ichida esa u this orqali mavjud boʻladi.

Takror

Obyektlarning nom fazolari izolyatsiyasi

class BankAccount: def __init__(self, owner: str, balance: float): self.owner = owner self.balance = balance alice_acc = BankAccount("Alice", 500.0) bob_acc = BankAccount("Bob", 10000.0) alice_acc.balance -= 50.0 print(f"Alice balansi: {alice_acc.balance}") print(f"Bob balansi: {bob_acc.balance}")
Alice balansi: 450.0 Bob balansi: 10000.0

Takror

__str__ metodi

class BankAccount: def __init__(self, owner: str, balance: float): self.owner = owner self.balance = balance def __str__(self) -> str: return f"Mijoz hisobi: {self.owner} | Balans: {self.balance} USD" acc = BankAccount("Oleg", 7500.0) print(acc)
Mijoz hisobi: Oleg | Balans: 7500.0 USD

Takror

Metod signaturalari

class BankAccount: def __init__(self, balance: float): self.balance = balance def transfer(self, target_acc: 'BankAccount', amount: float) -> bool: if self.balance >= amount: self.balance -= amount target_acc.balance += amount return True return False acc_a = BankAccount(100.0) acc_b = BankAccount(20.0) success = acc_a.transfer(acc_b, 50.0) print(f"Tranzaksiya holati: {success} | Qoldiq: {acc_a.balance}")
Tranzaksiya holati: True | Qoldiq: 50.0

Takror

Vorislik va super()

class BasicCard: def __init__(self, holder: str, number: str): self.holder = holder self.number = number class PremiumCard(BasicCard): def __init__(self, holder: str, number: str, cashback_rate: float): super().__init__(holder, number) self.cashback_rate = cashback_rate vip = PremiumCard("Asel", "4400...", 0.05) print(f"Karta: {vip.holder}, Keshbek: {vip.cashback_rate * 100}%")
Karta: Asel, Keshbek: 5.0%

Takror

Pythonda OOP hamma joyda

"card_verified".upper() # str klassi nusxasidagi metod balances.append(500.0) # list nusxasi holatini oʻzgartirish df.dropna() # pandas dagi DataFrame obyekti metodi api.send_payment(payload) # requests / httpx orqali HTTP-sessiya mijozi

Python bilan ishlaganda siz OOP dan har daqiqada foydalanasiz - nuqta orqali deyarli har bir chaqiruv - bu obyekt metodi.

Takror

Umumiy jadval

TushunchaQisqachaArxitekturadagi maʼnosi
Klass / NusxaTuzilma tavsifi vs RAMdagi obyektMahsulot shabloni va mijozning haqiqiy hisobi
Atribut / MetodHolat oʻzgaruvchisi vs funksiyaBalans (maʼlumot) va oʻtkazma metodi (xatti-harakat)
__init__ / selfKonstruktor va kontekstga havolaYaratishdagi kirish nuqtasi va nusxaga havola
Vorislik / super()Nusxa-koʻchirmasdan kengaytirishBasicAccount asosida CreditAccount

Kurs xaritasi

OOP ni oʻzlashtirishning joriy holati

TushunchaHolatBosqich
Vorislik🟢 OʻRGANILDIKlasslarni kengaytirish sintaksisi va super().
Polimorfizm🟡 JORIY MAVZUTurli toʻlov shlyuzlari uchun yagona interfeys.
Inkapsulyatsiya⚪ KUTILMOQDAKritik maʼlumotlarni yashirish va validatsiya.
Abstraksiya⚪ KUTILMOQDABiznes-mantiqni amalga oshirish tafsilotlaridan ajratish.

Bugun

7-leksiya rejasi

  • Polimorfizm - metodlarni qayta aniqlash, duck typing, MRO va romb shaklidagi vorislik
  • Inkapsulyatsiya - _ va __ modifikatorlari, @property, setterlar
  • Klass darajasidagi vositalar - @staticmethod, @classmethod
  • Sehrli metodlar - foydalanuvchi obyektlari uchun +, ==, len() operatorlari
  • Istisnolar - foydalanuvchi xato klasslari

Polimorfizm

Polimorfizm nima?

Kassadagi toʻlov terminalini tasavvur qiling. Unga mijoz aynan nimani yaqinlashtirishi farqsiz: UzCard plastik kartasi, Humo Pay'li smartfon yoki Milly Bank'dan QR-kod. Terminal uchun bitta interfeys mavjud - "toʻlovni qabul qilish". Yechib olish aynan qanday roʻy berishi esa aniq toʻlov vositasiga bogʻliq.

Polimorfizm - dasturning turli obyektlar bilan yagona interfeys orqali, faqat metod nomiga tayanib, obyektning ichki tuzilishiga emas, muloqot qila olish qobiliyati.

Polimorfizm

Polimorfizmni amalga oshirishning ikki yoʻli

1

Klassik

Vorislik va qayta aniqlash orqali. Ota-klass yaratiladi; vorislar bir xil nomli, ammo oʻz mantigʻiga ega metodni amalga oshiradi.

2

Dinamik

Duck typing. Vorislik talab qilinmaydi: mustaqil klasslarda bir xil nomli metod boʻlsa, uni polimorf tarzda chaqirish mumkin.

Polimorfizm

Pythonda polimorfizm mexanizmlari

┌──────────────────────────┐ │ POLIMORFIZM (tushuncha) │ └────────────┬─────────────┘ │ ┌──────────────────┴──────────────────┐ ▼ ▼ ┌──────────────────────────────┐ ┌──────────────────────────────┐ │ Klassik (ierarxik) │ │ Dinamik (duck typing) │ ├──────────────────────────────┤ ├──────────────────────────────┤ │ Umumiy ota-klass kerak │ │ Vorislik talab qilinmaydi │ │ Ota metodlarini qayta aniql. │ │ Faqat metod mavjudligi muhim │ │ Klasslar ierarxiyada bogʻliq │ │ Klasslar toʻliq mustaqil │ └──────────────────────────────┘ └──────────────────────────────┘

Klassik polimorfizm

Qayta aniqlash qanday ishlaydi

Qayta aniqlash - bu voris-klass ota-klassdagi bilan bir xil nomli metodni eʼlon qilib, uni oʻzicha amalga oshirishidir.

class Parent: def greet(self): print("Ota-klassdan salom") class Child(Parent): def greet(self): # bir xil nom - oʻz amalga oshirilishi print("Vorisdan salom") Child().greet()
Vorisdan salom

obj.greet() chaqirilganda Python metodni obyektning oʻz klassidan boshlab qidiradi. Vorisdagi versiya ota-klassnikini "yopadi". super().greet() esa qoʻshimcha ravishda ota-klass versiyasini ham bajarishga imkon beradi.

Klassik polimorfizm

Metodlarni qayta aniqlash

Ota-klass umumiy mantiqni saqlaydi. Voris-klass metodni qayta aniqlaydi:

class PaymentGateway: def process_payment(self, amount: float) -> None: print(f"[Core-Banking] {amount} soʻmga tranzaksiya qayd etilmoqda") class UzCardGateway(PaymentGateway): def process_payment(self, amount: float) -> None: super().process_payment(amount) # avval yadro mantigʻi print(f"[UzCard API] QR-kod generatsiyasi. {amount} soʻm yechilmoqda.") # soʻngra oʻziniki

super() ota-klass versiyasini chaqiradi. UzCardGateway umumiy yadro mantigʻini yoʻqotmasdan oʻz xatti-harakatini qoʻshadi.

Klassik polimorfizm

Xuddi shu shablon - boshqa shlyuzlar

Humo va Milly Bank metodni xuddi shunday qayta aniqlaydi. run_checkout istalgan shlyuz bilan ishlaydi:

class HumoGateway(PaymentGateway): def process_payment(self, amount: float) -> None: super().process_payment(amount) print(f"[Humo API] Ekvayringga soʻrov. {amount} soʻm yechilmoqda.") class MillyBankGateway(PaymentGateway): def process_payment(self, amount: float) -> None: super().process_payment(amount) print(f"[Milly Bank API] Onlayn toʻlov. Summa: {amount} soʻm.") def run_checkout(gateway: PaymentGateway, total: float): gateway.process_payment(total) # funksiya shlyuz turini bilmaydi run_checkout(UzCardGateway(), 150000.0) run_checkout(HumoGateway(), 320000.0)

Metodlarni qayta aniqlash

Chaqiruv natijasi

[Core-Banking] 150000.0 soʻmga tranzaksiya qayd etilmoqda [UzCard API] QR-kod generatsiyasi. 150000.0 soʻm yechilmoqda. [Core-Banking] 320000.0 soʻmga tranzaksiya qayd etilmoqda [Humo API] Ekvayringga soʻrov. 320000.0 soʻm yechilmoqda.

Har bir shlyuz process_payment ni oʻzicha qayta aniqlaydi. super() avval umumiy yadro mantigʻini (logga yozish), soʻngra - aniq shlyuz kodini bajaradi. run_checkout funksiyasi yagona metodni chaqiradi va unga qaysi klass uzatilganini bilmaydi.

Qayta aniqlashni mustahkamlaymiz

Oʻtkazma uchun komissiyani hisoblash

class BasicCardTariff: def calculate_fee(self, amount: float) -> float: return amount * 0.015 # 1.5% - standart komissiya class PremiumCardTariff(BasicCardTariff): def calculate_fee(self, amount: float) -> float: return amount * 0.005 # 0.5% - premium mijozlar uchun class CorporateCardTariff(BasicCardTariff): def calculate_fee(self, amount: float) -> float: return 50000.0 # yuridik shaxslar uchun fiks tariffs_pool = [BasicCardTariff(), PremiumCardTariff(), CorporateCardTariff()] for tariff in tariffs_pool: fee = tariff.calculate_fee(10_000_000.0) print(f"Tarif: {tariff.__class__.__name__} | Komissiya: {fee} soʻm")
Tarif: BasicCardTariff | Komissiya: 150000.0 soʻm Tarif: PremiumCardTariff | Komissiya: 50000.0 soʻm Tarif: CorporateCardTariff | Komissiya: 50000.0 soʻm

Uch tarif calculate_fee ni oʻz formulasi bilan qayta aniqlaydi. Sikl xuddi shu metodni chaqiradi - Python obyekt turiga qarab kerakli amalga oshirilishni oʻzi qoʻyadi.

Boshqa tillarda

Qayta aniqlashni solishtirish: Python vs Java

MezonPythonJava
Standart rejimBarcha metodlar virtual. Vorisdagi nom mosligi ota-klass metodini qayta aniqlaydi.Signaturalarning mosligini talab qiladi. @Override annotatsiyasi tavsiya etiladi.
Turlarni nazorat qilishRuntime darajasida yoʻq.Kompilyator nazorat qiladi.
Qidiruv tamoyiliBajarilish payti MRO boʻylab dinamik qidiruv.Kompilyatsiya bosqichida virtual metodlar jadvali (VMT) boʻylab.

Qayta aniqlash xavfi

Moʻrt ota-klass

class DocumentProcessor: def format_inn(self, raw_inn: str) -> str: return raw_inn.strip() # str qaytishini kafolatlaydi def register_legal_entity(self, inn: str) -> str: clean_inn = self.format_inn(inn) return f"Kompaniyani roʻyxatga olish, INN: {clean_inn.upper()}" class BadCustomProcessor(DocumentProcessor): def format_inn(self, raw_inn: str) -> None: # super() chaqirilmadi; return yoʻq -> metod None qaytaradi print(f"[Log] INN tekshiruviga soʻrov: {raw_inn}") processor = BadCustomProcessor() try: processor.register_legal_entity(" 123456789012 ") except AttributeError as error: print(f"RUNTIMEDA XATO: {error}")
[Log] INN tekshiruviga soʻrov: 123456789012 RUNTIMEDA XATO: 'NoneType' object has no attribute 'upper'

Sababi: format_inn None qaytardi, None.upper() chaqiruvi esa xato bilan tugadi - NoneType turida bu metod yoʻq.

Qayta aniqlash xavfi

Metod nomidagi xato

class NotificationService: def send_critical_alert(self, msg: str): print(f"[Fallback SMS] Kritik xato: {msg}") class TelegramService(NotificationService): def send_criticall_alert(self, msg: str): # xato: ikkita 'l' print(f"[Telegram Bot] Xato chatga yuborildi: {msg}") notifier = TelegramService() notifier.send_critical_alert("Avtorizatsiya serveri yiqildi!")
[Fallback SMS] Kritik xato: Avtorizatsiya serveri yiqildi!

Kompilyatsiya qilinadigan tillarda bunday xato yigʻish (build) xatosini chaqirardi. Python esa indamay yangi metod yaratadi va ota-klassnikini chaqiradi.

Turlar ierarxiyasi

object nima

object - bu Python butun turlar tizimining poydevori sifatida oʻzi yaratgan oʻrnatilgan klass. U sizning kodingizdan ham oldin mavjud: uni na aniqlash, na import qilish kerak.

print(object) # ildiz klassning oʻzi print(type(object)) # uning turi print(int.__bases__) # hatto int ham - object vorisi
<class 'object'> <class 'type'> (<class 'object'>,)

Pythondagi istalgan klass - oxir-oqibat object vorisi. Aynan undan barcha obyektlar bazaviy imkoniyatlarni oladi: __init__, __str__, __eq__, __hash__ va boshqalar.

Turlar ierarxiyasi

Python object ni avtomatik qoʻyadi

Siz ota-klassni koʻrsatmasdan klass yozsangiz, Python indamay object ni ota-klass sifatida qoʻshadi. Quyidagi ikki yozuv toʻliq bir xil:

class Account: # biz shunday yozamiz pass class Account(object): # Python buni shunday koʻradi pass print(Account.__bases__) # biz yozmasak ham ota-klass mavjud print(issubclass(Account, object))
(<class 'object'>,) True

object bilan qavslarni yozmaslik mumkin - Python uni oʻzi qoʻyadi. Shu sababli biz kodda ota-klassni "koʻrmaymiz", ammo u doimo oʻsha yerda.

Turlar ierarxiyasi

Klass object dan nimani oladi

class SimpleEntity: pass entity = SimpleEntity() print(f"Klassning bazaviy ajdodlari: {SimpleEntity.__bases__}") print(f"__str__: {entity.__str__()}") print(f"__eq__: {entity.__eq__(entity)}") print(f"__dir__: {len(entity.__dir__())} ta element")
Klassning bazaviy ajdodlari: (<class 'object'>,) __str__: <__main__.SimpleEntity object at 0x104b2b9a0> __eq__: True __dir__: 26 ta element

Agar ota-klass oshkora koʻrsatilmasa, Python object dan voris qiladi - butun ierarxiyaning choʻqqisidan.

Boshqa tillarda

Python object vs Java java.lang.Object

ParametrPythonJava
Avto-bogʻlanishIstalgan foydalanuvchi ierarxiyasini yopadi.Obyekt modelining mutlaq ildizi.
Bazaviy toʻplam__new__, __init__, __repr__, __eq__.toString(), equals(), hashCode().
Koʻp vorislikMROning yakuniy nuqtasi.Qoʻllanilmaydi - Java faqat yagona vorislikni qoʻllaydi.

Ierarxiyalar ziddiyati

Romb shaklidagi vorislik (Diamond Problem)

┌────────────────┐ │ BaseValidator │ validate() ni eʼlon qiladi └───────┬────────┘ ┌─────────┴─────────┐ ▼ ▼ ┌───────────────┐ ┌───────────────┐ │ CardCheck │ │ LimitCheck │ ikkalasi qayta aniqladi └───────┬───────┘ └───────┬───────┘ └─────────┬─────────┘ ▼ ┌───────────────┐ │ FullCheck │ kimning validate() versiyasi? └───────────────┘

Chuqur tahlil

MRO - metodlarni hal qilish tartibi

Python C3-linearizatsiyadan foydalanadi: metod chaqirilganda klasslar MRO tartibida koʻrib chiqiladi, birinchi topilgani bajariladi.

class BaseValidator: def validate(self): print("Amalga oshirish: BaseValidator") class CardCheck(BaseValidator): def validate(self): print("Amalga oshirish: CardCheck") class LimitCheck(BaseValidator): def validate(self): print("Amalga oshirish: LimitCheck") class FullCheck(CardCheck, LimitCheck): pass checker = FullCheck() checker.validate() for i, cls in enumerate(FullCheck.__mro__, start=1): print(f"{i}. {cls.__name__}")
Amalga oshirish: CardCheck 1. FullCheck 2. CardCheck 3. LimitCheck 4. BaseValidator 5. object

Kooperativ vorislik

super() va MRO

super() metodni "eʼlondagi ota-klass" da emas, joriy obyektning MRO zanjiridagi keyingi klassda chaqiradi.

class Base: def __init__(self): print("Base initsializatsiyasi") class Left(Base): def __init__(self): print("Left start"); super().__init__(); print("Left tugadi") class Right(Base): def __init__(self): print("Right start"); super().__init__(); print("Right tugadi") class Child(Left, Right): def __init__(self): print("Child start"); super().__init__(); print("Child tugadi") instance = Child() # MRO: Child -> Left -> Right -> Base
Child start -> Left start -> Right start -> Base initsializatsiyasi -> Right tugadi -> Left tugadi -> Child tugadi

Left ichidagi super() boshqaruvni Right ga uzatdi, garchi ular bevosita bogʻliq boʻlmasa ham - bu kooperativ koʻp vorislik.

Ierarxiyalar ziddiyati

Linearizatsiya xatolari

class X: pass class Y(X): pass try: class InvalidSystem(X, Y): # X oʻz vorisi Y dan oldin tura olmaydi pass except TypeError as error: print(f"Ierarxiyani qurish xatosi:\n{error}")
Ierarxiyani qurish xatosi: Cannot create a consistent method resolution order (MRO) for bases X, Y

Polimorfizm

Chaqiruvlarni Runtimeda hal qilish

gateways = [UzCardGateway(), HumoGateway(), MillyBankGateway()] def run_billing(gateway_instance, sum_to_charge): # Python turni oldindan tekshirmaydi. # Chaqiruv payti u uzatilgan nusxaning MRO sini koʻrib chiqadi. gateway_instance.process_payment(sum_to_charge) for gw in gateways: run_billing(gw, 100000.0)

Polimorfizm

Toʻplamlarni polimorf qayta ishlash

Yagona interfeys turli xil obyektlar toʻplamini oddiy sikllarda - if isinstance() orqali tur tekshiruvlarisiz - qayta ishlashga imkon beradi.

processing_pool = [UzCardGateway(), HumoGateway(), MillyBankGateway(), UzCardGateway()] for gateway in processing_pool: gateway.process_payment(50000.0) print("--- Tranzaksiya logi saqlandi ---")

Arxitektura

Dasturiy interfeys kontrakt sifatida

Arxitektura nuqtai nazaridan polimorfizm biznes-mantiqni aniq vendorlarning texnik tafsilotlaridan izolyatsiya qiladi.

Yangi shlyuz (masalan, Click yoki Payme) ulanganda yadro kodini oʻzgartirish talab qilinmaydi. PaymentGateway voris klassini yaratish va process_payment ni amalga oshirish kifoya. Qolgani avtomatik ishlaydi.

Yakunlar

Metodlarni qayta aniqlash - rezyume

QoidaNega muhim
Metod nomini aniq saqlangXato yangi metod yaratadi, eskisi esa indamay ishlashda davom etadi
Qaytariladigan turni saqlangOta-klass kodi aniq maʼlumot turini olishni kutadi
Kerak boʻlsa super() ni chaqiringAgar bazaviy mantiq (audit-log) bajarilishi shart boʻlsa
Argumentlar signaturasini saqlangAks holda polimorf chaqiruv kutilmagan maʼlumot oladi
PaymentGateway ← umumiy kontrakt ├── UzCardGateway ← oʻz amalga oshirilishi ├── HumoGateway ← oʻz amalga oshirilishi └── MillyBankGateway ← oʻz amalga oshirilishi

Dinamik polimorfizm

Duck Typing (utiy tiplashtirish)

"Agar obyekt oʻrdakdek tutsa - u oʻrdakdir". Umumiy ajdodlar shart emas: kerakli nomli metod boʻlsa - uni chaqirish mumkin.

class ApplePayService: def charge_money(self, total: float): print(f"[Apple Pay] Karta tokenidan yechish: {total} soʻm") class GooglePayService: # ApplePayService bilan ierarxiya orqali bogʻlanmagan, ammo metod nomi bir xil def charge_money(self, total: float): print(f"[Google Pay] Google Wallet API ga soʻrov: {total} soʻm") def process_mobile_wallet(payment_service, amount: float): payment_service.charge_money(amount) # faqat metod mavjudligi muhim process_mobile_wallet(ApplePayService(), 120000.0) process_mobile_wallet(GooglePayService(), 120000.0)

Diqqat

Prodakshenda duck typing xavflari

class TaxService: def charge_money(self, total: float): # nomi toʻlov shlyuzlariniki bilan bir xil, ammo mantiq boshqa print(f"[Soliq xizmati] Jarimalarni yechish: {total} soʻm") class FakePayGateway: def charge_monney(self, total: float): # nomdagi xato print(total) # 1-xavf: xato sababli AttributeError try: process_mobile_wallet(FakePayGateway(), 100.0) except AttributeError as err: print(f"Bajarilish xatosi: {err}") # 2-xavf: semantik xato - kod bajariladi, mantiq buziladi process_mobile_wallet(TaxService(), 500000.0)

OOP haqiqiy loyihalarda

Duck Typing analitik kutubxonalarda

from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier # turli klass obyektlari yagona interfeys beradi: .fit() va .predict() scoring_models = [LogisticRegression(), RandomForestClassifier()] for model in scoring_models: model.fit(X_train, y_train) predictions = model.predict(X_test)

Scikit-learn toʻliq duck typing ustiga qurilgan: .fit() va .predict() metodlariga ega istalgan model umumiy payplaynda ishlaydi.

Yakunlar

Polimorfizm - toʻliq rezyume

XususiyatQayta aniqlashDuck typing
Umumiy ajdod talab qiladiHaYoʻq
Kontrakt nazoratiIerarxiya orqaliFaqat metod nomi boʻyicha
Xatolar qachon koʻrinadiLoyihalash bosqichidaFaqat runtimeda
Qayerda qoʻllaniladiBitta tizim shlyuzlariMustaqil kutubxonalar
gateway.process_payment(amount) ↓ Python obyektning MRO siga qaraydi ↓ → kerakli amalga oshirilishni avtomatik chaqiradi

Kurs xaritasi

OOP tushunchalarini oʻzlashtirish holati

TushunchaHolatTavsif
Vorislik🟢 OʻRGANILDIKlasslarni kengaytirish sintaksisi va super().
Polimorfizm🟢 OʻRGANILDIQayta aniqlash, MRO va duck typing.
Inkapsulyatsiya🟡 JORIY MAVZUKritik maydonlarni tashqi kirishdan yashirish.
Abstraksiya⚪ KUTILMOQDABiznes-mantiqni amalga oshirish tafsilotlaridan ajratish.

Inkapsulyatsiya

Muammo - holatni erkin oʻzgartirish

class BankUser: def __init__(self, login: str, raw_password: str): self.login = login self.password = raw_password # parol ochiq koʻrinishda saqlanadi user = BankUser("manager_olga", "123456") # istalgan dasturchi parolni toʻgʻridan-toʻgʻri oʻzgartira oladi: user.password = "HACKED_OR_CLEARED" # yoki uni hech qanday xeshlashsiz oʻqiy oladi: print(f"Loglardagi parol: {user.password}")

Ochiq koʻrinishdagi, har kimga oʻqish va yozish uchun ochiq parol - sizib chiqishga toʻgʻridan-toʻgʻri yoʻl.

Inkapsulyatsiya

Inkapsulyatsiya vazifalari

Inkapsulyatsiya ikki vazifani hal qiladi:

  • Maʼlumotlarni va ular bilan ishlash metodlarini bitta tuzilmaga birlashtirish
  • Ichki tafsilotlarni yashirib, tashqariga faqat xavfsiz, oshkora validatsiyaga ega ochiq interfeysni taqdim etish

🔐 Balans yoki parolni toʻgʻridan-toʻgʻri oʻzgartirib boʻlmaydi. Buning uchun change_password() (eski parolni tekshirish va xeshlash bilan) va deposit() (summani validatsiya qilish bilan) metodlari taqdim etiladi.

Boshqa tillarda

Kirishni boshqarish falsafasi

Java

Qatʼiy taqiq

private modifikatori kirishni kompilyatsiya darajasida bloklaydi. Tashqaridan user.password ga murojaat - kompilyator xatosi.

Python

Kelishuv

Jismoniy cheklovlar yoʻq. Himoya atributlarni nomlash (_ va __) ustiga quriladi. Dasturchilar ularga kelishuv asosida amal qiladi.

Inkapsulyatsiya

Himoyalangan atributlar: _

Bitta tag chizigʻi - signal: "maydon ichki, klassdan tashqarida foydalanish uchun moʻljallanmagan".

  • Texnik jihatdan kirish ochiq
  • IDE bunday murojaatni uslub buzilishi sifatida belgilaydi
  • from module import * da eksport qilinmaydi
class InternalBankConfig: def __init__(self): self._swift_code = "UZUZBGBU" # kelishuv boʻyicha himoyalangan atribut config = InternalBankConfig() print(config._swift_code) # ishlaydi, ammo arxitektura kontraktini buzadi

Inkapsulyatsiya

Privat atributlar: __ va Name Mangling

Ikkita tag chizigʻi Name Mangling ni yoqadi: __password_hash _KlassNomi__password_hash ga aylanadi. Bu maydonni vorislikda tasodifan qayta aniqlanishidan himoya qiladi.

import hashlib class SecureUser: def __init__(self, login: str, raw_password: str): self.login = login self.__password_hash = hashlib.sha256(raw_password.encode()).hexdigest() user = SecureUser("admin_damir", "qwerty2026") try: print(user.__password_hash) # bunday nomli atribut mavjud emas except AttributeError as error: print(f"Xavfsizlik tizimi: {error}") print(f"Aylanma yoʻl: {user._SecureUser__password_hash}")
Xavfsizlik tizimi: 'SecureUser' object has no attribute '__password_hash' Aylanma yoʻl: 4bc82b9a76d...

Inkapsulyatsiya

@property dekoratori - getterlar

Agar maʼlumotni oʻqish, ammo jarayonni nazorat qilish yoki chiqishni niqoblash kerak boʻlsa - @property ishlatiladi. U metodni "virtual atribut" ga aylantiradi - murojaat qavslarsiz.

class CreditCard: def __init__(self, client_name: str, raw_pan: str): self.client_name = client_name self._pan = raw_pan @property def masked_pan(self) -> str: print("[Security Audit] Karta raqamini oʻqishga soʻrov.") return f"{self._pan[:4]} **** **** {self._pan[-4:]}" card = CreditCard("Arman", "8600112233445566") print(f"Karta raqami: {card.masked_pan}") # qavslarsiz - atribut kabi
[Security Audit] Karta raqamini oʻqishga soʻrov. Karta raqami: 8600 **** **** 5566

@property nima uchun

1-sabab - oʻqishdagi mantiq

class UserAccount: def __init__(self, email: str): self._email = email self._read_count = 0 @property def email(self) -> str: self._read_count += 1 return self._email acc = UserAccount("user@millybank.uz") print(acc.email) print(acc.email) print(f"Email {acc._read_count} marta soʻraldi")
user@millybank.uz user@millybank.uz Email 2 marta soʻraldi

@property nima uchun

2-sabab - hisoblanadigan qiymatlar

class LoanPortfolio: def __init__(self, principal: float, interest_rate: float): self.principal = principal self.interest_rate = interest_rate @property def total_debt(self) -> float: return self.principal * (1 + self.interest_rate) portfolio = LoanPortfolio(10_000_000, 0.20) print(f"Jami qaytariladigan summa: {portfolio.total_debt} soʻm") portfolio.principal -= 2_000_000 print(f"Qisman toʻlovdan keyin: {portfolio.total_debt} soʻm")
Jami qaytariladigan summa: 12000000.0 soʻm Qisman toʻlovdan keyin: 9600000.0 soʻm

@property nima uchun

3-sabab - interfeys mosligi

Agar atribut ochiq boʻlgan, soʻngra oʻqishda mantiq talab qilingan boʻlsa - @property uni chaqiruv sintaksisini oʻzgartirmasdan qoʻshishga imkon beradi. Butun tashqi kod ishlashda davom etadi.

# Edi: acc.balance → shunchaki qiymatni oʻqiydi # Boʻldi: acc.balance → qiymatni oʻqiydi VA audit-logga yozadi # Chaqiruv sintaksisi butun loyihada oʻzgarmadi

Shu sababli Pythonda oddiy atributdan boshlaydi va uni mantiq paydo boʻlgandagina @property ga aylantiradi.

Boshqa tillarda

Getter va setterlarga yondashuvlar

Java

Birinchi kundan boilerplate

Har bir maydon birinchi kundan getBalance() / setBalance() ga oʻraladi, ichida mantiq boʻlmasa ham. Katta hajmdagi shablon kod.

Python

Zarurat boʻyicha

Oddiy atributdan boshlaymiz. Zarurat boʻlganda @property ga aylantiramiz - va butun tashqi kod oʻzgarishlarsiz ishlashda davom etadi.

Inkapsulyatsiya

@property.setter - yozishdagi validatsiya

Setter = orqali qiymat berishni ushlab qoladi va maʼlumotni saqlashdan oldin tekshiradi.

class CreditLimitManager: def __init__(self, current_limit: float): self._limit = current_limit @property def limit(self) -> float: return self._limit @limit.setter def limit(self, new_value: float): print(f"Kredit limitini quyidagiga oʻzgartirishga urinish: {new_value} soʻm") if new_value < 0: raise ValueError("Kredit limiti manfiy boʻla olmaydi!") if new_value > 500_000_000: raise ValueError("Filialning ruxsat etilgan maksimal limiti oshib ketdi!") self._limit = new_value manager = CreditLimitManager(50_000_000) try: manager.limit = 700_000_000 except ValueError as error: print(f"Operatsiya rad etildi: {error}")
Kredit limitini quyidagiga oʻzgartirishga urinish: 700000000 soʻm Operatsiya rad etildi: Filialning ruxsat etilgan maksimal limiti oshib ketdi!

Ichki mexanizm

@property + setter bogʻlanishi qanday ishlaydi

@property → Python oʻqish mantigʻiga ega 'limit' deskriptorini yaratadi @limit.setter → xuddi shu deskriptorga yozish mantigʻi qoʻshiladi manager.limit → limit.fget(manager) ni chaqiradi manager.limit = 700000 → limit.fset(manager, 700000) ni chaqiradi

Getter va setter nomlari mos kelishi shart - shuning uchun dekorator @limit.setter deb yoziladi, shunchaki @setter emas.

class PinValidator: def __init__(self): self._pin = None @property def pin(self) -> str: return "****" # haqiqiy qiymat qaytarilmaydi @pin.setter def pin(self, value: str): if not value.isdigit() or len(value) != 4: raise ValueError("PIN-kod aniq 4 ta raqamdan iborat boʻlishi kerak!") self._pin = value print("PIN-kod muvaffaqiyatli yangilandi.")

Inkapsulyatsiya

Settersiz xususiyat - faqat oʻqish uchun

class Contract: def __init__(self, contract_id: str, client: str): self._contract_id = contract_id self.client = client @property def contract_id(self) -> str: """Shartnoma identifikatori - yaratilgandan keyin oʻzgarmas""" return self._contract_id contract = Contract("UZ-2024-001", "Timur") print(contract.contract_id) try: contract.contract_id = "UZ-2024-999" except AttributeError as e: print(f"Himoya ishladi: {e}")
UZ-2024-001 Himoya ishladi: can't set attribute

Inkapsulyatsiya

Etalon klass BankAccount

Konstruktor, himoyalangan balans va tekshiruv bilan hisobni toʻldirish:

class BankAccount: def __init__(self, owner: str, initial_balance: float = 0.0): self.owner = owner self._balance = initial_balance # himoyalangan balans @property def balance(self) -> float: return self._balance def deposit(self, amount: float) -> None: if amount <= 0: print("Xato: toʻldirish summasi noldan katta boʻlishi kerak.") return self._balance += amount

Balans toʻgʻridan-toʻgʻri yozishdan yopiq: @property orqali oʻqiladi, faqat metodlar orqali oʻzgartiriladi.

Inkapsulyatsiya

BankAccount: mablagʻni yechish

Xuddi shu klass - ikki tomonlama tekshiruvga ega yechish metodi:

def withdraw(self, amount: float) -> None: if amount <= 0: print("Xato: yechish summasi musbat boʻlishi kerak.") return if amount > self._balance: print("Tranzaksiya bloklandi: mablagʻ yetarli emas.") return self._balance -= amount acc = BankAccount("Dmitriy", 1_000_000.0) acc.withdraw(1_500_000.0)
Tranzaksiya bloklandi: mablagʻ yetarli emas.

Metod summa va qoldiqni oʻzi tekshiradi - notoʻgʻri operatsiya shunchaki oʻtmaydi.

Inkapsulyatsiya

Dinamik hisoblanadigan xususiyatlar

class LoanPortfolio: def __init__(self, principal: float, interest_rate: float): self.principal = principal self.interest_rate = interest_rate @property def total_debt(self) -> float: """Har bir murojaatda dolzarb maʼlumotlar boʻyicha hisoblanadi""" return self.principal * (1 + self.interest_rate) portfolio = LoanPortfolio(10_000_000, 0.20) print(f"Jami qaytariladigan summa: {portfolio.total_debt} soʻm") portfolio.principal -= 2_000_000 print(f"Qisman toʻlovdan keyin: {portfolio.total_debt} soʻm")
Jami qaytariladigan summa: 12000000.0 soʻm Qisman toʻlovdan keyin: 9600000.0 soʻm

Inkapsulyatsiya

Inkapsulyatsiya vositalarining umumiy jadvali

VositaNima qiladiQachon ishlatish kerak
_nameIchki tegishlilik haqida signalTashqi foydalanish uchun moʻljallanmagan maydonlar
__nameName Mangling - maydonni xotirada qayta nomlaydiKritik maʼlumotlar: parollar, tokenlar, PIN-kodlar
@propertyNazorat ostida oʻqishOʻqishdagi mantiq, niqoblash, hisoblanadigan qiymatlar
@name.setterYozishdan oldin validatsiyaSaqlashdan oldin maʼlumotni tekshirish
@property settersizFaqat oʻqish uchunOʻzgarmas identifikatorlar: shartnoma ID si, hisob raqami

Yakunlar

OOP ning uch tushunchasi - umumiy matritsa

TushunchaHolatQaysi muammoni hal qiladiAsosiy vositalar
Vorislik🟢Oʻxshash klasslarda kod takrorlanishiclass Child(Parent), super()
Polimorfizm🟢Kodning aniq amalga oshirilishlarga bogʻliqligiQayta aniqlash, Duck Typing, MRO
Inkapsulyatsiya🟢Maʼlumotga toʻgʻridan-toʻgʻri kirish mantiqni buzadi_, __, @property, setter
AbstraksiyaYuqori darajadagi kod tafsilotlar bilan aralashganABC, @abstractmethod
Vorislik → ierarxiya yaratamiz: UzCardGateway(PaymentGateway) Polimorfizm → yagona interfeys: gateway.process_payment() Inkapsulyatsiya → maʼlumotni himoya: self.__pin_hash, @property

Klass darajasi

Nusxa atributi vs klass atributi

Nusxa atributi self.maydon orqali har bir obyekt uchun alohida yaratiladi. Klass atributi klass tanasida eʼlon qilinadi - barcha nusxalar uchun bitta nusxa.

class NationalBankCreditProduct: base_interest_rate = 0.20 # klass atributi - barcha uchun umumiy def __init__(self, client_name: str, loan_amount: float): self.client_name = client_name # nusxa atributi self.loan_amount = loan_amount contract1 = NationalBankCreditProduct("Timur", 50_000_000) contract2 = NationalBankCreditProduct("Elena", 90_000_000) print(f"Timur uchun stavka: {contract1.base_interest_rate * 100}%") print(f"Elena uchun stavka: {contract2.base_interest_rate * 100}%")
Timur uchun stavka: 20.0% Elena uchun stavka: 20.0%

Klass darajasi

Klass atributini oʻzgartirish hammasiga taʼsir qiladi

print("--- Stavkani oʻzgartirishdan oldin ---") print(f"Timur: {contract1.base_interest_rate * 100}%") print(f"Elena: {contract2.base_interest_rate * 100}%") NationalBankCreditProduct.base_interest_rate = 0.22 print("--- Stavkani oʻzgartirgandan keyin ---") print(f"Timur: {contract1.base_interest_rate * 100}%") print(f"Elena: {contract2.base_interest_rate * 100}%")
--- Stavkani oʻzgartirishdan oldin --- Timur: 20.0% Elena: 20.0% --- Stavkani oʻzgartirgandan keyin --- Timur: 22.0% Elena: 22.0%

Nusxa orqali oʻzgartirilganda (contract1.base_interest_rate = 0.25) Python shu nusxada yangi atribut yaratadi - klass atributi oʻzgarmasdan qoladi.

Amaliyot

Amaliyot uchun materiallar

Maʼruzaning birinchi qismi uchun ikkita Jupyter daftari.

↓ Topshiriqlar va misollar ↓ Amaliyot uchun snippetlar

Jupyter Notebook, JupyterLab yoki VS Code da oching.

RU UZ EN
Python · Leksiya 7 · 1-qism
1 / 64