Asosiy qismga o'tish

Maxsus metodlar, @property va dataclass

18-bob 14 kod bloki 2265 so'z

Bu bobda

  • obyektni chiroyli chiqarishni: __str__ va __repr__
  • obyektlarni solishtirish va tartiblashni: __eq__, __lt__
  • o'z klassingizga + va len() ni o'rgatishni
  • atributni @property bilan qo'riqlashni
  • @classmethod, @staticmethod va @dataclass ni

Muammo: print(obyekt) xunuk chiqadi

python9 qator
class Kitob:
    def __init__(self, nom, muallif, yil):
        self.nom = nom
        self.muallif = muallif
        self.yil = yil


k = Kitob("O'tkan kunlar", "Abdulla Qodiriy", 1926)
print(k)
Natija
<__main__.Kitob object at 0x7fb0d0742b60>

Python obyekt haqida faqat klass nomi va xotiradagi manzilni biladi. Uni qanday ko'rsatishni o'zimiz aytishimiz kerak.

__str__ va __repr__

Ikki pastki chiziq bilan o'ralgan metodlar (__init__ kabi) — maxsus metodlar (dunder methods). Python ularni ma'lum holatlarda o'zi chaqiradi.

python18 qator
class Kitob:
    def __init__(self, nom, muallif, yil):
        self.nom = nom
        self.muallif = muallif
        self.yil = yil

    def __str__(self):
        return f"«{self.nom}» — {self.muallif}, {self.yil}"

    def __repr__(self):
        return f"Kitob({self.nom!r}, {self.muallif!r}, {self.yil})"


k = Kitob("O'tkan kunlar", "Abdulla Qodiriy", 1926)
print(k)
print(str(k))
print([k])
print(repr(k))
Natija
«O'tkan kunlar» — Abdulla Qodiriy, 1926
«O'tkan kunlar» — Abdulla Qodiriy, 1926
[Kitob("O'tkan kunlar", 'Abdulla Qodiriy', 1926)]
Kitob("O'tkan kunlar", 'Abdulla Qodiriy', 1926)
  • __str__ — o'quvchi uchun: print() va str() ishlatadi.
  • __repr__ — dasturchi uchun: ro'yxat ichida, Jupyterda katak oxirida ko'rinadi. Yaxshi __repr__ obyektni qayta yasash mumkin bo'lgan kodga o'xshaydi.

{self.nom!r} — f-string ichida qiymatning repr() ini qo'yadi (satr qo'shtirnoq bilan chiqadi).

__eq__ va __lt__: solishtirish

Odatda ikki obyekt faqat o'sha bitta obyekt bo'lsagina teng:

python6 qator
class Pul:
    def __init__(self, som):
        self.som = som


print(Pul(1000) == Pul(1000))
Natija
False

Ma'no bo'yicha solishtirish uchun __eq__ (==) va __lt__ (<) yoziladi:

python19 qator
class Pul:
    def __init__(self, som):
        self.som = som

    def __eq__(self, boshqa):
        return self.som == boshqa.som

    def __lt__(self, boshqa):
        return self.som < boshqa.som

    def __repr__(self):
        return f"Pul({self.som})"


print(Pul(1000) == Pul(1000))
print(Pul(500) < Pul(1000))
narxlar = [Pul(25_000), Pul(4_000), Pul(12_500)]
print(sorted(narxlar))
print(max(narxlar))
Natija
True
True
[Pul(4000), Pul(12500), Pul(25000)]
Pul(25000)

__lt__ yozilgani uchun sorted(), max(), min() ham ishladi — ular ichkarida < ni ishlatadi.

__add__ va __len__: o'z operatorlaringiz

a + b yozilganda Python aslida a.__add__(b) ni chaqiradi. Shuni yozsak, klassimiz + ni tushunadi:

