IlmHamroh
Python kursi/OOP13/20-dars43 daqiqa
Mundarija (21)

8.13-dars: Dunder: arifmetika

8-QISM — OBYEKTGA YO'NALTIRILGAN DASTURLASH · 13-dars


1. Kirish va motivatsiya

+ operatori — bitta belgi, lekin uch metod bilan bog'liq:

python
class Vektor:
    def __init__(self, x, y): self.x, self.y = x, y
    def __add__(self, b):  ...      # v + b
    def __radd__(self, b): ...      # b + v   ⭐ teskari
    def __iadd__(self, b): ...      # v += b  ⭐ joyida

Va bu uchtasi turli holatlarda chaqiriladi:

python
v = Vektor(1, 2)

v + Vektor(3, 4)                # v.__add__      ✅
3 * v                           # v.__rmul__     ⭐ int bilmaydi
v += Vektor(1, 1)               # v.__iadd__     ⭐ yoki __add__
sum([v1, v2, v3])               # 0 + v1 → v1.__radd__  ⚠️

Bu esa chalkash:

python
lst = [1, 2]
lst2 = lst
lst += [3]                      # lst2 ham o'zgardi  ⚠️
lst = lst + [4]                 # lst2 o'zgarmadi    ✅

t = (1, 2)
t += (3,)                       # yangi tuple        ✅ (tuple o'zgarmas)

Bu darsda:

  • Uch oila: oddiy, teskari (__r*__), joyida (__i*__)
  • NotImplemented va operator hal qilish tartibi
  • += — __iadd__ vs __add__ farqi
  • Unar amallar: __neg__, __abs__, __invert__
  • Turga o'girish: __int__, __float__, __index__
  • numbers ABC ierarxiyasi

2. Nazariya — chuqur tushuntirish

2.1. Uch oila

Amal Oddiy Teskari Joyida
+ __add__ __radd__ __iadd__
- __sub__ __rsub__ __isub__
* __mul__ __rmul__ __imul__
/ __truediv__ __rtruediv__ __itruediv__
// __floordiv__ __rfloordiv__ __ifloordiv__
% __mod__ __rmod__ __imod__
** __pow__ __rpow__ __ipow__
@ __matmul__ __rmatmul__ __imatmul__
<< __lshift__ __rlshift__ __ilshift__
>> __rshift__ __rrshift__ __irshift__
& __and__ __rand__ __iand__
| __or__ __ror__ __ior__
^ __xor__ __rxor__ __ixor__

divmod() — __divmod__ / __rdivmod__.

2.2. Operator hal qilish tartibi

a + b uchun:

1. type(b) — type(a) ning AVLODI va __radd__ ni qayta aniqlagan?
   → b.__radd__(a)  birinchi

2. a.__add__(b)
   → NotImplemented emas → natija

3. b.__radd__(a)
   → NotImplemented emas → natija

4. TypeError: unsupported operand type(s) for +: 'A' and 'B'
python
class A:
    def __add__(self, b):
        print("A.__add__")
        return NotImplemented

class B:
    def __radd__(self, a):
        print("B.__radd__")
        return "natija"

A() + B()
# A.__add__
# B.__radd__
# 'natija'

Nega __r*__ kerak:

python
3 * v                           # int.__mul__(3, v) → NotImplemented
                                # v.__rmul__(3)     → ✅

int sizning sinfingizni bilmaydi — shuning uchun NotImplemented qaytaradi.

2.3. += — __iadd__ vs __add__

python
a += b

Mexanizm:

1. a.__iadd__(b)     bor bo'lsa
   → natija a ga qayta bog'lanadi

2. a.__add__(b)      __iadd__ yo'q bo'lsa
   → natija a ga bog'lanadi

3. b.__radd__(a)
4. TypeError

__iadd__ — obyektni joyida o'zgartirib, self qaytarish uchun.

python
class Royxat:
    def __init__(self, el=None): self.el = list(el or [])

    def __add__(self, b):
        return Royxat(self.el + b.el)       # ⭐ YANGI obyekt

    def __iadd__(self, b):
        self.el.extend(b.el)                # ⭐ joyida
        return self                         # ⚠️ QAYTARISH SHART

Bu — list va tuple farqining sababi:

python
lst = [1, 2]
lst2 = lst
lst += [3]                      # list.__iadd__ — joyida
lst2                            # [1, 2, 3]  ⚠️ o'zgardi

t = (1, 2)
t2 = t
t += (3,)                       # tuple.__iadd__ yo'q → __add__
t2                              # (1, 2)     ✅ o'zgarmadi

Klassik tuzoq (8.5-darsda ko'rgan edik):

python
class A:
    lst = []                    # sinf atributi

a = A()
a.lst += ["x"]                  # ⚠️ __iadd__ sinf ro'yxatini o'zgartiradi

__iadd__ da return unutish:

python
def __iadd__(self, b):
    self.el.extend(b.el)        # ⚠️ return yo'q → None
a += b
a                               # None ⚠️

2.4. Unar amallar

Amal Metod Misol
-a __neg__ -Vektor(1,2)
+a __pos__ +Decimal("1.10")
abs(a) __abs__ abs(Vektor(3,4))
~a __invert__ ~Bayroq(5)
round(a) __round__ round(Pul(1.5))
math.floor(a) __floor__
math.ceil(a) __ceil__
math.trunc(a) __trunc__
python
class Vektor:
    def __neg__(self): return Vektor(-self.x, -self.y)
    def __pos__(self): return self                  # odatda o'zi
    def __abs__(self): return (self.x**2 + self.y**2) ** 0.5

__pos__ odatda self qaytaradi, lekin Decimal da u normallashtiradi:

python
from decimal import Decimal
+Decimal("1.10")                # Decimal('1.10')
Decimal("1.10").normalize()     # Decimal('1.1')

2.5. Turga o'girish

Funksiya Metod Talab
int(a) __int__ int qaytarsin
float(a) __float__ float
complex(a) __complex__ complex
bool(a) __bool__ bool (yoki __len__)
bytes(a) __bytes__ bytes
indeks __index__ int, yo'qotishsiz

__index__ — __int__ dan farqi:

python
class A:
    def __int__(self): return 5

lst = [1, 2, 3, 4, 5, 6]
lst[A()]                        # ❌ TypeError — __index__ kerak
int(A())                        # 5   ✅

__index__ — yo'qotishsiz butun songa o'girish demakdir. float da __int__ bor, __index__ yo'q:

python
lst[2.0]                        # ❌ TypeError
hex(2.0)                        # ❌ TypeError
bin(True)                       # '0b1'  ✅ bool.__index__ bor

__index__ ishlatiladigan joylar: indekslash, kesish, hex(), oct(), bin(), range(), operator.index(), bitwise amallar.

Python 3.11+ da: __int__ faqat int() uchun; boshqa joylarda __index__ talab qilinadi.

__bool__ va __len__:

python
bool(x):
  1. x.__bool__()      bor bo'lsa
  2. len(x) != 0       __len__ bor bo'lsa
  3. True              ikkalasi ham yo'q

2.6. numbers ABC ierarxiyasi

python
from numbers import Number, Complex, Real, Rational, Integral

Number
 └── Complex        __complex__, __abs__, real, imag, conjugate
      └── Real      __float__, __trunc__, __floor__, __ceil__, __round__
           └── Rational    numerator, denominator
                └── Integral    __int__, __index__, bitwise amallar
python
isinstance(5, Integral)         # True
isinstance(5.0, Real)           # True
isinstance(Fraction(1,2), Rational)     # True
isinstance(complex(1,2), Complex)       # True
isinstance(Decimal("1"), Real)          # ⚠️ False!

Decimal ataylab ro'yxatga olinmagan — u float bilan aralashishi mumkin emas.

2.7. Amaliy maslahatlar

Qaysi metodlarni yozish:

python
# Minimum — qiymat obyekti uchun
__add__, __radd__               # yig'indi ma'noli bo'lsa
__eq__, __hash__                # 8.12-dars
__repr__                        # 8.11-dars

# Skalyar ko'paytirish
__mul__, __rmul__

# To'liq son kabi
__add__, __sub__, __mul__, __truediv__
+ hammasining __r*__ versiyasi
+ __neg__, __abs__

__r*__ ni unutmang:

python
class Pul:
    def __mul__(self, n): return Pul(self.miqdor * n)

Pul(100) * 3                    # ✅
3 * Pul(100)                    # ❌ TypeError

__rmul__ = __mul__ (kommutativ bo'lsa).

Kommutativ bo'lmasa ehtiyot:

python
def __sub__(self, b): return Pul(self.m - b.m)
def __rsub__(self, b): return Pul(b - self.m)       # ⭐ tartib teskari!

sum() bilan ishlash:

python
sum([v1, v2, v3])               # 0 + v1 + v2 + v3
                                # ⚠️ 0.__add__(v1) → NotImplemented
                                #    v1.__radd__(0) kerak

def __radd__(self, b):
    if b == 0: return self      # ⭐ sum() uchun
    return self.__add__(b)

# yoki:
sum([v1, v2, v3], Vektor(0, 0)) # ✅ boshlang'ich qiymat

3. Tez ma'lumotnoma

Uch oila

python
__add__(self, b)     a + b
__radd__(self, b)    b + a      ⭐ b bilmasa
__iadd__(self, b)    a += b     ⭐ joyida, self qaytaring

Barcha amallar uchun: sub mul truediv floordiv mod pow
                      matmul lshift rshift and or xor

Hal qilish tartibi (a + b)

1. b avlod va __radd__ qayta aniqlangan → b.__radd__(a)
2. a.__add__(b)          → NotImplemented bo'lmasa
3. b.__radd__(a)         → NotImplemented bo'lmasa
4. TypeError

+=

1. a.__iadd__(b)   → a ga qayta bog'lanadi
2. a.__add__(b)    → __iadd__ yo'q bo'lsa
3. b.__radd__(a)

__iadd__ da return SHART!
list.__iadd__ → joyida (havolalar ta'sirlanadi)
tuple.__iadd__ yo'q → yangi obyekt

Unar

python
-a       __neg__          abs(a)   __abs__
+a       __pos__          ~a       __invert__
round(a) __round__        math.floor/ceil/trunc

Turga o'girish

python
int(a)      __int__
float(a)    __float__
bool(a)     __bool__ → __len__ → True
bytes(a)    __bytes__
lst[a]      ⭐ __index__ (yo'qotishsiz!)

4. Batafsil misollar

Misol 1 — Uch oila

python
"""Oddiy, teskari va joyida amallar."""

print("=== ⭐ 1. Hal qilish tartibi ===\n")


class Chap:
    def __add__(self, b):
        print(f"      Chap.__add__({type(b).__name__})")
        return NotImplemented


class Ong:
    def __radd__(self, a):
        print(f"      Ong.__radd__({type(a).__name__})")
        return "Ong hal qildi"


class Hech:
    pass


print(f"  Chap() + Ong():")
n = Chap() + Ong()
print(f"    → {n!r}\n")

print(f"  Chap() + Hech():")
try:
    Chap() + Hech()
except TypeError as e:
    print(f"    ❌ TypeError: {e}\n")

print(f"  1 + Ong():")
n = 1 + Ong()
print(f"    → {n!r}")
print(f"    ⭐ int.__add__(1, Ong) → NotImplemented → Ong.__radd__")


print("\n=== ⚠️ 2. Avlod ustuvorligi ===\n")


class Ota:
    def __add__(self, b):
        print(f"      Ota.__add__")
        return "Ota"


class Bola(Ota):
    def __radd__(self, a):
        print(f"      Bola.__radd__ (AVLOD — birinchi!)")
        return "Bola"


print(f"  Ota() + Bola():")
n = Ota() + Bola()
print(f"    → {n!r}")

print(f"""
  ⭐ QOIDA: o'ng operand chap operandning AVLODI bo'lsa
     va __radd__ ni QAYTA ANIQLAGAN bo'lsa —
     u BIRINCHI chaqiriladi.

     Sabab: avlod ota-sinfdan ko'proq biladi.
""")


print("=== ⚠️ 3. += mexanizmi ===\n")


class BilanIadd:
    def __init__(self, el): self.el = list(el)

    def __add__(self, b):
        print(f"      __add__ (yangi obyekt)")
        return BilanIadd(self.el + b.el)

    def __iadd__(self, b):
        print(f"      __iadd__ (joyida)")
        self.el.extend(b.el)
        return self                         # ⭐ SHART

    def __repr__(self): return f"B({self.el})"


class Iaddsiz:
    def __init__(self, el): self.el = list(el)

    def __add__(self, b):
        print(f"      __add__ (yangi obyekt)")
        return Iaddsiz(self.el + b.el)

    def __repr__(self): return f"I({self.el})"


print(f"  __iadd__ BOR:")
a = BilanIadd([1, 2])
a2 = a
a += BilanIadd([3])
print(f"    a  = {a}")
print(f"    a2 = {a2}   ⚠️ a2 ham o'zgardi (bir obyekt)")
print(f"    a is a2 = {a is a2}\n")

print(f"  __iadd__ YO'Q:")
b = Iaddsiz([1, 2])
b2 = b
b += Iaddsiz([3])
print(f"    b  = {b}")
print(f"    b2 = {b2}   ✅ o'zgarmadi")
print(f"    b is b2 = {b is b2}")

print(f"""
  ⭐ MEXANIZM (a += b):
     1. a.__iadd__(b)   bor bo'lsa → natija a ga bog'lanadi
     2. a.__add__(b)    → natija a ga bog'lanadi
     3. b.__radd__(a)
     4. TypeError
""")


print("=== ⚠️ 4. list vs tuple ===\n")

lst = [1, 2]
lst2 = lst
lst += [3]
print(f"  lst = [1,2]; lst2 = lst; lst += [3]")
print(f"    lst  = {lst}")
print(f"    lst2 = {lst2}   ⚠️ o'zgardi (list.__iadd__ joyida)")

lst3 = [1, 2]
lst4 = lst3
lst3 = lst3 + [3]
print(f"\n  lst3 = lst3 + [3] (= bilan):")
print(f"    lst3 = {lst3}")
print(f"    lst4 = {lst4}   ✅ o'zgarmadi")

t = (1, 2)
t2 = t
t += (3,)
print(f"\n  t = (1,2); t2 = t; t += (3,)")
print(f"    t  = {t}")
print(f"    t2 = {t2}   ✅ o'zgarmadi (tuple.__iadd__ YO'Q)")

print(f"\n  Metodlar mavjudligi:")
for tur in (list, tuple, set, dict, str, int):
    iadd = "✅" if hasattr(tur, "__iadd__") else "❌"
    ior = "✅" if hasattr(tur, "__ior__") else "❌"
    print(f"    {tur.__name__:<8} __iadd__: {iadd}   __ior__: {ior}")


print("\n=== ⚠️ 5. __iadd__ da return unutish ===\n")


class Unutgan:
    def __init__(self, el): self.el = list(el)
    def __iadd__(self, b):
        self.el.extend(b.el)                # ⚠️ return yo'q
    def __repr__(self): return f"U({self.el})"


u = Unutgan([1])
print(f"  u = {u}")
u += Unutgan([2])
print(f"  u += Unutgan([2]) dan keyin:")
print(f"    u = {u}   ⚠️ None!")

print(f"""
  ⚠️ __iadd__ None qaytarsa — a ga None bog'lanadi.
     Bu — jimgina xato, topish qiyin.

  ✅ HAR DOIM return self
""")


print("=== 6. To'liq amallar to'plami ===\n")


class Son:
    """Barcha arifmetik amallarni qo'llab-quvvatlaydi."""

    def __init__(self, q): self.q = float(q)

    def _b(self, b):
        """Boshqa operandni songa o'giradi yoki None."""
        if isinstance(b, Son): return b.q
        if isinstance(b, (int, float)): return float(b)
        return None

    # ── Oddiy ──
    def __add__(self, b):
        x = self._b(b)
        return Son(self.q + x) if x is not None else NotImplemented

    def __sub__(self, b):
        x = self._b(b)
        return Son(self.q - x) if x is not None else NotImplemented

    def __mul__(self, b):
        x = self._b(b)
        return Son(self.q * x) if x is not None else NotImplemented

    def __truediv__(self, b):
        x = self._b(b)
        if x is None: return NotImplemented
        if x == 0: raise ZeroDivisionError("Son nolga bo'lindi")
        return Son(self.q / x)

    def __floordiv__(self, b):
        x = self._b(b)
        return Son(self.q // x) if x is not None else NotImplemented

    def __mod__(self, b):
        x = self._b(b)
        return Son(self.q % x) if x is not None else NotImplemented

    def __pow__(self, b):
        x = self._b(b)
        return Son(self.q ** x) if x is not None else NotImplemented

    def __divmod__(self, b):
        x = self._b(b)
        if x is None: return NotImplemented
        d, m = divmod(self.q, x)
        return (Son(d), Son(m))

    # ── Teskari ──
    def __radd__(self, b): return self.__add__(b)       # kommutativ
    def __rmul__(self, b): return self.__mul__(b)       # kommutativ

    def __rsub__(self, b):
        x = self._b(b)
        return Son(x - self.q) if x is not None else NotImplemented

    def __rtruediv__(self, b):
        x = self._b(b)
        if x is None: return NotImplemented
        if self.q == 0: raise ZeroDivisionError("Son nolga bo'lindi")
        return Son(x / self.q)

    def __rfloordiv__(self, b):
        x = self._b(b)
        return Son(x // self.q) if x is not None else NotImplemented

    def __rmod__(self, b):
        x = self._b(b)
        return Son(x % self.q) if x is not None else NotImplemented

    def __rpow__(self, b):
        x = self._b(b)
        return Son(x ** self.q) if x is not None else NotImplemented

    # ── Unar ──
    def __neg__(self): return Son(-self.q)
    def __pos__(self): return Son(+self.q)
    def __abs__(self): return Son(abs(self.q))
    def __round__(self, n=0): return Son(round(self.q, n))

    # ── Turga o'girish ──
    def __int__(self): return int(self.q)
    def __float__(self): return self.q
    def __bool__(self): return self.q != 0
    def __complex__(self): return complex(self.q)

    def __repr__(self): return f"Son({self.q!r})"

    def __eq__(self, b):
        x = self._b(b)
        return self.q == x if x is not None else NotImplemented

    def __hash__(self): return hash(self.q)


s = Son(10)

AMALLAR = [
    ("s + 5",           lambda: s + 5),
    ("5 + s",           lambda: 5 + s),
    ("s + Son(5)",      lambda: s + Son(5)),
    ("s - 3",           lambda: s - 3),
    ("3 - s",           lambda: 3 - s),
    ("s * 2",           lambda: s * 2),
    ("2 * s",           lambda: 2 * s),
    ("s / 4",           lambda: s / 4),
    ("100 / s",         lambda: 100 / s),
    ("s // 3",          lambda: s // 3),
    ("s % 3",           lambda: s % 3),
    ("s ** 2",          lambda: s ** 2),
    ("2 ** s",          lambda: 2 ** s),
    ("divmod(s, 3)",    lambda: divmod(s, 3)),
    ("-s",              lambda: -s),
    ("abs(Son(-5))",    lambda: abs(Son(-5))),
    ("round(Son(3.7))", lambda: round(Son(3.7))),
    ("int(s)",          lambda: int(s)),
    ("float(s)",        lambda: float(s)),
    ("bool(Son(0))",    lambda: bool(Son(0))),
    ("sum([Son(1), Son(2)])", lambda: sum([Son(1), Son(2)])),
    ("s == 10",         lambda: s == 10),
    ("s / 0",           lambda: s / 0),
    ("s + 'a'",         lambda: s + "a"),
]

for kod, f in AMALLAR:
    try:
        natija = f"✅ {f()!r}"
    except (TypeError, ZeroDivisionError) as e:
        natija = f"❌ {type(e).__name__}: {str(e)[:34]}"
    print(f"  {kod:<24} {natija}")

print(f"""
  ⭐ sum() ISHLADI, chunki __radd__ int ni qabul qiladi:
     sum([Son(1), Son(2)]) = 0 + Son(1) + Son(2)
                             ↑ int.__add__ → NotImplemented
                               Son.__radd__(0) → Son(1)
""")

Natijaning muhim qismi:

text
=== ⭐ 1. Hal qilish tartibi ===

  Chap() + Ong():
      Chap.__add__(Ong)
      Ong.__radd__(Chap)
    → 'Ong hal qildi'

  1 + Ong():
      Ong.__radd__(int)
    → 'Ong hal qildi'

=== ⚠️ 3. += mexanizmi ===

  __iadd__ BOR:
      __iadd__ (joyida)
    a  = B([1, 2, 3])
    a2 = B([1, 2, 3])   ⚠️ a2 ham o'zgardi

  __iadd__ YO'Q:
      __add__ (yangi obyekt)
    b2 = I([1, 2])   ✅ o'zgarmadi

=== ⚠️ 5. __iadd__ da return unutish ===

  u += Unutgan([2]) dan keyin:
    u = None   ⚠️

=== 6. To'liq amallar to'plami ===

  5 + s                    ✅ Son(15.0)
  3 - s                    ✅ Son(-7.0)
  2 ** s                   ✅ Son(1024.0)
  sum([Son(1), Son(2)])    ✅ Son(3.0)
  s / 0                    ❌ ZeroDivisionError: Son nolga bo'lindi

Nima ko'rsatdi: 2.1, 2.2, 2.3-bo'limlar.

Misol 2 — Turga o'girish

python
"""__int__, __index__, __bool__ va boshqalar."""

from decimal import Decimal
from fractions import Fraction

print("=== ⭐ 1. __int__ vs __index__ ===\n")


class FaqatInt:
    def __init__(self, q): self.q = q
    def __int__(self): return int(self.q)
    def __repr__(self): return f"FaqatInt({self.q})"


class Indeksli:
    def __init__(self, q): self.q = q
    def __int__(self): return int(self.q)
    def __index__(self): return int(self.q)         # ⭐
    def __repr__(self): return f"Indeksli({self.q})"


LST = ["a", "b", "c", "d", "e"]

SINOVLAR = [
    ("int(x)",              lambda x: int(x)),
    ("LST[x]",              lambda x: LST[x]),
    ("LST[x:x+2]",          lambda x: LST[x:2]),
    ("hex(x)",              lambda x: hex(x)),
    ("bin(x)",              lambda x: bin(x)),
    ("oct(x)",              lambda x: oct(x)),
    ("range(x)",            lambda x: list(range(x))),
    ("'ab' * x",            lambda x: "ab" * x),
    ("operator.index(x)",   lambda x: __import__("operator").index(x)),
    ("[0]*x",               lambda x: [0] * x),
]

print(f"  {'Amal':<22} {'FaqatInt(2)':<24} {'Indeksli(2)'}")
print("  " + "─" * 62)
for kod, f in SINOVLAR:
    natijalar = []
    for x in (FaqatInt(2), Indeksli(2)):
        try:
            natijalar.append(f"✅ {f(x)!r}"[:22])
        except TypeError:
            natijalar.append("❌ TypeError")
    print(f"  {kod:<22} {natijalar[0]:<24} {natijalar[1]}")

print(f"""
  ⭐ __index__ MA'NOSI:
     "Bu obyektni butun songa YO'QOTISHSIZ o'girish mumkin"

     __int__  → o'girish mumkin (yo'qotish bo'lishi mumkin)
     __index__ → aniq butun son (indeks sifatida ishlatsa bo'ladi)

  ⚠️ float da __int__ bor, __index__ YO'Q:
""")

for x in [2.0, 2.5, True, Fraction(4, 2), Decimal("2")]:
    ind = "✅" if hasattr(type(x), "__index__") else "❌"
    try:
        LST[x]
        natija = "✅"
    except TypeError:
        natija = "❌"
    print(f"    {x!r:<16} __index__: {ind}   LST[x]: {natija}")

print(f"""
  ⭐ Sabab: LST[2.0] va LST[2.5] chalkash bo'lardi —
     birinchisi ishlab, ikkinchisi ishlamasdi.
     Python ikkalasini ham rad etadi.
""")


print("=== 2. __bool__ va __len__ ===\n")


class BilanBool:
    def __init__(self, q): self.q = q
    def __bool__(self): return self.q > 0
    def __repr__(self): return f"Bool({self.q})"


class BilanLen:
    def __init__(self, n): self.n = n
    def __len__(self): return self.n
    def __repr__(self): return f"Len({self.n})"


class Ikkalasi:
    def __bool__(self):
        print(f"      __bool__ chaqirildi")
        return False
    def __len__(self):
        print(f"      __len__ chaqirildi")
        return 10


class HechBiri:
    def __repr__(self): return "Hech()"


print(f"  {'Obyekt':<16} {'bool()':<10} {'Manba'}")
print("  " + "─" * 46)
for x, manba in [
    (BilanBool(5), "__bool__"),
    (BilanBool(-1), "__bool__"),
    (BilanLen(3), "__len__ != 0"),
    (BilanLen(0), "__len__ == 0"),
    (HechBiri(), "sukut True"),
]:
    print(f"  {x!r:<16} {bool(x)!s:<10} {manba}")

print(f"\n  Ikkalasi ham bor:")
print(f"    bool(Ikkalasi()) →")
n = bool(Ikkalasi())
print(f"    natija: {n}   ⭐ __bool__ ustuvor")

print(f"""
  ⭐ TARTIB:
     1. __bool__()      bor bo'lsa
     2. len(x) != 0     __len__ bor bo'lsa
     3. True            ikkalasi ham yo'q

  ⚠️ __bool__ FAQAT bool qaytarishi kerak:
""")


class YomonBool:
    def __bool__(self): return 1                # ⚠️ int


try:
    bool(YomonBool())
except TypeError as e:
    print(f"    return 1 → ❌ TypeError: {e}")


print("\n=== 3. Barcha o'girishlar ===\n")


class Toliq:
    """Barcha turga o'girish metodlari."""

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

    def __int__(self):      return int(self.q)
    def __float__(self):    return float(self.q)
    def __complex__(self):  return complex(self.q, 0)
    def __bool__(self):     return bool(self.q)
    def __index__(self):    return int(self.q)
    def __bytes__(self):    return str(self.q).encode()
    def __str__(self):      return f"Toliq: {self.q}"
    def __repr__(self):     return f"Toliq({self.q!r})"
    def __format__(self, s): return format(self.q, s)
    def __hash__(self):     return hash(self.q)

    def __round__(self, n=None):
        return Toliq(round(self.q, n) if n is not None else round(self.q))

    def __floor__(self):
        import math
        return Toliq(math.floor(self.q))

    def __ceil__(self):
        import math
        return Toliq(math.ceil(self.q))

    def __trunc__(self):
        import math
        return Toliq(math.trunc(self.q))


import math

t = Toliq(3.7)

OGIRISHLAR = [
    ("int(t)",          lambda: int(t)),
    ("float(t)",        lambda: float(t)),
    ("complex(t)",      lambda: complex(t)),
    ("bool(t)",         lambda: bool(t)),
    ("bytes(t)",        lambda: bytes(t)),
    ("str(t)",          lambda: str(t)),
    ("repr(t)",         lambda: repr(t)),
    ("format(t, '.1f')", lambda: format(t, ".1f")),
    ("hash(t) % 1000",  lambda: hash(t) % 1000),
    ("round(t)",        lambda: round(t)),
    ("round(t, 1)",     lambda: round(t, 1)),
    ("math.floor(t)",   lambda: math.floor(t)),
    ("math.ceil(t)",    lambda: math.ceil(t)),
    ("math.trunc(t)",   lambda: math.trunc(t)),
    ("[0,1,2,3][Toliq(2)]", lambda: [0, 1, 2, 3][Toliq(2)]),
    ("hex(Toliq(255))", lambda: hex(Toliq(255))),
]

for kod, f in OGIRISHLAR:
    try:
        print(f"  {kod:<24} → {f()!r}")
    except Exception as e:
        print(f"  {kod:<24} → ❌ {type(e).__name__}")


print("\n=== 4. numbers ABC ierarxiyasi ===\n")

from numbers import Number, Complex, Real, Rational, Integral

QIYMATLAR = [
    5, 5.0, True, Fraction(1, 2), complex(1, 2),
    Decimal("1.5"), "matn", [1],
]

print(f"  {'Qiymat':<18} {'Number':<9} {'Complex':<9} {'Real':<8} "
      f"{'Rational':<10} {'Integral'}")
print("  " + "─" * 68)
for q in QIYMATLAR:
    qatorlar = [
        "✅" if isinstance(q, A) else "  "
        for A in (Number, Complex, Real, Rational, Integral)
    ]
    print(f"  {q!r:<18} {qatorlar[0]:<9} {qatorlar[1]:<9} "
          f"{qatorlar[2]:<8} {qatorlar[3]:<10} {qatorlar[4]}")

print(f"""
  ⭐ IERARXIYA:
     Number
      └── Complex     __complex__, __abs__, real, imag, conjugate
           └── Real   __float__, __trunc__, __floor__, __ceil__, __round__
                └── Rational    numerator, denominator
                     └── Integral    __int__, __index__, bitwise

  ⚠️ Decimal Real EMAS — ataylab:
     Decimal float bilan aralashishi xato natija berardi:
       Decimal('0.1') + 0.1  →  TypeError  ✅ himoya
""")

try:
    Decimal("0.1") + 0.1
except TypeError as e:
    print(f"    Decimal('0.1') + 0.1 → ❌ TypeError: {str(e)[:52]}")

print(f"    Decimal('0.1') + Fraction(1,10) → "
      f"{Decimal('0.1') + Decimal(1) / Decimal(10)}   ✅ aniq o'girib")
print(f"    ⚠️ Decimal(str(Fraction(1, 10))) ishlamaydi: str → '1/10',")
print(f"       Decimal esa bu ko'rinishni tushunmaydi (InvalidOperation).")
print(f"       To'g'risi: Decimal(f.numerator) / Decimal(f.denominator)")


print("\n=== 5. O'z sinfini ro'yxatga olish ===\n")


class MeningSonim:
    def __init__(self, q): self.q = q
    def __int__(self): return int(self.q)
    def __float__(self): return float(self.q)
    def __index__(self): return int(self.q)
    def __abs__(self): return MeningSonim(abs(self.q))
    def __neg__(self): return MeningSonim(-self.q)
    def __pos__(self): return self
    def __add__(self, b): return MeningSonim(self.q + float(b))
    def __radd__(self, b): return self.__add__(b)
    def __mul__(self, b): return MeningSonim(self.q * float(b))
    def __rmul__(self, b): return self.__mul__(b)
    def __truediv__(self, b): return MeningSonim(self.q / float(b))
    def __rtruediv__(self, b): return MeningSonim(float(b) / self.q)
    def __sub__(self, b): return MeningSonim(self.q - float(b))
    def __rsub__(self, b): return MeningSonim(float(b) - self.q)
    def __pow__(self, b): return MeningSonim(self.q ** float(b))
    def __rpow__(self, b): return MeningSonim(float(b) ** self.q)
    def __eq__(self, b):
        try: return self.q == float(b)
        except (TypeError, ValueError): return NotImplemented
    def __lt__(self, b):
        try: return self.q < float(b)
        except (TypeError, ValueError): return NotImplemented
    def __le__(self, b):
        try: return self.q <= float(b)
        except (TypeError, ValueError): return NotImplemented
    def __hash__(self): return hash(self.q)
    def __floor__(self): return math.floor(self.q)
    def __ceil__(self): return math.ceil(self.q)
    def __trunc__(self): return math.trunc(self.q)
    def __round__(self, n=None): return round(self.q, n)
    def __repr__(self): return f"MeningSonim({self.q!r})"

    @property
    def real(self): return self.q

    @property
    def imag(self): return 0.0

    def conjugate(self): return self

    def __complex__(self): return complex(self.q, 0)


print(f"  Ro'yxatga olishdan oldin:")
m = MeningSonim(5)
for A in (Number, Complex, Real, Integral):
    print(f"    isinstance(m, {A.__name__}) = {isinstance(m, A)}")

Real.register(MeningSonim)                          # ⭐

print(f"\n  Real.register(MeningSonim) dan keyin:")
for A in (Number, Complex, Real, Integral):
    print(f"    isinstance(m, {A.__name__}) = {isinstance(m, A)}")

print(f"\n  Amallar:")
for kod, f in [
    ("m + 3",       lambda: m + 3),
    ("3 + m",       lambda: 3 + m),
    ("m * 2",       lambda: m * 2),
    ("10 / m",      lambda: 10 / m),
    ("m ** 2",      lambda: m ** 2),
    ("abs(-m)",     lambda: abs(-m)),
    ("m < 10",      lambda: m < 10),
    ("sorted([MeningSonim(3), m])",
     lambda: sorted([MeningSonim(3), m])),
    ("math.floor(MeningSonim(3.7))",
     lambda: math.floor(MeningSonim(3.7))),
    ("[0,1,2,3,4,5][m]", lambda: [0, 1, 2, 3, 4, 5][m]),
]:
    print(f"    {kod:<32} → {f()!r}")

print(f"""
  ⚠️ register() TEKSHIRMAYDI — barcha kerakli metodlarni
     o'zingiz yozishingiz kerak.

  ✅ Muqobil: numbers.Real dan MEROS oling —
     abstrakt metodlar majburiy bo'ladi.
""")

Natijaning muhim qismi:

text
=== ⭐ 1. __int__ vs __index__ ===

  Amal                   FaqatInt(2)              Indeksli(2)
  ──────────────────────────────────────────────────────────────
  int(x)                 ✅ 2                     ✅ 2
  LST[x]                 ❌ TypeError             ✅ 'c'
  hex(x)                 ❌ TypeError             ✅ '0x2'
  range(x)               ❌ TypeError             ✅ [0, 1]

    2.0              __index__: ❌   LST[x]: ❌
    True             __index__: ✅   LST[x]: ✅

=== 4. numbers ABC ierarxiyasi ===

  Qiymat             Number    Complex   Real     Rational   Integral
  ────────────────────────────────────────────────────────────────────
  5                  ✅        ✅        ✅       ✅         ✅
  5.0                ✅        ✅        ✅
  True               ✅        ✅        ✅       ✅         ✅
  Fraction(1, 2)     ✅        ✅        ✅       ✅
  Decimal('1.5')     ✅

    Decimal('0.1') + 0.1 → ❌ TypeError: unsupported operand type(s)

Nima ko'rsatdi: 2.5, 2.6-bo'limlar.

Misol 3 — Matritsa va @

python
"""Matritsa amallar — @ operatori bilan."""

from __future__ import annotations

print("=== MATRITSA ===\n")


class Matritsa:
    """To'liq arifmetika bilan matritsa."""

    __slots__ = ("_q", "qatorlar", "ustunlar")

    def __init__(self, qiymatlar):
        q = [list(map(float, r)) for r in qiymatlar]
        if not q or not q[0]:
            raise ValueError("Bo'sh matritsa")
        if len({len(r) for r in q}) != 1:
            raise ValueError("Qatorlar uzunligi bir xil bo'lsin")
        object.__setattr__(self, "_q", q)
        object.__setattr__(self, "qatorlar", len(q))
        object.__setattr__(self, "ustunlar", len(q[0]))

    # ── Fabrika ──

    @classmethod
    def nol(cls, n, m=None):
        m = m or n
        return cls([[0] * m for _ in range(n)])

    @classmethod
    def birlik(cls, n):
        return cls([[1 if i == j else 0 for j in range(n)]
                    for i in range(n)])

    # ── Ko'rinish ──

    def __repr__(self):
        return f"Matritsa({self._q!r})"

    def __str__(self):
        kenglik = max(len(f"{x:g}") for r in self._q for x in r)
        return "\n".join(
            "│ " + "  ".join(f"{x:>{kenglik}g}" for x in r) + " │"
            for r in self._q
        )

    def __format__(self, spec):
        if spec == "olcham":
            return f"{self.qatorlar}×{self.ustunlar}"
        if not spec:
            return str(self)
        return format(str(self), spec)

    # ── Konteyner ──

    def __getitem__(self, indeks):
        if isinstance(indeks, tuple):
            i, j = indeks
            return self._q[i][j]
        return list(self._q[indeks])

    def __len__(self):
        return self.qatorlar

    def __iter__(self):
        return iter(list(r) for r in self._q)

    # ── Taqqoslash ──

    def __eq__(self, b):
        if not isinstance(b, Matritsa):
            return NotImplemented
        return self._q == b._q

    def __hash__(self):
        return hash(tuple(tuple(r) for r in self._q))

    # ── Arifmetika ──

    def __add__(self, b):
        if isinstance(b, (int, float)):
            return Matritsa([[x + b for x in r] for r in self._q])
        if not isinstance(b, Matritsa):
            return NotImplemented
        if (self.qatorlar, self.ustunlar) != (b.qatorlar, b.ustunlar):
            raise ValueError(
                f"O'lchamlar mos emas: {self:olcham} + {b:olcham}"
            )
        return Matritsa([[x + y for x, y in zip(r1, r2)]
                         for r1, r2 in zip(self._q, b._q)])

    def __radd__(self, b):
        return self.__add__(b)

    def __sub__(self, b):
        if isinstance(b, (int, float)):
            return Matritsa([[x - b for x in r] for r in self._q])
        if not isinstance(b, Matritsa):
            return NotImplemented
        return self.__add__(-b)

    def __rsub__(self, b):
        return (-self).__add__(b)

    def __mul__(self, b):
        """⭐ Elementma-element yoki skalyar."""
        if isinstance(b, (int, float)):
            return Matritsa([[x * b for x in r] for r in self._q])
        if not isinstance(b, Matritsa):
            return NotImplemented
        if (self.qatorlar, self.ustunlar) != (b.qatorlar, b.ustunlar):
            raise ValueError("Elementma-element uchun o'lcham bir xil bo'lsin")
        return Matritsa([[x * y for x, y in zip(r1, r2)]
                         for r1, r2 in zip(self._q, b._q)])

    def __rmul__(self, b):
        return self.__mul__(b)

    def __matmul__(self, b):
        """⭐ @ — haqiqiy matritsa ko'paytmasi (PEP 465)."""
        if not isinstance(b, Matritsa):
            return NotImplemented
        if self.ustunlar != b.qatorlar:
            raise ValueError(
                f"Ko'paytirib bo'lmaydi: {self:olcham} @ {b:olcham} "
                f"({self.ustunlar} != {b.qatorlar})"
            )
        return Matritsa([
            [sum(self._q[i][k] * b._q[k][j] for k in range(self.ustunlar))
             for j in range(b.ustunlar)]
            for i in range(self.qatorlar)
        ])

    def __truediv__(self, b):
        if isinstance(b, (int, float)):
            if b == 0:
                raise ZeroDivisionError("Matritsa nolga bo'lindi")
            return Matritsa([[x / b for x in r] for r in self._q])
        return NotImplemented

    def __pow__(self, n):
        """⭐ Matritsa darajasi (@ bilan)."""
        if not isinstance(n, int) or n < 0:
            return NotImplemented
        if self.qatorlar != self.ustunlar:
            raise ValueError("Faqat kvadrat matritsa")
        natija = Matritsa.birlik(self.qatorlar)
        asos = self
        while n:                                # tez darajaga ko'tarish
            if n & 1:
                natija = natija @ asos
            asos = asos @ asos
            n >>= 1
        return natija

    # ── Unar ──

    def __neg__(self):
        return Matritsa([[-x for x in r] for r in self._q])

    def __pos__(self):
        return self

    def __abs__(self):
        return Matritsa([[abs(x) for x in r] for r in self._q])

    def __round__(self, n=0):
        return Matritsa([[round(x, n) for x in r] for r in self._q])

    def __bool__(self):
        return any(x != 0 for r in self._q for x in r)

    # ── Xususiyatlar ──

    @property
    def T(self):
        """Transponirlangan."""
        return Matritsa([[self._q[i][j] for i in range(self.qatorlar)]
                         for j in range(self.ustunlar)])

    @property
    def iz(self):
        """Diagonal yig'indisi."""
        if self.qatorlar != self.ustunlar:
            raise ValueError("Faqat kvadrat matritsa")
        return sum(self._q[i][i] for i in range(self.qatorlar))

    def determinant(self):
        if self.qatorlar != self.ustunlar:
            raise ValueError("Faqat kvadrat matritsa")
        n = self.qatorlar
        if n == 1:
            return self._q[0][0]
        if n == 2:
            return (self._q[0][0] * self._q[1][1]
                    - self._q[0][1] * self._q[1][0])
        d = 0.0
        for j in range(n):
            minor = Matritsa([
                [self._q[i][k] for k in range(n) if k != j]
                for i in range(1, n)
            ])
            d += ((-1) ** j) * self._q[0][j] * minor.determinant()
        return d


A = Matritsa([[1, 2], [3, 4]])
B = Matritsa([[5, 6], [7, 8]])

print("1. Asosiy amallar:\n")

print(f"  A =")
for q in str(A).split("\n"):
    print(f"    {q}")
print(f"\n  B =")
for q in str(B).split("\n"):
    print(f"    {q}")

AMALLAR = [
    ("A + B",       lambda: A + B),
    ("A - B",       lambda: A - B),
    ("A * B",       lambda: A * B),
    ("A @ B",       lambda: A @ B),
    ("A * 2",       lambda: A * 2),
    ("2 * A",       lambda: 2 * A),
    ("A + 10",      lambda: A + 10),
    ("10 + A",      lambda: 10 + A),
    ("10 - A",      lambda: 10 - A),
    ("A / 2",       lambda: A / 2),
    ("-A",          lambda: -A),
    ("A.T",         lambda: A.T),
    ("A ** 2",      lambda: A ** 2),
    ("A ** 0",      lambda: A ** 0),
]

print()
for kod, f in AMALLAR:
    natija = f()
    qatorlar = str(natija).split("\n")
    print(f"  {kod:<10} = {qatorlar[0]}")
    for q in qatorlar[1:]:
        print(f"  {'':<10}   {q}")


print("\n\n2. ⭐ @ va * farqi:\n")

print(f"  A * B  (elementma-element):")
for q in str(A * B).split("\n"):
    print(f"    {q}")
print(f"    → [1*5, 2*6; 3*7, 4*8]")

print(f"\n  A @ B  (matritsa ko'paytmasi):")
for q in str(A @ B).split("\n"):
    print(f"    {q}")
print(f"    → [1*5+2*7, 1*6+2*8; 3*5+4*7, 3*6+4*8]")

print(f"""
  ⭐ PEP 465 (Python 3.5) @ operatorini QO'SHDI.

     Sabab: NumPy da ikki xil ko'paytma kerak edi:
       *  elementma-element (Hadamard)
       @  matritsa ko'paytmasi

     Oldin: A.dot(B) yoki numpy.matmul(A, B)
     Endi:  A @ B
""")


print("=== 3. O'lcham xatolari ===\n")

C = Matritsa([[1, 2, 3], [4, 5, 6]])
D = Matritsa([[1, 2], [3, 4], [5, 6]])

print(f"  C: {C:olcham}, D: {D:olcham}\n")

for kod, f in [
    ("C @ D",   lambda: C @ D),
    ("D @ C",   lambda: D @ C),
    ("C @ A",   lambda: C @ A),
    ("C + A",   lambda: C + A),
    ("C.determinant()", lambda: C.determinant()),
    ("C.iz",    lambda: C.iz),
]:
    try:
        natija = f()
        if isinstance(natija, Matritsa):
            print(f"  {kod:<20} ✅ {natija:olcham}")
        else:
            print(f"  {kod:<20} ✅ {natija}")
    except ValueError as e:
        print(f"  {kod:<20} ❌ ValueError: {e}")


print("\n\n4. Xususiyatlar:\n")

E = Matritsa([[4, -2, 1], [3, 6, -4], [2, 1, 8]])

print(f"  E =")
for q in str(E).split("\n"):
    print(f"    {q}")

print(f"\n    E.iz            = {E.iz}")
print(f"    E.determinant() = {E.determinant()}")
print(f"    E.T =")
for q in str(E.T).split("\n"):
    print(f"      {q}")

print(f"\n  Birlik matritsa xossasi:")
I = Matritsa.birlik(3)
print(f"    E @ I == E: {E @ I == E}   ✅")
print(f"    I @ E == E: {I @ E == E}   ✅")

print(f"\n  Transponirlash xossalari:")
print(f"    (A.T).T == A:      {(A.T).T == A}   ✅")
print(f"    (A @ B).T == B.T @ A.T: {(A @ B).T == B.T @ A.T}   ✅")


print("\n\n5. Konteyner protokoli:\n")

print(f"  A[0]     = {A[0]}")
print(f"  A[0, 1]  = {A[0, 1]}")
print(f"  A[1, 0]  = {A[1, 0]}")
print(f"  len(A)   = {len(A)}")
print(f"  list(A)  = {list(A)}")
print(f"  bool(A)  = {bool(A)}")
print(f"  bool(Matritsa.nol(2)) = {bool(Matritsa.nol(2))}")

print(f"\n  Iteratsiya:")
for i, qator in enumerate(A):
    print(f"    {i}-qator: {qator}")


print("\n\n6. Fibonachchi — matritsa darajasi bilan:\n")


def fib_matritsa(n: int) -> int:
    """⭐ O(log n) — matritsa darajasi orqali."""
    if n <= 0:
        return 0
    M = Matritsa([[1, 1], [1, 0]])
    return int((M ** (n - 1))[0, 0])


def fib_oddiy(n: int) -> int:
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a


print(f"  {'n':<6} {'Matritsa':<16} {'Oddiy':<16} {'Mos'}")
print("  " + "─" * 48)
for n in [1, 5, 10, 20, 30, 40]:
    m = fib_matritsa(n)
    o = fib_oddiy(n)
    print(f"  {n:<6} {m:<16,} {o:<16,} {'✅' if m == o else '❌'}")

print(f"""
  ⭐ [[1,1],[1,0]]^n = [[F(n+1), F(n)], [F(n), F(n-1)]]

     __pow__ tez darajaga ko'tarish ishlatadi:
     O(log n) matritsa ko'paytmasi
""")

import timeit

n = 100
t1 = timeit.timeit(lambda: fib_matritsa(n), number=1000)
t2 = timeit.timeit(lambda: fib_oddiy(n), number=1000)
print(f"  n={n}, 1000 marta:")
print(f"    Matritsa: {t1:.3f}s")
print(f"    Oddiy:    {t2:.3f}s")
print(f"    ⚠️ Kichik n da oddiy tezroq (matritsa ustama xarajati)")

Natijaning muhim qismi:

text
1. Asosiy amallar:

  A + B      = │  6   8 │
                 │ 10  12 │
  A * B      = │  5  12 │
                 │ 21  32 │
  A @ B      = │ 19  22 │
                 │ 43  50 │

2. ⭐ @ va * farqi:

  A @ B  (matritsa ko'paytmasi):
    → [1*5+2*7, 1*6+2*8; 3*5+4*7, 3*6+4*8]

=== 3. O'lcham xatolari ===

  C @ D                ✅ 2×2
  C @ A                ❌ ValueError: Ko'paytirib bo'lmaydi: 2×3 @ 2×2
  C.determinant()      ❌ ValueError: Faqat kvadrat matritsa

6. Fibonachchi — matritsa darajasi bilan:

  n      Matritsa         Oddiy            Mos
  ────────────────────────────────────────────────
  40     102,334,155      102,334,155      ✅

Nima ko'rsatdi: 2.1, 2.4, 2.7-bo'limlar.

Misol 4 — Amaliy: birlik tizimi

python
"""Fizik birliklar — turlar aralashuvidan himoya."""

from __future__ import annotations

import math
from dataclasses import dataclass
from decimal import Decimal
from typing import Any

print("=== FIZIK BIRLIKLAR ===\n")


@dataclass(frozen=True, slots=True)
class Olcham:
    """SI asosiy birliklarining darajalari."""

    m: int = 0          # metr
    kg: int = 0         # kilogramm
    s: int = 0          # sekund
    A: int = 0          # amper
    K: int = 0          # kelvin

    def __mul__(self, b: "Olcham") -> "Olcham":
        return Olcham(self.m + b.m, self.kg + b.kg, self.s + b.s,
                      self.A + b.A, self.K + b.K)

    def __truediv__(self, b: "Olcham") -> "Olcham":
        return Olcham(self.m - b.m, self.kg - b.kg, self.s - b.s,
                      self.A - b.A, self.K - b.K)

    def __pow__(self, n: int) -> "Olcham":
        return Olcham(self.m * n, self.kg * n, self.s * n,
                      self.A * n, self.K * n)

    def __bool__(self) -> bool:
        return any((self.m, self.kg, self.s, self.A, self.K))

    def __str__(self) -> str:
        if not self:
            return "o'lchamsiz"
        surat, maxraj = [], []
        for nom, d in (("m", self.m), ("kg", self.kg), ("s", self.s),
                       ("A", self.A), ("K", self.K)):
            if d > 0:
                surat.append(nom if d == 1 else f"{nom}^{d}")
            elif d < 0:
                maxraj.append(nom if d == -1 else f"{nom}^{-d}")
        s = "·".join(surat) or "1"
        return f"{s}/{'·'.join(maxraj)}" if maxraj else s


# ── Standart o'lchamlar ──
OLCHAMSIZ = Olcham()
UZUNLIK = Olcham(m=1)
MASSA = Olcham(kg=1)
VAQT = Olcham(s=1)
TEZLIK = UZUNLIK / VAQT
TEZLANISH = TEZLIK / VAQT
KUCH = MASSA * TEZLANISH
ENERGIYA = KUCH * UZUNLIK
QUVVAT = ENERGIYA / VAQT
BOSIM = KUCH / (UZUNLIK ** 2)


class Miqdor:
    """Birlik bilan son — noto'g'ri amallardan himoya."""

    __slots__ = ("qiymat", "olcham")

    NOMLAR = {
        UZUNLIK: "m", MASSA: "kg", VAQT: "s",
        TEZLIK: "m/s", TEZLANISH: "m/s²",
        KUCH: "N", ENERGIYA: "J", QUVVAT: "W", BOSIM: "Pa",
        Olcham(m=2): "m²", Olcham(m=3): "m³",
    }

    def __init__(self, qiymat: float, olcham: Olcham = OLCHAMSIZ):
        object.__setattr__(self, "qiymat", float(qiymat))
        object.__setattr__(self, "olcham", olcham)

    def __setattr__(self, n, q):
        raise AttributeError("Miqdor o'zgarmas")

    # ── Ko'rinish ──

    def __repr__(self) -> str:
        return f"Miqdor({self.qiymat!r}, {self.olcham!r})"

    def __str__(self) -> str:
        if not self.olcham:
            return f"{self.qiymat:g}"
        birlik = self.NOMLAR.get(self.olcham) or str(self.olcham)
        return f"{self.qiymat:g} {birlik}"

    def __format__(self, spec: str) -> str:
        if spec == "si":
            return f"{self.qiymat:g} {self.olcham}"
        if not spec:
            return str(self)
        birlik = self.NOMLAR.get(self.olcham) or str(self.olcham)
        return f"{format(self.qiymat, spec)} {birlik}".strip()

    # ── Taqqoslash ──

    def _tekshir(self, b, amal: str) -> "Miqdor":
        if isinstance(b, (int, float)):
            b = Miqdor(b, OLCHAMSIZ)
        if not isinstance(b, Miqdor):
            return NotImplemented
        if self.olcham != b.olcham:
            raise TypeError(
                f"O'lchamlar mos emas: [{self.olcham}] {amal} "
                f"[{b.olcham}]"
            )
        return b

    def __eq__(self, b) -> bool:
        if isinstance(b, (int, float)):
            return not self.olcham and self.qiymat == b
        if not isinstance(b, Miqdor):
            return NotImplemented
        return (self.olcham == b.olcham
                and math.isclose(self.qiymat, b.qiymat, rel_tol=1e-9))

    def __lt__(self, b) -> bool:
        b = self._tekshir(b, "<")
        if b is NotImplemented:
            return NotImplemented
        return self.qiymat < b.qiymat

    def __le__(self, b) -> bool:
        return self < b or self == b

    def __gt__(self, b) -> bool:
        b = self._tekshir(b, ">")
        return NotImplemented if b is NotImplemented else self.qiymat > b.qiymat

    def __ge__(self, b) -> bool:
        return self > b or self == b

    def __hash__(self) -> int:
        return hash((round(self.qiymat, 9), self.olcham))

    # ── Arifmetika ──

    def __add__(self, b) -> "Miqdor":
        b = self._tekshir(b, "+")
        if b is NotImplemented:
            return NotImplemented
        return Miqdor(self.qiymat + b.qiymat, self.olcham)

    def __radd__(self, b) -> "Miqdor":
        # ⭐ sum() hisobni 0 dan boshlaydi: 0 + m(1) → m(1).__radd__(0).
        #    Oddiy __radd__ = __add__ bo'lsa, 0 "o'lchamsiz" deb olinib,
        #    [o'lchamsiz] + [m] → TypeError bo'lardi.
        if isinstance(b, int) and not isinstance(b, bool) and b == 0:
            return self
        return self.__add__(b)

    def __sub__(self, b) -> "Miqdor":
        b = self._tekshir(b, "-")
        if b is NotImplemented:
            return NotImplemented
        return Miqdor(self.qiymat - b.qiymat, self.olcham)

    def __rsub__(self, b) -> "Miqdor":
        return (-self).__add__(b)

    def __mul__(self, b) -> "Miqdor":
        """⭐ O'lchamlar KO'PAYADI."""
        if isinstance(b, (int, float)):
            return Miqdor(self.qiymat * b, self.olcham)
        if not isinstance(b, Miqdor):
            return NotImplemented
        return Miqdor(self.qiymat * b.qiymat, self.olcham * b.olcham)

    __rmul__ = __mul__

    def __truediv__(self, b) -> "Miqdor":
        """⭐ O'lchamlar BO'LINADI."""
        if isinstance(b, (int, float)):
            if b == 0:
                raise ZeroDivisionError("Nolga bo'lish")
            return Miqdor(self.qiymat / b, self.olcham)
        if not isinstance(b, Miqdor):
            return NotImplemented
        if b.qiymat == 0:
            raise ZeroDivisionError("Nolga bo'lish")
        return Miqdor(self.qiymat / b.qiymat, self.olcham / b.olcham)

    def __rtruediv__(self, b) -> "Miqdor":
        if not isinstance(b, (int, float)):
            return NotImplemented
        if self.qiymat == 0:
            raise ZeroDivisionError("Nolga bo'lish")
        return Miqdor(b / self.qiymat, OLCHAMSIZ / self.olcham)

    def __pow__(self, n) -> "Miqdor":
        if not isinstance(n, int):
            raise TypeError("Daraja butun son bo'lsin (o'lcham uchun)")
        return Miqdor(self.qiymat ** n, self.olcham ** n)

    # ── Unar ──

    def __neg__(self): return Miqdor(-self.qiymat, self.olcham)
    def __pos__(self): return self
    def __abs__(self): return Miqdor(abs(self.qiymat), self.olcham)
    def __round__(self, n=0): return Miqdor(round(self.qiymat, n), self.olcham)
    def __bool__(self): return self.qiymat != 0

    def __float__(self) -> float:
        if self.olcham:
            raise TypeError(
                f"O'lchamli miqdorni float ga o'girib bo'lmaydi: "
                f"[{self.olcham}]"
            )
        return self.qiymat

    def sqrt(self) -> "Miqdor":
        yarim = Olcham(*(d // 2 for d in
                         (self.olcham.m, self.olcham.kg, self.olcham.s,
                          self.olcham.A, self.olcham.K)))
        if yarim * yarim != self.olcham:
            raise ValueError(
                f"[{self.olcham}] dan ildiz chiqarib bo'lmaydi"
            )
        return Miqdor(math.sqrt(self.qiymat), yarim)


# ── Qulaylik funksiyalari ──
def m(q): return Miqdor(q, UZUNLIK)
def kg(q): return Miqdor(q, MASSA)
def s(q): return Miqdor(q, VAQT)
def N(q): return Miqdor(q, KUCH)


print("1. Asosiy amallar:\n")

masofa = m(100)
vaqt = s(9.58)
massa = kg(75)

print(f"  masofa = {masofa}")
print(f"  vaqt   = {vaqt}")
print(f"  massa  = {massa}\n")

tezlik = masofa / vaqt
print(f"  tezlik = masofa / vaqt = {tezlik}")
print(f"    SI: {tezlik:si}")
print(f"    2 xona: {tezlik:.2f}")

tezlanish = tezlik / vaqt
kuch = massa * tezlanish
energiya = kuch * masofa
quvvat = energiya / vaqt

print(f"\n  tezlanish = tezlik / vaqt = {tezlanish:.3f}")
print(f"  kuch      = massa * tezlanish = {kuch:.1f}")
print(f"  energiya  = kuch * masofa = {energiya:.0f}")
print(f"  quvvat    = energiya / vaqt = {quvvat:.0f}")


print("\n\n2. ⭐ Noto'g'ri amallardan himoya:\n")

XATOLAR = [
    ("masofa + vaqt",       lambda: masofa + vaqt),
    ("masofa + massa",      lambda: masofa + massa),
    ("masofa - tezlik",     lambda: masofa - tezlik),
    ("masofa < vaqt",       lambda: masofa < vaqt),
    ("float(masofa)",       lambda: float(masofa)),
    ("masofa ** 0.5",       lambda: masofa ** 0.5),
    ("masofa / s(0)",       lambda: masofa / s(0)),
]

for kod, f in XATOLAR:
    try:
        natija = f"⚠️ {f()}"
    except (TypeError, ZeroDivisionError, ValueError) as e:
        natija = f"✅ {type(e).__name__}: {str(e)[:44]}"
    print(f"  {kod:<24} {natija}")

print(f"\n  ✅ To'g'ri amallar:")
for kod, f in [
    ("masofa + m(50)",      lambda: masofa + m(50)),
    ("masofa * 2",          lambda: masofa * 2),
    ("2 * masofa",          lambda: 2 * masofa),
    ("masofa / m(2)",       lambda: masofa / m(2)),
    ("masofa ** 2",         lambda: masofa ** 2),
    ("(masofa ** 2).sqrt()", lambda: (masofa ** 2).sqrt()),
    ("masofa < m(200)",     lambda: masofa < m(200)),
    ("sum([m(1), m(2), m(3)])", lambda: sum([m(1), m(2), m(3)])),
    ("abs(m(-5))",          lambda: abs(m(-5))),
    ("-masofa",             lambda: -masofa),
    ("100 / s(4)",          lambda: 100 / s(4)),
]:
    print(f"    {kod:<26} → {f()}")


print("\n\n3. ⭐ O'lcham tahlili:\n")

FORMULALAR = [
    ("Tezlik",      masofa / vaqt),
    ("Tezlanish",   masofa / (vaqt ** 2)),
    ("Kuch",        massa * masofa / (vaqt ** 2)),
    ("Energiya",    massa * (masofa ** 2) / (vaqt ** 2)),
    ("Quvvat",      massa * (masofa ** 2) / (vaqt ** 3)),
    ("Bosim",       massa / (masofa * vaqt ** 2)),
    ("Zichlik",     massa / (masofa ** 3)),
    ("Chastota",    Miqdor(1) / vaqt),
]

si_sarlavha = "SI o'lchami"
print(f"  {'Kattalik':<14} {'Qiymat':<20} {si_sarlavha}")
print("  " + "─" * 56)
for nom, q in FORMULALAR:
    print(f"  {nom:<14} {str(q):<20} {q.olcham}")


print("\n\n4. Fizik masala:\n")

print("  Masala: 75 kg massali jism 10 m balandlikdan tushdi.")
print("          Yerga urilish tezligi va energiyasi?\n")

g = Miqdor(9.81, TEZLANISH)
h = m(10)
mm = kg(75)

# v = sqrt(2gh)
v = (Miqdor(2) * g * h).sqrt()
# E = mgh
E = mm * g * h
# t = sqrt(2h/g)
t = (Miqdor(2) * h / g).sqrt()

print(f"    g = {g}")
print(f"    h = {h}")
print(f"    m = {mm}\n")
print(f"    v = sqrt(2gh)   = {v:.2f}")
print(f"        SI: {v:si}")
print(f"    E = mgh         = {E:.1f}")
print(f"        SI: {E:si}")
print(f"    t = sqrt(2h/g)  = {t:.3f}")

print(f"\n  ⭐ Tekshiruv (kinetik energiya):")
Ek = Miqdor(0.5) * mm * (v ** 2)
print(f"    Ek = ½mv² = {Ek:.1f}")
print(f"    E == Ek: {E == Ek}   ✅")


print("\n\n5. Taqqoslash va saralash:\n")

MASOFALAR = [m(100), m(42.195), m(5000), m(1500), m(21.0975)]

print(f"  Saralangan: {[str(x) for x in sorted(MASOFALAR)]}")
print(f"  Eng uzun:   {max(MASOFALAR)}")
print(f"  Jami:       {sum(MASOFALAR)}")
print(f"  O'rtacha:   {sum(MASOFALAR) / len(MASOFALAR):.2f}")

print(f"\n  Turli o'lchamlarni saralash:")
try:
    sorted([m(100), s(50)])
except TypeError as e:
    print(f"    sorted([m(100), s(50)]) → ✅ TypeError: {str(e)[:44]}")

print(f"\n  Hashable:")
print(f"    {{m(1), m(1), m(2), s(1)}} → "
      f"{len({m(1), m(1), m(2), s(1)})} element")
print(f"    ⭐ m(1) va s(1) — turli (o'lcham hashga kiradi)")

print(f"""

  ⭐ BU SINFDA:

     __add__/__radd__     o'lcham TEKSHIRILADI
     __mul__/__rmul__     o'lcham KO'PAYADI
     __truediv__          o'lcham BO'LINADI
     __rtruediv__         100 / s(4) uchun
     __pow__              o'lcham darajaga ko'tariladi
     __neg__/__abs__      o'lcham saqlanadi
     __float__            ⚠️ o'lchamli bo'lsa TypeError
     __eq__/__hash__      o'lcham hisobga olinadi
     __lt__ va boshqalar  turli o'lcham → TypeError

  ⭐ NATIJA: Mars Climate Orbiter (1999) tipidagi
     xatolar MUMKIN EMAS — birliklar aralashuvi
     ish vaqtida darhol aniqlanadi.
""")

Natijaning muhim qismi:

text
1. Asosiy amallar:

  tezlik = masofa / vaqt = 10.4384 m/s
    SI: 10.4384 m/s
  kuch      = massa * tezlanish = 81.7 N
  energiya  = kuch * masofa = 8172 J

2. ⭐ Noto'g'ri amallardan himoya:

  masofa + vaqt            ✅ TypeError: O'lchamlar mos emas: [m] + [s]
  masofa < vaqt            ✅ TypeError: O'lchamlar mos emas: [m] < [s]
  float(masofa)            ✅ TypeError: O'lchamli miqdorni float ga
  masofa ** 0.5            ✅ TypeError: Daraja butun son bo'lsin

3. ⭐ O'lcham tahlili:

  Kattalik       Qiymat               SI o'lchami
  ────────────────────────────────────────────────────────
  Tezlik         10.4384 m/s          m/s
  Kuch           81.7204 N            m·kg/s^2
  Energiya       8172.04 J            m^2·kg/s^2
  Bosim          0.00817204 Pa        kg/m·s^2

4. Fizik masala:

    v = sqrt(2gh)   = 14.01 m/s
    E = mgh         = 7357.5 J
    E == Ek: True   ✅

Nima ko'rsatdi: 2.1, 2.2, 2.4, 2.5, 2.7-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"__add__ yetadi" 3 * v uchun __rmul__ kerak
"+= doim __iadd__ chaqiradi" Yo'q bo'lsa __add__ ga tushadi
"__iadd__ None qaytarsa bo'ladi" a None bo'ladi
"__int__ indekslash uchun yetadi" __index__ kerak
"__bool__ int qaytarishi mumkin" TypeError
"Decimal — Real" Ataylab ro'yxatga olinmagan
"sum() faqat __add__ talab qiladi" __radd__ ham (0 + x)
"@ — dekorator belgisi" Matritsa ko'paytmasi ham (PEP 465)

6. Keng tarqalgan xatolar va yechimlari

1. __r*__ unutish

python
def __mul__(self, n): ...       # ⚠️ 3 * v ishlamaydi
__rmul__ = __mul__              # ✅ kommutativ bo'lsa

2. Kommutativ bo'lmaganda tartib

python
def __rsub__(self, b):
    return Pul(self.m - b)      # ⚠️ teskari!
    return Pul(b - self.m)      # ✅

3. __iadd__ da return yo'q

python
def __iadd__(self, b):
    self.el.extend(b.el)        # ⚠️ None
    return self                 # ✅

4. NotImplemented o'rniga TypeError

python
def __add__(self, b):
    raise TypeError(...)        # ⚠️ teskari sinalmaydi
    return NotImplemented       # ✅

5. __index__ unutish

python
def __int__(self): ...          # ⚠️ lst[x] ishlamaydi
def __index__(self): ...        # ✅

6. sum() uchun __radd__ yo'q

python
sum([v1, v2])                   # ⚠️ 0 + v1
def __radd__(self, b):
    if b == 0: return self      # ✅

7. __bool__ int qaytaradi

python
def __bool__(self): return 1    # ❌ TypeError
def __bool__(self): return True # ✅

8. __eq__ da float taqqoslash

python
return self.q == b.q                        # ⚠️ float aniqligi
return math.isclose(self.q, b.q)            # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 8.12-dars (o'tilgan): __eq__, NotImplemented
  • 8.14-dars: konteyner protokoli
  • 3-qism: sonlar, Decimal, Fraction
  • numbers, operator, math — standart kutubxona
  • NumPy, pandas: @, __array_ufunc__
  • pint, astropy.units: haqiqiy birlik kutubxonalari
  • PEP 465: @ operatori

8. Eng yaxshi amaliyotlar

  1. Har __op__ uchun __rop__ yozing. Aks holda 3 * v ishlamaydi.

  2. NotImplemented qaytaring. TypeError tashlamang — teskari sinalsin.

  3. __iadd__ da return self. Unutish — jimgina xato.

  4. __iadd__ faqat o'zgaruvchan sinflarda. O'zgarmas bo'lsa — __add__ yetadi.

  5. __index__ qo'shing. Butun son ma'nosini bersa.

  6. sum() uchun __radd__ da 0 ni qabul qiling. Yoki boshlang'ich qiymat bering.

  7. O'lchamlarni tekshiring. Ma'nosiz amallar TypeError bersin.

  8. float taqqoslashda math.isclose. Aniq tenglik xavfli.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
1.  class A:
        def __add__(self, b): return "add"
    print(A() + 1, end=" ")
    try: print(1 + A())
    except TypeError: print("TypeError")
2.  class A:
        def __radd__(self, b): return "radd"
    print(1 + A())
3.  lst = [1]; lst2 = lst; lst += [2]; print(lst2)
4.  t = (1,); t2 = t; t += (2,); print(t2)
5.  class A:
        def __init__(self, x): self.x = x
        def __iadd__(self, b): self.x += b
    a = A(1); a += 2; print(a)
6.  class A:
        def __int__(self): return 2
    try: print([0,1,2][A()])
    except TypeError: print("TypeError")
7.  class A:
        def __len__(self): return 0
    print(bool(A()))
8.  class A:
        def __bool__(self): return False
        def __len__(self): return 5
    print(bool(A()), len(A()))
9.  print(sum([1, 2, 3]), sum([], 10))
10. from decimal import Decimal
    from numbers import Real
    print(isinstance(Decimal("1"), Real))
11. class A:
        def __mul__(self, b): return "mul"
    print(A() * 2, end=" ")
    try: print(2 * A())
    except TypeError: print("TypeError")
12. print(divmod(7, 2), 7 // 2, 7 % 2)
Javoblar
  1. add TypeError — __radd__ yo'q
  2. radd
  3. [1, 2] — list.__iadd__ joyida
  4. (1,) — tuple.__iadd__ yo'q
  5. None — __iadd__ return yo'q
  6. TypeError — __index__ kerak
  7. False
  8. False 5 — __bool__ ustuvor
  9. 6 10
  10. False — Decimal ataylab ro'yxatda yo'q
  11. mul TypeError
  12. (3, 1) 3 1

Vazifa 2: Xatolarni tuzating

python
1.  def __mul__(self, n): return Pul(self.m * n)
2.  def __rsub__(self, b): return Pul(self.m - b)
3.  def __iadd__(self, b): self.el.extend(b.el)
4.  def __add__(self, b):
        if not isinstance(b, A): raise TypeError("...")
5.  def __int__(self): return int(self.q)     # lst[x] kerak
6.  def __bool__(self): return len(self.el)
7.  sum([Vektor(1,2), Vektor(3,4)])
8.  def __eq__(self, b): return self.q == b.q  # float
Javoblar
python
1.  __rmul__ = __mul__  qo'shing
2.  return Pul(b - self.m)
3.  return self  qo'shing
4.  return NotImplemented
5.  __index__ ham qo'shing
6.  return len(self.el) > 0  (yoki bool(...))
7.  __radd__ qo'shing yoki sum(..., Vektor(0,0))
8.  math.isclose(self.q, b.q)

Vazifa 3: Kasr sinfi

Yozing:

  1. Avtomatik qisqartirish (gcd)
  2. +, -, *, /, ** va __r*__
  3. int, float, Kasr bilan aralash amallar
  4. __neg__, __abs__, __round__
  5. __int__, __float__, __bool__
  6. To'liq taqqoslash (total_ordering)
  7. numbers.Rational ga ro'yxatga olish

Vazifa 4: Polinom sinfi

  1. [1, 2, 3] → 1 + 2x + 3x²
  2. +, -, * (svertka)
  3. __call__ — qiymatni hisoblash
  4. __truediv__ — qoldiq bilan bo'lish (divmod)
  5. hosila(), integral()
  6. __str__ — chiroyli ko'rinish (3x² + 2x + 1)
  7. __pow__ — butun daraja

Vazifa 5: Bitwise bayroqlar

  1. Bayroq sinfi — &, |, ^, ~, <<, >>
  2. Barcha __r*__ va __i*__ versiyalar
  3. __contains__ — bayroq bormi
  4. __iter__ — o'rnatilgan bayroqlar
  5. __index__, __int__, __bool__
  6. enum.Flag bilan solishtiring

Vazifa 6: Arifmetika tekshiruvchisi

Vosita yozing:

  1. Sinfda __op__ bor, __rop__ yo'qlarni topsin
  2. __iadd__ da return yo'qligini
  3. NotImplemented o'rniga istisno tashlashni
  4. __int__ bor, __index__ yo'qligini
  5. __eq__ bor, __hash__ yo'qligini
  6. Kommutativ bo'lmagan __r*__ xatolarini

Vazifa 7: O'ylash

Nega Python NotImplemented mexanizmini tanladi — nega operator ortiqcha yuklashda oddiy istisno ishlatilmaydi?

Javob

Chunki ikkala operand ham hal qilishga urinishi kerak, va istisno birinchi urinishdayoq to'xtatib qo'yardi.

1. Muammo: kim javobgar?

python
3 * Vektor(1, 2)

Kim bu amalni bajarishi kerak?

  • int.__mul__(3, v) — int Vektor ni bilmaydi
  • Vektor.__rmul__(v, 3) — Vektor int ni biladi

Muammo: int — o'rnatilgan tur, uni o'zgartirib bo'lmaydi. Sizning sinfingiz int dan keyin yozilgan.

2. Istisno bilan bo'lsa

Faraz qilaylik, int.__mul__ TypeError tashlasin:

python
3 * Vektor(1, 2)
# int.__mul__(3, v) → TypeError
# ⚠️ Python bu yerda to'xtaydi

Vektor.__rmul__ hech qachon chaqirilmasdi. Ya'ni:

  • O'rnatilgan turlar bilan aralash amal mumkin bo'lmasdi
  • 3 * v, 2 + pul, 10 / miqdor — hech biri
  • NumPy, Decimal, Fraction — hech biri ishlamasdi

3. NotImplemented — "men bilmayman, sen sinab ko'r"

python
class Vektor:
    def __mul__(self, b):
        if not isinstance(b, (int, float, Vektor)):
            return NotImplemented       # ⭐ "men bu turni bilmayman"
        ...

Bu — muzokara protokoli:

a + b:
  a: "Men b ni bilamanmi?"    → yo'q → NotImplemented
  b: "Men a ni bilamanmi?"    → ha  → natija
  ikkalasi ham yo'q           → TypeError

4. Amalda qanday ishlaydi

python
from decimal import Decimal
from fractions import Fraction

Decimal("1") + Fraction(1, 2)
# Decimal.__add__(Fraction)  → NotImplemented
# Fraction.__radd__(Decimal) → NotImplemented
# → TypeError                  ✅ ataylab
python
Fraction(1, 2) + 0.5
# Fraction.__add__(float) → 1.0   ✅ Fraction float ni biladi
python
import numpy as np
np.array([1, 2]) * 3            # ✅ ndarray.__mul__
3 * np.array([1, 2])            # ✅ ndarray.__rmul__

NumPy int ni o'zgartirmasdan int bilan ishlay oladi — aynan shu mexanizm tufayli.

5. Nega maxsus qiymat, None emas

python
def __add__(self, b):
    return None                 # ⚠️ chalkash

None — haqiqiy qaytarish qiymati bo'lishi mumkin:

python
class Bosh:
    def __add__(self, b):
        return None             # ataylab None qaytaradi

NotImplemented — maxsus singleton, hech qachon oddiy qiymat sifatida ishlatilmaydi:

python
type(NotImplemented)            # <class 'NotImplementedType'>
NotImplemented is NotImplemented  # True — yagona nusxa

6. NotImplemented — istisno emas

Bu chalkashlik keng tarqalgan:

python
NotImplemented                  # ⭐ QIYMAT (singleton)
NotImplementedError             # ⭐ ISTISNO (sinf)
python
def __add__(self, b):
    return NotImplemented       # ✅ qaytariladi

@abstractmethod
def f(self):
    raise NotImplementedError   # ✅ tashlanadi

Xato:

python
def __add__(self, b):
    raise NotImplemented        # ❌ TypeError: exceptions must derive
                                #    from BaseException

7. bool(NotImplemented) — eskirgan

python
if x == y:                      # x.__eq__(y) NotImplemented qaytarsa
    ...

Python 3.9 dan bool(NotImplemented) DeprecationWarning beradi:

python
import warnings
warnings.simplefilter("always")
bool(NotImplemented)
# DeprecationWarning: NotImplemented should not be used in a
# boolean context

Sabab: NotImplemented truthy edi, ya'ni:

python
class A:
    def __eq__(self, b): return NotImplemented

if A() == 42:                   # ⚠️ Python `is` ga tushadi → False
    ...

# Lekin to'g'ridan-to'g'ri:
n = A().__eq__(42)              # NotImplemented
if n:                           # ⚠️ True! (truthy)
    ...

Kelajakda bu TypeError bo'ladi.

8. Avlod ustuvorligi

Mexanizmda yana bir nozik qoida bor:

python
class Ota:
    def __add__(self, b): return "Ota.__add__"

class Bola(Ota):
    def __radd__(self, a): return "Bola.__radd__"

Ota() + Bola()                  # 'Bola.__radd__'  ⭐ avlod birinchi

Sabab: avlod ota-sinfdan ko'proq biladi. Agar Ota.__add__ birinchi ishlaganda, u Bola ni oddiy Ota deb hisoblardi va avlodning maxsus mantiqini o'tkazib yuborardi.

Shart: avlod __radd__ ni qayta aniqlagan bo'lishi kerak (meros olgan emas).

9. Boshqa tillar

Til Yondashuv
Python NotImplemented muzokara
C++ Erkin funksiya: operator+(int, Vec)
Ruby coerce metodi
Haskell Tur sinflari (kompilyatsiya paytida)
Rust impl Mul<Vec> for i32 (trait)
Java Operator ortiqcha yuklash yo'q

C++ eng moslashuvchan:

cpp
Vec operator*(int lhs, const Vec& rhs) { ... }
// ⭐ Sinf TASHQARISIDA — int ni o'zgartirmasdan

C++ da operator — erkin funksiya, sinf a'zosi bo'lishi shart emas. Shuning uchun __r*__ kerak emas.

Ruby — coerce:

ruby
class Vec
  def coerce(other)
    [Vec.new(other), self]      # ⭐ ikkalasini bir turga keltiradi
  end
end

3 * Vec.new(5)
# Integer#* → Vec ni bilmaydi → vec.coerce(3) ni chaqiradi
# → [Vec(3), Vec(5)] → Vec(3) * Vec(5)

Ruby yondashuvi eleganter: bitta coerce metodi barcha amallar uchun. Lekin u har doim ham mos kelmaydi (masalan, Matritsa * skalyar da o'girish ma'nosiz).

Rust — trait:

rust
impl Mul<Vec> for i32 {         // ⭐ i32 uchun implementatsiya
    type Output = Vec;
    fn mul(self, rhs: Vec) -> Vec { ... }
}

Rust da i32 uchun ham implementatsiya yozish mumkin — chunki trait tizimi buni ruxsat etadi (orphan rule cheklovlari bilan).

10. Xulosa

NotImplemented kerak, chunki:

  1. O'rnatilgan turlarni o'zgartirib bo'lmaydi — int.__mul__ sizning sinfingizni bilmaydi
  2. Ikkala operand ham urinishi kerak — istisno birinchisida to'xtatardi
  3. Maxsus qiymat kerak — None haqiqiy natija bo'lishi mumkin
  4. Xato xabari to'liq bo'lsin — Python ikkalasi rad etgandan keyin aniq TypeError beradi:
python
Vektor(1,2) + "matn"
# TypeError: unsupported operand type(s) for +: 'Vektor' and 'str'
#                                                ⭐ ikkala tur ham

Kompromis: Python C++ ning moslashuvchanligini (erkin operator funksiyalari) tanlamadi — chunki bu nom fazosini murakkablashtiradi. O'rniga u oddiy va bashoratli muzokara protokolini tanladi: har tur o'z metodlarida, NotImplemented — "keyingisiga o't" signali.

Nimani mustahkamlaydi: 2.2, 2.7-bo'limlar.


Xulosa

Bu darsda arifmetika dunder metodlarini o'rgandik.

Eng muhim uch fikr:

  1. Har amal uchun uch metod: __op__, __rop__, __iop__. Teskari versiya (__radd__, __rmul__) majburiy — chunki int va boshqa o'rnatilgan turlar sizning sinfingizni bilmaydi va NotImplemented qaytaradi. 3 * v faqat __rmul__ bilan ishlaydi.

  2. NotImplemented — muzokara signali, istisno emas. a + b da avval a.__add__(b), u NotImplemented qaytarsa b.__radd__(a), ikkalasi ham rad etsa — TypeError. TypeError ni o'zingiz tashlasangiz, teskari metod hech qachon sinalmaydi.

  3. += — __iadd__, u yo'q bo'lsa __add__. __iadd__ obyektni joyida o'zgartiradi va self qaytarishi shart (unutish — None bilan jimgina xato). Aynan shu sabab list da lst += [x] havolalarga ta'sir qiladi, tuple da esa yo'q. __index__ ham alohida e'tibor talab qiladi: __int__ indekslash uchun yetarli emas.

Keyingi darsda konteyner protokolini ko'ramiz: __len__, __getitem__, __iter__, __contains__ va o'z konteyner turlaringizni yaratish.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
8.13-dars: Dunder: arifmetika — IlmHamroh