python23 qator
class Vektor:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, boshqa):
        return Vektor(self.x + boshqa.x, self.y + boshqa.y)

    def __mul__(self, son):
        return Vektor(self.x * son, self.y * son)

    def __abs__(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5

    def __repr__(self):
        return f"Vektor({self.x}, {self.y})"


a = Vektor(1, 2)
b = Vektor(3, 4)
print(a + b)
print(a * 3)
print(abs(b))
Natija
Vektor(4, 6)
Vektor(3, 6)
5.0

__len__ — len() uchun:

python19 qator
class Savat:
    def __init__(self):
        self.mahsulotlar = []

    def qosh(self, nom):
        self.mahsulotlar.append(nom)

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

    def __contains__(self, nom):
        return nom in self.mahsulotlar


s = Savat()
s.qosh("non")
s.qosh("sut")
print(len(s))
print("sut" in s, "go'sht" in s)
Natija
2
True False
Maxsus metod Qachon chaqiriladi
__init__ obyekt yasalganda
__str__ print(x), str(x)
__repr__ Jupyter, ro'yxat ichida, repr(x)
__eq__ x == y
__lt__ x < y, sorted(), min(), max()
__add__ x + y
__len__ len(x)
__contains__ y in x

@property: qo'riqlanadigan atribut

16-bobda probegni metod orqali tekshirdik. Lekin baribir mashina.probeg = -5 deb yozish mumkin edi. @property atributni tashqaridan oddiy atributdek ko'rsatadi, lekin ichkarida tekshiruv qiladi:

python21 qator
class Talaba:
    def __init__(self, ism, yosh):
        self.ism = ism
        self.yosh = yosh

    @property
    def yosh(self):
        return self._yosh

    @yosh.setter
    def yosh(self, qiymat):
        if not 0 < qiymat < 120:
            raise ValueError(f"Yosh noto'g'ri: {qiymat}")
        self._yosh = qiymat


t = Talaba("Ali", 20)
print(t.yosh)
t.yosh = 21
print(t.yosh)
t.yosh = -5
Natija
20
21
Xato xabari
ValueError: Yosh noto'g'ri: -5
  • @property ostidagi yosh(self) — o'qish: t.yosh yozilganda chaqiriladi.
  • @yosh.setter ostidagi yosh(self, qiymat) — yozish: t.yosh = ... da chaqiriladi.
  • Haqiqiy qiymat self._yosh da saqlanadi. Boshidagi _ — "bu ichki narsa, tashqaridan tegmang" degan kelishuv.
  • raise ValueError(...) — xato yuzaga keltirish; uni 20-bobda batafsil ko'ramiz.

__init__ dagi self.yosh = yosh ham setter orqali o'tadi, shuning uchun Talaba("Ali", -5) ham darhol rad etiladi.

Setter yozilmasa — atribut faqat o'qiladigan bo'ladi. Hisoblanadigan qiymatlar uchun qulay:

python14 qator
class Tortburchak:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def yuza(self):
        return self.a * self.b


t = Tortburchak(3, 4)
print(t.yuza)
t.a = 10
print(t.yuza)
Natija
12
40

t.yuza — qavssiz, xuddi atributdek; lekin har safar yangidan hisoblanadi.

@classmethod va @staticmethod

  • @classmethod — obyekt emas, klassning o'zini (cls) oladi. Ko'pincha "boshqa usulda obyekt yasash" uchun ishlatiladi.
  • @staticmethod — na obyektni, na klassni oladi; klassga mantiqan tegishli oddiy funksiya.
python22 qator
class Sana:
    def __init__(self, kun, oy, yil):
        self.kun = kun
        self.oy = oy
        self.yil = yil

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

    @staticmethod
    def kabisami(yil):
        return (yil % 4 == 0 and yil % 100 != 0) or yil % 400 == 0

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


s = Sana.matndan("14.10.2026")
print(s)
print(Sana.kabisami(2024), Sana.kabisami(2026))
Natija
14.10.2026
True False

@dataclass: kamroq yozib, ko'proq olish

Ko'p klasslar asosan ma'lumot saqlaydi: __init__, __repr__, __eq__ — hammasi bir xil andoza. dataclass ularni avtomatik yozib beradi:

python19 qator
from dataclasses import dataclass, field


@dataclass
class Mahsulot:
    nom: str
    narx: int
    soni: int = 1
    teglar: list = field(default_factory=list)

    def jami(self):
        return self.narx * self.soni


a = Mahsulot("non", 4000, 3)
b = Mahsulot("non", 4000, 3)
print(a)
print(a == b)
print(a.jami())
Natija
Mahsulot(nom='non', narx=4000, soni=3, teglar=[])
True
12000
  • nom: str — atribut va uning turi (type hint). Python turni tekshirmaydi, bu o'quvchi va muharrir uchun izoh.
  • soni: int = 1 — standart qiymat.
  • field(default_factory=list) — har bir obyektga yangi ro'yxat (14-bobdagi [] tuzog'idan himoya).
  • __init__, __repr__, __eq__ avtomatik yozildi.

@dataclass(order=True) — <, sorted() ham ishlaydi (maydonlar tartibi bo'yicha solishtiradi).

Kitoblarda

  • Python asoslari: OOP bo'limi 95–112-betlar (maxsus metodlar va dataclass bu kitobda yo'q).
  • Python Crash Course: 9-bob, Importing Classes 173–178-betlar, The Python Standard Library 179-bet. Maxsus metodlar va @property kitobda o'rgatilmaydi — bu bob ikkala kitobni ham to'ldiradi.

Xulosa

  • __str__ — print uchun, __repr__ — dasturchi uchun.
  • __eq__, __lt__ — solishtirish va tartiblash; __add__, __len__, __contains__ — +, len(), in.
  • @property + @nom.setter — tekshiruvli atribut; _nom — ichki atribut kelishuvi.
  • @classmethod — klassni oladi (cls), muqobil konstruktor uchun; @staticmethod — oddiy funksiya klass ichida.
  • @dataclass — ma'lumot klasslari uchun __init__, __repr__, __eq__ ni avtomatik yozadi.

Mashqlar

18.1

Kasr(surat, maxraj) klassini yozing: konstruktor kasrni qisqartirib saqlaydi (math.gcd), __add__ ikki kasrni qo'shadi, __str__ uni surat/maxraj ko'rinishida qaytaradi. Ikki qatorda ikki kasr a/b ko'rinishida berilgan; yig'indisini chiqaring.

KirishKutilgan natija
1/2, 1/35/6
1/4, 1/41/2
2/3, 1/31/1
Yechimni ko'rish
python22 qator
import math


class Kasr:
    def __init__(self, surat, maxraj):
        e = math.gcd(surat, maxraj)
        self.surat = surat // e
        self.maxraj = maxraj // e

    def __add__(self, b):
        return Kasr(self.surat * b.maxraj + b.surat * self.maxraj, self.maxraj * b.maxraj)

    def __str__(self):
        return f"{self.surat}/{self.maxraj}"


def oqi():
    s, m = input().split("/")
    return Kasr(int(s), int(m))


print(oqi() + oqi())
18.2

Vektor(x, y) klassini __add__, __sub__, __abs__ (uzunlik) va __str__ ((x, y) ko'rinishida) bilan yozing. Ikki vektor ikki qatorda berilgan; ularning yig'indisini, ayirmasini va yig'indi uzunligini alohida qatorlarda chiqaring.

KirishKutilgan natija
1 2, 3 4(4, 6) (-2, -2) 7.211102550927978
3 0, 0 4(3, 4) (3, -4) 5.0
Yechimni ko'rish
python22 qator
class Vektor:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __add__(self, b):
        return Vektor(self.x + b.x, self.y + b.y)

    def __sub__(self, b):
        return Vektor(self.x - b.x, self.y - b.y)

    def __abs__(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5

    def __str__(self):
        return f"({self.x}, {self.y})"


a = Vektor(*[int(s) for s in input().split()])
b = Vektor(*[int(s) for s in input().split()])
print(a + b)
print(a - b)
print(abs(a + b))
18.3

Harorat klassini yozing: selsiy — @property, setter −273.15 dan past qiymatni rad etadi (ValueError); farengeyt — faqat o'qiladigan @property: C · 9/5 + 32. Selsiy berilgan; Farengeytni chiqaring. Qiymat imkonsiz bo'lsa — Xato. Xatoni ushlash uchun shu andozani ishlating (20-bobda batafsil): try: ichida obyekt yasab natijani chiqaring, except ValueError: ichida print("Xato").

KirishKutilgan natija
100212
-40-40
-300Xato
Yechimni ko'rish
python23 qator
class Harorat:
    def __init__(self, selsiy):
        self.selsiy = selsiy

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

    @selsiy.setter
    def selsiy(self, q):
        if q < -273.15:
            raise ValueError("absolyut noldan past")
        self._selsiy = q

    @property
    def farengeyt(self):
        return self._selsiy * 9 / 5 + 32


try:
    print(Harorat(float(input())).farengeyt)
except ValueError:
    print("Xato")
18.4

@dataclass bilan Talaba(ism: str, ball: int) yozing va uni ball bo'yicha tartiblanadigan qiling (order=True uchun maydonlar tartibiga e'tibor bering yoki sorted(key=...) ishlating). Birinchi qatorda N, keyin N qatorda ism ball. Eng yuqori ball olgan talabaning ismini chiqaring.

KirishKutilgan natija
3, Ali 78, Sarvinoz 91, Otabek 85Sarvinoz
1, Vali 50Vali
Yechimni ko'rish
python14 qator
from dataclasses import dataclass


@dataclass
class Talaba:
    ism: str
    ball: int


talabalar = []
for _ in range(int(input())):
    ism, ball = input().split()
    talabalar.append(Talaba(ism, int(ball)))
print(max(talabalar, key=lambda t: t.ball).ism)

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar