Mundarija (19)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Joyida amallar amalda
- 2.2. NotImplemented yoki TypeError
- 2.3. Pul turini loyihalash
- 2.4. Algebraik qonunlar
- 2.5. Operatorlar DSL sifatida
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Joyida amallar, taxalluslar va rad etish
- Misol 2 — Pul turi to'liq
- Misol 3 — Qonunlarni avtomatik tekshirish
- Misol 4 — Amaliy: so'rov quruvchi DSL
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
10.14-dars: Operator ortiqcha yuklash amaliyoti
10-QISM — ILG'OR TIL IMKONIYATLARI · 14-dars
1. Kirish va motivatsiya
8.12 va 8.13-darslarda operatorlar qanday hal qilinishini o'rgandik: __add__ va __radd__, NotImplemented, merosxo'r ustuvorligi, __iadd__, sum() bilan ishlash. Endi ular bilan real tur loyihalash vaqti.
Jamoa ma'lumotlar bazasi uchun kichik so'rov quruvchi yozdi, pandas va SQLAlchemy uslubida:
kattalar = Foydalanuvchi.yosh >= 18 & Foydalanuvchi.faol == TrueNatija:
TypeError: Taqqoslash'ni bool ga aylantirib bo'lmaydi — and/or/not o'rniga & | ~ ishlating va & | atrofiga qavs qo'yingKod "to'g'ri" ko'rinadi, lekin Python uni boshqacha o'qiydi. & ning ustuvorligi >= va == dan yuqori, shuning uchun bu:
Foydalanuvchi.yosh >= (18 & Foydalanuvchi.faol) == True— ya'ni zanjirli taqqoslash a >= b == c, u esa (a >= b) and (b == c) ga ochiladi. and esa bool() chaqiradi.
Boshqa jamoa pul turini yozdi. Pul("0.10") + Pul("0.20") + Pul("0.30") bir tartibda 0.60, boshqa tartibda 0.6000000000000001 berdi — ichida float ishlatilgan edi. Uchinchi jamoaning Vektor turi += dan keyin boshqa o'zgaruvchidagi vektorni ham "sehrli" o'zgartirib yubordi.
Operatorni ortiqcha yuklash — sintaksis emas, va'da. + qo'shgan tur foydalanuvchiga "bu qo'shishga o'xshaydi" deb aytadi: o'rin almashtirish, guruhlash, neytral element, == va hash mosligi. Va'da buzilsa, xatolar operator yozilgan joyda emas, uzoqda — sum(), sorted(), lug'at kalitlari yoki parallel hisobda chiqadi.
Bu darsda:
- Joyida amallar: taxalluslar (alias),
NotImplementeddan qaytish, kortej tuzog'i NotImplementedvaTypeErrorfarqi amalda- Pul turi to'liq:
Decimal, valyuta, ruxsat etilgan kombinatsiyalar, taqsimlash, format - Algebraik qonunlar va ularni avtomatik tekshirish
- Operatorlar DSL sifatida: ifoda daraxti,
and/oro'rniga&/|, ustuvorlik va__bool__tuzoqlari
2. Nazariya — chuqur tushuntirish
2.1. Joyida amallar amalda
a += b taxminan:
natija = a.__iadd__(b) if hasattr(type(a), "__iadd__") else NotImplemented
if natija is NotImplemented:
natija = a + b # __add__ / __radd__
a = natija # ⭐ qayta bog'lash — HAR DOIM| Tur | __iadd__ |
b = a; a += x dan keyin |
|---|---|---|
O'zgarmas (int, str, tuple, o'zgarmas Pul) |
Yo'q → yangi obyekt | a is b → False, b o'zgarmadi |
O'zgaruvchan (list, set, Counter) |
Joyida o'zgartirib, self qaytaradi |
a is b → True, b ham o'zgardi |
Loyihalash qoidasi:
- Qiymat turlari (pul, vektor, sana, versiya) — o'zgarmas,
__iadd__yozmang - Konteyner/akkumulyator turlari (to'plam, hisob-kitob daftari) —
__iadd__joyida o'zgartirsin vaselfqaytarsin __iadd__danNotImplementedqaytsa, Python__add__ga o'tadi — natija boshqa turdagi yangi obyekt bo'lishi mumkin
Kortej tuzog'i:
t = ([1, 2],)
t[0] += [3] # TypeError: 'tuple' object does not support item assignment
t # ([1, 2, 3],) — lekin ro'yxat baribir o'zgardi!Chunki t[0] += [3] — bu t[0] = t[0].__iadd__([3]). __iadd__ ro'yxatni joyida o'zgartiradi va muvaffaqiyatli qaytadi, keyin kortejga qayta yozish xato beradi.
2.2. NotImplemented yoki TypeError
| Operator metodida | Nima bo'ladi |
|---|---|
return NotImplemented |
Python ikkinchi operandning teskari metodini sinaydi; u ham rad etsa — standart TypeError |
raise TypeError(...) |
Hammasi to'xtaydi — ikkinchi operand imkoniyat olmaydi |
"Men bu turni bilmayman" → NotImplemented. Boshqa tur (masalan, keyinroq yozilgan Soliq klassi) sizning turingiz bilan qanday ishlashni bilishi mumkin.
"Men bu turni bilaman, lekin bu amal ma'nosiz" → TypeError aniq xabar bilan: Pul("5 USD") + Pul("5 UZS") — ikkala tur ham Pul, boshqa hech kim yordam bera olmaydi, xabar esa foydali bo'lishi kerak.
2.3. Pul turini loyihalash
1. Ichki tasvir. float — yo'q: 0.1 + 0.2 != 0.3. Variantlar:
| Tasvir | Afzallik | Kamchilik |
|---|---|---|
Decimal |
O'nlik aniqlik, yumaloqlash rejimlari | Sekinroq |
int — eng kichik birlikda (tiyin, sent) |
Tez, aniq | Valyutaga qarab birlik har xil (JPY da 0, BHD da 3 xona) |
2. Qaysi amallar ma'noli:
| Amal | Natija | Izoh |
|---|---|---|
Pul + Pul (bir valyuta) |
Pul |
|
Pul + Pul (boshqa valyuta) |
TypeError |
Kursni aniq ko'rsatish kerak |
Pul + 5 |
Birliksiz son — xato manbai. Faqat 0 (sum uchun) |
|
Pul * 3, 3 * Pul, Pul * Decimal |
Pul |
|
Pul * float |
Aniqlik yo'qoladi | |
Pul * Pul |
"so'm²" — ma'nosiz | |
Pul / 3 |
Qoldiq yo'qoladi — ajrat(3) yaxshiroq |
|
Pul / Pul |
Decimal |
Nisbat — birliksiz |
-Pul, abs(Pul), bool(Pul) |
||
Pul < Pul (bir valyuta) |
bool |
Boshqa valyutada — TypeError |
3. Taqsimlash. 100 so'm / 3 → 33.33 × 3 = 99.99 — 1 tiyin yo'qoladi. To'g'ri usul — ajratish: qoldiqni birma-bir taqsimlash ([33.34, 33.33, 33.33]), yig'indi har doim asl summaga teng.
4. Tenglik va xesh. Pul("1.0") == Pul("1.00") bo'lsa, hash ham teng bo'lishi shart. Ichki qiymatni normallashtiring (quantize) — keyin __eq__ va __hash__ bir xil maydonlardan hisoblanadi.
5. Format. __format__(spec) — f"{narx:,}", f"{narx:kod}" kabi o'z format tilingizni berish mumkin.
2.4. Algebraik qonunlar
Operator foydalanuvchiga quyidagi xossalarni va'da qiladi:
| Qonun | Formula | Qayerda kerak |
|---|---|---|
| O'rin almashtirish | a + b == b + a |
sum() tartibi, sorted + sum |
| Guruhlash (assotsiativlik) | (a + b) + c == a + (b + c) |
Parallel yig'ish (10.13-dars), reduce |
| Neytral element | a + 0 == a |
sum(..., start), bo'sh ro'yxat |
| Teskari element | a + (-a) == 0, a - b == a + (-b) |
Balans hisobi |
| Taqsimlash | k * (a + b) == k*a + k*b |
Soliq, chegirma |
| Tenglik–xesh | a == b ⇒ hash(a) == hash(b) |
Lug'at, to'plam |
| Tartib to'liqligi | a < b, a == b, a > b — aniq bittasi |
sorted, bisect |
Qonunlarni avtomatik tekshirish — "xossaga asoslangan test": tasodifiy qiymatlar yaratiladi va formulalar minglab marta tekshiriladi. Uchinchi tomon hypothesis kutubxonasi buni professional darajada qiladi; oddiy variantini random bilan yozish mumkin (Misol 3).
Hamma turlar hamma qonunni bajarmaydi — va bu normal, lekin hujjatlashtirilishi kerak:
float: guruhlash va taqsimlash yaqinlashuv bilanstr,list+: o'rin almashtirish yo'q ("a" + "b" != "b" + "a")- Matritsa
@: o'rin almashtirish yo'q
2.5. Operatorlar DSL sifatida
Operator metodlari istalgan obyekt qaytarishi mumkin. Shu tufayli kutubxonalar kichik "tillar" quradi:
| Kutubxona | Ifoda | Qaytaradi |
|---|---|---|
pathlib |
Path("a") / "b" |
Yangi yo'l |
set |
a | b, a & b |
To'plam |
| pandas, NumPy | df["yosh"] >= 18 |
Mantiqiy massiv, bool emas |
| SQLAlchemy | User.yosh >= 18 |
SQL ifoda obyekti |
enum.Flag |
Ruxsat.OQISH | Ruxsat.YOZISH |
Bayroqlar birikmasi |
Cheklovlar va tuzoqlar:
1. and, or, not, is, in (natija turi) ni yuklab bo'lmaydi. and/or operandni bool() ga aylantiradi va qisqa tutashuv qiladi. Shuning uchun DSL lar &, |, ~ ishlatadi.
2. Ustuvorlik o'zgarmaydi. &, |, ^ taqqoslashlardan yuqori:
yosh >= 18 & faol == True # yosh >= (18 & faol) == True ❌
(yosh >= 18) & (faol == True) # ✅3. Zanjirli taqqoslash bool() chaqiradi. a < b < c → (a < b) and (b < c).
4. __eq__ ifoda qaytarsa:
x in royxat,royxat.index(x),royxat.remove(x)— ichida==vabool()→ xato yoki noto'g'ri natijaassert ifoda1 == ifoda2testlarda — ma'nosiz__hash__avtomatikNonebo'ladi (8.12-dars) — lug'at kaliti bo'lolmaydi; kerak bo'lsa aniq__hash__ = object.__hash__
5. __bool__ ni taqiqlang. Ifoda obyekti bool() da TypeError bersin — NumPy va pandas shunday qiladi ("The truth value of an array is ambiguous"). Aks holda if ifoda: jim True bo'lib, xato yashirinadi.
3. Tez ma'lumotnoma
Joyida amallar
a += b → a = a.__iadd__(b) (NotImplemented bo'lsa → a = a + b)
o'zgarmas tur: __iadd__ yozmang o'zgaruvchan: joyida + return self
t[0] += [x] → ro'yxat o'zgaradi VA TypeErrorRad etish
return NotImplemented # turni bilmayman — boshqasi urinib ko'rsin
raise TypeError("...") # bilaman, lekin ma'nosiz — aniq xabarPul turi
Decimal yoki int (tiyin) — float emas
Pul + Pul (bir valyuta) ✅ Pul + son ❌ (0 bundan mustasno)
Pul * int/Decimal ✅ Pul * float ❌ Pul * Pul ❌
Pul / Pul → Decimal Pul / n → ajrat(n)
__eq__ va __hash__ — normallashtirilgan qiymatdanQonunlar
a+b == b+a (a+b)+c == a+(b+c) a+0 == a a-b == a+(-b)
k*(a+b) == k*a+k*b a == b ⇒ hash(a) == hash(b)DSL
and/or/not — yuklanmaydi → & | ~
& | ^ ustuvorligi taqqoslashdan yuqori → qavs
a < b < c → bool() chaqiriladi
__bool__ → TypeError __hash__ = object.__hash__ (kerak bo'lsa)4. Batafsil misollar
Misol 1 — Joyida amallar, taxalluslar va rad etish
"""+= o'zgarmas va o'zgaruvchan turlarda; __iadd__ dan NotImplemented; kortej tuzog'i; NotImplemented vs TypeError."""
class Vektor:
"""O'zgarmas qiymat turi."""
__slots__ = ("x", "y")
def __init__(self, x, y):
object.__setattr__(self, "x", x)
object.__setattr__(self, "y", y)
def __setattr__(self, nom, qiymat):
raise AttributeError("Vektor o'zgarmas")
def __add__(self, boshqa):
if not isinstance(boshqa, Vektor):
return NotImplemented
return Vektor(self.x + boshqa.x, self.y + boshqa.y)
def __repr__(self):
return f"Vektor({self.x}, {self.y})"
class Savatcha:
"""O'zgaruvchan akkumulyator."""
def __init__(self, mahsulotlar=()):
self.mahsulotlar = list(mahsulotlar)
def __add__(self, boshqa):
if not isinstance(boshqa, Savatcha):
return NotImplemented
return Savatcha(self.mahsulotlar + boshqa.mahsulotlar)
def __iadd__(self, boshqa):
if isinstance(boshqa, Savatcha):
self.mahsulotlar.extend(boshqa.mahsulotlar)
return self
if isinstance(boshqa, str):
self.mahsulotlar.append(boshqa)
return self
return NotImplemented
def __repr__(self):
return f"Savatcha({self.mahsulotlar})"
print("=== 1. Taxalluslar: o'zgarmas va o'zgaruvchan ===")
a = Vektor(1, 2)
b = a
a += Vektor(10, 10)
print(f" Vektor: a is b: {a is b}, a={a}, b={b}")
s = Savatcha(["non"])
nusxa = s
s += "sut"
s += Savatcha(["tuxum"])
print(f" Savatcha: s is nusxa: {s is nusxa}, nusxa={nusxa} ⚠️ taxallus ham o'zgardi")
yangi = s + Savatcha(["choy"])
print(f" s + ... o'sha obyektmi: {yangi is s}, s o'zgarmadi: {s}")
print("\n=== 2. __iadd__ NotImplemented qaytarsa — __add__ ga o'tiladi ===")
class Jurnal(list):
def __iadd__(self, boshqa):
if not isinstance(boshqa, str):
print(f" Jurnal.__iadd__({boshqa!r}) → NotImplemented")
return NotImplemented
self.append(boshqa)
return self
j = Jurnal(["boshlandi"])
eski = j
j += "xato"
print(f" j += 'xato' → {j}, turi {type(j).__name__}, o'sha obyekt: {j is eski}")
j += ["a", "b"]
print(f" j += [...] → {j}, turi {type(j).__name__}, o'sha obyekt: {j is eski} ⚠️ list.__add__ yangi list berdi")
print("\n=== 3. ⚠️ Kortej ichidagi ro'yxat ===")
t = (["a"], "b")
try:
t[0] += ["c"]
except TypeError as e:
print(f" TypeError: {e}")
print(f" t: {t} ⚠️ xato bo'ldi, lekin ro'yxat o'zgardi")
t2 = (["a"], "b")
t2[0].extend(["c"])
print(f" ✅ extend bilan — xatosiz: {t2}")
print("\n=== 4. NotImplemented yoki TypeError ===")
class Metr:
def __init__(self, qiymat):
self.qiymat = qiymat
def __add__(self, boshqa):
if isinstance(boshqa, Metr):
return Metr(self.qiymat + boshqa.qiymat)
return NotImplemented
def __repr__(self):
return f"{self.qiymat} m"
class QattiqMetr(Metr):
def __add__(self, boshqa):
if isinstance(boshqa, Metr):
return QattiqMetr(self.qiymat + boshqa.qiymat)
raise TypeError(f"Metr ga {type(boshqa).__name__} qo'shib bo'lmaydi")
class Santimetr:
def __init__(self, qiymat):
self.qiymat = qiymat
def __radd__(self, boshqa):
if isinstance(boshqa, Metr):
return type(boshqa)(boshqa.qiymat + self.qiymat / 100)
return NotImplemented
for chap in (Metr(2), QattiqMetr(2)):
try:
print(f" {type(chap).__name__}(2) + Santimetr(50) = {chap + Santimetr(50)}")
except TypeError as e:
print(f" {type(chap).__name__}(2) + Santimetr(50) → TypeError: {e} ⚠️ Santimetr.__radd__ imkoniyat olmadi")
try:
Metr(2) + "uch"
except TypeError as e:
print(f" Metr(2) + 'uch' → standart TypeError: {e}")Natijaning muhim qismi:
=== 1. Taxalluslar: o'zgarmas va o'zgaruvchan ===
Vektor: a is b: False, a=Vektor(11, 12), b=Vektor(1, 2)
Savatcha: s is nusxa: True, nusxa=Savatcha(['non', 'sut', 'tuxum']) ⚠️ taxallus ham o'zgardi
s + ... o'sha obyektmi: False, s o'zgarmadi: Savatcha(['non', 'sut', 'tuxum'])
=== 2. __iadd__ NotImplemented qaytarsa — __add__ ga o'tiladi ===
j += 'xato' → ['boshlandi', 'xato'], turi Jurnal, o'sha obyekt: True
Jurnal.__iadd__(['a', 'b']) → NotImplemented
j += [...] → ['boshlandi', 'xato', 'a', 'b'], turi list, o'sha obyekt: False ⚠️ list.__add__ yangi list berdi
=== 3. ⚠️ Kortej ichidagi ro'yxat ===
TypeError: 'tuple' object does not support item assignment
t: (['a', 'c'], 'b') ⚠️ xato bo'ldi, lekin ro'yxat o'zgardi
✅ extend bilan — xatosiz: (['a', 'c'], 'b')
=== 4. NotImplemented yoki TypeError ===
Metr(2) + Santimetr(50) = 2.5 m
QattiqMetr(2) + Santimetr(50) → TypeError: Metr ga Santimetr qo'shib bo'lmaydi ⚠️ Santimetr.__radd__ imkoniyat olmadi
Metr(2) + 'uch' → standart TypeError: unsupported operand type(s) for +: 'Metr' and 'str'Nima ko'rsatdi: 2.1, 2.2-bo'limlar.
Misol 2 — Pul turi to'liq
"""Decimal asosidagi Pul: valyuta, ruxsat etilgan amallar jadvali, ajratish, tenglik/xesh, format, sum."""
from decimal import ROUND_HALF_EVEN, Decimal
from functools import total_ordering
XONALAR = {"UZS": 2, "USD": 2, "JPY": 0}
@total_ordering
class Pul:
__slots__ = ("_summa", "valyuta")
def __init__(self, summa, valyuta="UZS"):
if isinstance(summa, float):
raise TypeError("Pul float qabul qilmaydi — satr yoki Decimal bering")
if valyuta not in XONALAR:
raise ValueError(f"noma'lum valyuta: {valyuta}")
birlik = Decimal(1).scaleb(-XONALAR[valyuta])
object.__setattr__(self, "_summa", Decimal(summa).quantize(birlik, ROUND_HALF_EVEN))
object.__setattr__(self, "valyuta", valyuta)
def __setattr__(self, nom, qiymat):
raise AttributeError("Pul o'zgarmas")
@property
def summa(self):
return self._summa
def _bir_xil(self, boshqa, amal):
if boshqa.valyuta != self.valyuta:
raise TypeError(f"{self.valyuta} va {boshqa.valyuta} ni {amal} uchun avval konvertatsiya qiling")
def __add__(self, boshqa):
if isinstance(boshqa, Pul):
self._bir_xil(boshqa, "qo'shish")
return Pul(self._summa + boshqa._summa, self.valyuta)
if boshqa == 0 and type(boshqa) is int: # sum() uchun
return self
return NotImplemented
__radd__ = __add__
def __sub__(self, boshqa):
if isinstance(boshqa, Pul):
self._bir_xil(boshqa, "ayirish")
return Pul(self._summa - boshqa._summa, self.valyuta)
return NotImplemented
def __neg__(self):
return Pul(-self._summa, self.valyuta)
def __abs__(self):
return Pul(abs(self._summa), self.valyuta)
def __bool__(self):
return bool(self._summa)
def __mul__(self, k):
if type(k) in (int, Decimal):
return Pul(self._summa * k, self.valyuta)
return NotImplemented
__rmul__ = __mul__
def __truediv__(self, boshqa):
if isinstance(boshqa, Pul):
self._bir_xil(boshqa, "nisbat")
return self._summa / boshqa._summa
if type(boshqa) is int:
raise TypeError(f"Pul / int qoldiqni yo'qotadi — ajrat({boshqa}) ishlating")
return NotImplemented
def ajrat(self, *ulushlar):
"""Ulushlarga (masalan 1, 1, 1 yoki 70, 30) qoldiqsiz taqsimlaydi."""
if len(ulushlar) == 1 and type(ulushlar[0]) is int:
ulushlar = (1,) * ulushlar[0]
birlik = Decimal(1).scaleb(-XONALAR[self.valyuta])
jami_birlik = int(self._summa / birlik)
jami_ulush = sum(ulushlar)
qismlar = [jami_birlik * u // jami_ulush for u in ulushlar]
for i in range(jami_birlik - sum(qismlar)):
qismlar[i] += 1
return [Pul(q * birlik, self.valyuta) for q in qismlar]
def __eq__(self, boshqa):
if not isinstance(boshqa, Pul):
return NotImplemented
return (self._summa, self.valyuta) == (boshqa._summa, boshqa.valyuta)
def __lt__(self, boshqa):
if not isinstance(boshqa, Pul):
return NotImplemented
self._bir_xil(boshqa, "taqqoslash")
return self._summa < boshqa._summa
def __hash__(self):
return hash((self._summa, self.valyuta))
def __format__(self, spec):
if spec == "kod":
return f"{self._summa} {self.valyuta}"
son = format(self._summa, spec or ",")
return f"{son.replace(',', ' ')} {self.valyuta}"
def __repr__(self):
return f"Pul('{self._summa}', '{self.valyuta}')"
print("=== 1. Aniqlik: float va Pul ===")
f = [0.10, 0.20, 0.30]
print(f" float: (0.1+0.2)+0.3 = {(f[0] + f[1]) + f[2]!r}, 0.1+(0.2+0.3) = {f[0] + (f[1] + f[2])!r}")
p = [Pul("0.10"), Pul("0.20"), Pul("0.30")]
print(f" Pul: (a+b)+c = {(p[0] + p[1]) + p[2]!r}, a+(b+c) = {p[0] + (p[1] + p[2])!r}")
try:
Pul(0.1)
except TypeError as e:
print(f" Pul(0.1) → TypeError: {e}")
print("\n=== 2. Ruxsat etilgan amallar jadvali ===")
m, d = Pul("1500.50"), Pul("20", "USD")
amallar = [
("Pul + Pul", lambda: m + Pul("499.50")),
("Pul + Pul(USD)", lambda: m + d),
("Pul + 100", lambda: m + 100),
("sum([Pul, Pul])", lambda: sum([m, m])),
("Pul * 3", lambda: m * 3),
("3 * Pul", lambda: 3 * m),
("Pul * Decimal('0.12')", lambda: m * Decimal("0.12")),
("Pul * 1.5", lambda: m * 1.5),
("Pul * Pul", lambda: m * m),
("Pul / Pul", lambda: m / Pul("500")),
("Pul / 3", lambda: m / 3),
("-Pul, abs", lambda: (-m, abs(-m))),
("Pul < Pul(USD)", lambda: m < d),
]
for tavsif, amal in amallar:
try:
print(f" {tavsif:24} ✅ {amal()!r}")
except TypeError as e:
print(f" {tavsif:24} ❌ {e}")
print("\n=== 3. Ajratish: qoldiq yo'qolmaydi ===")
for summa, ulushlar in [(Pul("100"), (3,)), (Pul("0.05"), (1, 1)), (Pul("1000", "JPY"), (70, 20, 10)),
(Pul("250000.01"), (1, 1, 1, 1))]:
qismlar = summa.ajrat(*ulushlar)
print(f" {summa:kod} ÷ {ulushlar} → {[format(q, 'kod') for q in qismlar]}, yig'indi to'g'ri: {sum(qismlar) == summa}")
print("\n=== 4. Tenglik, xesh va format ===")
a, b = Pul("1.0"), Pul("1.00")
print(f" Pul('1.0') == Pul('1.00'): {a == b}, hash teng: {hash(a) == hash(b)}, to'plamda: {len({a, b})}")
print(f" Pul('5', 'UZS') == Pul('5', 'USD'): {Pul('5') == Pul('5', 'USD')}")
narx = Pul("1234567.891")
print(f" f'{{narx}}' → {narx} f'{{narx:kod}}' → {narx:kod} f'{{narx:,.0f}}' → {narx:,.0f}")
print(f" saralash: {[format(x, 'kod') for x in sorted([Pul('30'), Pul('5.5'), Pul('100')])]}, bool(Pul(0)): {bool(Pul(0))}")Natijaning muhim qismi:
=== 1. Aniqlik: float va Pul ===
float: (0.1+0.2)+0.3 = 0.6000000000000001, 0.1+(0.2+0.3) = 0.6
Pul: (a+b)+c = Pul('0.60', 'UZS'), a+(b+c) = Pul('0.60', 'UZS')
Pul(0.1) → TypeError: Pul float qabul qilmaydi — satr yoki Decimal bering
=== 2. Ruxsat etilgan amallar jadvali ===
Pul + Pul ✅ Pul('2000.00', 'UZS')
Pul + Pul(USD) ❌ UZS va USD ni qo'shish uchun avval konvertatsiya qiling
Pul + 100 ❌ unsupported operand type(s) for +: 'Pul' and 'int'
sum([Pul, Pul]) ✅ Pul('3001.00', 'UZS')
Pul * 3 ✅ Pul('4501.50', 'UZS')
3 * Pul ✅ Pul('4501.50', 'UZS')
Pul * Decimal('0.12') ✅ Pul('180.06', 'UZS')
Pul * 1.5 ❌ unsupported operand type(s) for *: 'Pul' and 'float'
Pul * Pul ❌ unsupported operand type(s) for *: 'Pul' and 'Pul'
Pul / Pul ✅ Decimal('3.001')
Pul / 3 ❌ Pul / int qoldiqni yo'qotadi — ajrat(3) ishlating
-Pul, abs ✅ (Pul('-1500.50', 'UZS'), Pul('1500.50', 'UZS'))
Pul < Pul(USD) ❌ UZS va USD ni taqqoslash uchun avval konvertatsiya qiling
=== 3. Ajratish: qoldiq yo'qolmaydi ===
100.00 UZS ÷ (3,) → ['33.34 UZS', '33.33 UZS', '33.33 UZS'], yig'indi to'g'ri: True
0.05 UZS ÷ (1, 1) → ['0.03 UZS', '0.02 UZS'], yig'indi to'g'ri: True
1000 JPY ÷ (70, 20, 10) → ['700 JPY', '200 JPY', '100 JPY'], yig'indi to'g'ri: True
250000.01 UZS ÷ (1, 1, 1, 1) → ['62500.01 UZS', '62500.00 UZS', '62500.00 UZS', '62500.00 UZS'], yig'indi to'g'ri: True
=== 4. Tenglik, xesh va format ===
Pul('1.0') == Pul('1.00'): True, hash teng: True, to'plamda: 1
Pul('5', 'UZS') == Pul('5', 'USD'): False
f'{narx}' → 1 234 567.89 UZS f'{narx:kod}' → 1234567.89 UZS f'{narx:,.0f}' → 1 234 568 UZS
saralash: ['5.50 UZS', '30.00 UZS', '100.00 UZS'], bool(Pul(0)): FalseNima ko'rsatdi: 2.2, 2.3-bo'limlar.
Misol 3 — Qonunlarni avtomatik tekshirish
"""Tasodifiy qiymatlar bilan algebraik qonunlar: float, Fraction, Decimal, str va o'zimizning turlar."""
import math
import random
from decimal import Decimal, getcontext
from fractions import Fraction
def qonunlar(qosh, neytral, teskari=None, kopaytir=None):
q = {
"o'rin almashtirish": lambda a, b, c: qosh(a, b) == qosh(b, a),
"guruhlash": lambda a, b, c: qosh(qosh(a, b), c) == qosh(a, qosh(b, c)),
"neytral element": lambda a, b, c: qosh(a, neytral) == a,
}
if teskari:
q["teskari element"] = lambda a, b, c: qosh(a, teskari(a)) == neytral
if kopaytir:
q["taqsimlash"] = lambda a, b, c: kopaytir(3, qosh(a, b)) == qosh(kopaytir(3, a), kopaytir(3, b))
q["tenglik ⇒ xesh"] = lambda a, b, c: (a != b) or hash(a) == hash(b)
return q
def tekshir(nom, yaratuvchi, qonun_royxati, sinovlar=2000, urug=42):
rng = random.Random(urug)
namunalar = [(yaratuvchi(rng), yaratuvchi(rng), yaratuvchi(rng)) for _ in range(sinovlar)]
natijalar = []
for qonun, shart in qonun_royxati.items():
buzilgan = [n for n in namunalar if not shart(*n)]
belgi = "✅" if not buzilgan else f"❌ {len(buzilgan)}/{sinovlar}"
natijalar.append(f"{qonun}: {belgi}")
print(f" {nom}")
for n in natijalar:
print(f" {n}")
return namunalar
print("=== 1. Sonli turlar ===")
getcontext().prec = 28
namuna = tekshir("float", lambda r: round(r.uniform(-100, 100), 2),
qonunlar(lambda a, b: a + b, 0.0, lambda a: -a, lambda k, a: k * a))
tekshir("Fraction", lambda r: Fraction(r.randint(-10_000, 10_000), 100),
qonunlar(lambda a, b: a + b, Fraction(0), lambda a: -a, lambda k, a: k * a))
tekshir("Decimal (2 xona)", lambda r: Decimal(r.randint(-10_000, 10_000)).scaleb(-2),
qonunlar(lambda a, b: a + b, Decimal(0), lambda a: -a, lambda k, a: k * a))
a, b, c = next((a, b, c) for a, b, c in namuna if (a + b) + c != a + (b + c))
print(f" float guruhlash buzilishi misoli: ({a} + {b}) + {c} = {(a + b) + c!r}, {a} + ({b} + {c}) = {a + (b + c)!r}")
print(f" ✅ float uchun to'g'ri taqqoslash: math.isclose → {math.isclose((a + b) + c, a + (b + c))}")
print("\n=== 2. str: + qo'shish emas, ulash ===")
tekshir("str +", lambda r: r.choice("abc") * r.randint(0, 2), qonunlar(lambda a, b: a + b, ""))
print("\n=== 3. O'zimizning turlar: xatolarni qonunlar topadi ===")
class YomonNuqta:
"""eq koordinatalardan, hash esa id dan — va qo'shish yumaloqlaydi."""
def __init__(self, x):
self.x = x
def __add__(self, boshqa):
return YomonNuqta(round((self.x + boshqa.x) * 10) / 10)
def __neg__(self):
return YomonNuqta(-self.x)
def __eq__(self, boshqa):
return isinstance(boshqa, YomonNuqta) and self.x == boshqa.x
__hash__ = object.__hash__
class Nuqta:
__slots__ = ("x",)
def __init__(self, x):
self.x = Fraction(x)
def __add__(self, boshqa):
return Nuqta(self.x + boshqa.x)
def __neg__(self):
return Nuqta(-self.x)
def __rmul__(self, k):
return Nuqta(k * self.x)
def __eq__(self, boshqa):
return isinstance(boshqa, Nuqta) and self.x == boshqa.x
def __hash__(self):
return hash(self.x)
tekshir("YomonNuqta", lambda r: YomonNuqta(r.randint(-100, 100) / 7),
qonunlar(lambda a, b: a + b, YomonNuqta(0), lambda a: -a))
tekshir("Nuqta (Fraction)", lambda r: Nuqta(Fraction(r.randint(-100, 100), 7)),
qonunlar(lambda a, b: a + b, Nuqta(0), lambda a: -a, lambda k, a: k * a))Natijaning muhim qismi:
=== 1. Sonli turlar ===
float
o'rin almashtirish: ✅
guruhlash: ❌ 645/2000
neytral element: ✅
teskari element: ✅
taqsimlash: ❌ 853/2000
tenglik ⇒ xesh: ✅
Fraction
o'rin almashtirish: ✅
guruhlash: ✅
neytral element: ✅
teskari element: ✅
taqsimlash: ✅
tenglik ⇒ xesh: ✅
Decimal (2 xona)
o'rin almashtirish: ✅
guruhlash: ✅
neytral element: ✅
teskari element: ✅
taqsimlash: ✅
tenglik ⇒ xesh: ✅
float guruhlash buzilishi misoli: (27.89 + -95.0) + -44.99 = -112.1, 27.89 + (-95.0 + -44.99) = -112.10000000000001
✅ float uchun to'g'ri taqqoslash: math.isclose → True
=== 2. str: + qo'shish emas, ulash ===
str +
o'rin almashtirish: ❌ 591/2000
guruhlash: ✅
neytral element: ✅
tenglik ⇒ xesh: ✅
=== 3. O'zimizning turlar: xatolarni qonunlar topadi ===
YomonNuqta
o'rin almashtirish: ✅
guruhlash: ❌ 631/2000
neytral element: ❌ 1697/2000
teskari element: ✅
tenglik ⇒ xesh: ❌ 12/2000
Nuqta (Fraction)
o'rin almashtirish: ✅
guruhlash: ✅
neytral element: ✅
teskari element: ✅
taqsimlash: ✅
tenglik ⇒ xesh: ✅Nima ko'rsatdi: 2.4-bo'lim — float guruhlash va taqsimlashni yaqinlashuv bilan bajaradi, str o'rin almashtirmaydi, YomonNuqta dagi yumaloqlash va id asosidagi xesh esa bir necha qator test yozmasdan aniqlandi.
Misol 4 — Amaliy: so'rov quruvchi DSL
Kichik so'rov quruvchi: Ustun ustidagi operatorlar Ifoda daraxtini quradi, u SQL ga (parametrlar bilan) va Python filtrga kompilyatsiya qilinadi. Ustuvorlik, and/or, in va __bool__ tuzoqlari bilan.
"""Operatorlar ifoda daraxtini quradi; & | ~; __bool__ taqiqlangan; ustuvorlik va in tuzoqlari; SQL va Python ga kompilyatsiya."""
import operator
class Ifoda:
__hash__ = object.__hash__ # __eq__ ni yuklaganimiz uchun aniq
def __and__(self, boshqa):
return Mantiq("AND", self, _ifoda(boshqa))
def __or__(self, boshqa):
return Mantiq("OR", self, _ifoda(boshqa))
def __rand__(self, boshqa):
return Mantiq("AND", _ifoda(boshqa), self)
def __ror__(self, boshqa):
return Mantiq("OR", _ifoda(boshqa), self)
def __invert__(self):
return Inkor(self)
def __bool__(self):
raise TypeError(
f"{type(self).__name__}'ni bool ga aylantirib bo'lmaydi — "
"and/or/not o'rniga & | ~ ishlating va & | atrofiga qavs qo'ying"
)
def _taqqos(belgi, amal):
def metod(self, boshqa):
return Taqqoslash(belgi, amal, self, _ifoda(boshqa))
return metod
__eq__ = _taqqos("=", operator.eq)
__ne__ = _taqqos("<>", operator.ne)
__lt__ = _taqqos("<", operator.lt)
__le__ = _taqqos("<=", operator.le)
__gt__ = _taqqos(">", operator.gt)
__ge__ = _taqqos(">=", operator.ge)
del _taqqos
def ichida(self, qiymatlar):
return Ichida(self, tuple(qiymatlar))
class Ustun(Ifoda):
def __init__(self, nom):
self.nom = nom
def sql(self, p):
return self.nom
def py(self, qator):
return qator[self.nom]
def __repr__(self):
return self.nom
class Qiymat(Ifoda):
def __init__(self, q):
self.q = q
def sql(self, p):
p.append(self.q)
return "?"
def py(self, qator):
return self.q
def __repr__(self):
return repr(self.q)
def _ifoda(x):
return x if isinstance(x, Ifoda) else Qiymat(x)
class Taqqoslash(Ifoda):
def __init__(self, belgi, amal, chap, ong):
self.belgi, self.amal, self.chap, self.ong = belgi, amal, chap, ong
def sql(self, p):
return f"{self.chap.sql(p)} {self.belgi} {self.ong.sql(p)}"
def py(self, qator):
return self.amal(self.chap.py(qator), self.ong.py(qator))
def __repr__(self):
return f"({self.chap!r} {self.belgi} {self.ong!r})"
class Mantiq(Ifoda):
def __init__(self, belgi, chap, ong):
self.belgi, self.chap, self.ong = belgi, chap, ong
def sql(self, p):
return f"({self.chap.sql(p)} {self.belgi} {self.ong.sql(p)})"
def py(self, qator):
c = self.chap.py(qator)
return (c and self.ong.py(qator)) if self.belgi == "AND" else (c or self.ong.py(qator))
def __repr__(self):
return f"({self.chap!r} {self.belgi} {self.ong!r})"
class Inkor(Ifoda):
def __init__(self, ichki):
self.ichki = ichki
def sql(self, p):
return f"NOT {self.ichki.sql(p)}"
def py(self, qator):
return not self.ichki.py(qator)
class Ichida(Ifoda):
def __init__(self, ustun, qiymatlar):
self.ustun, self.qiymatlar = ustun, qiymatlar
def sql(self, p):
p.extend(self.qiymatlar)
return f"{self.ustun.sql(p)} IN ({', '.join('?' * len(self.qiymatlar))})"
def py(self, qator):
return self.ustun.py(qator) in self.qiymatlar
def kompilyatsiya(ifoda):
parametrlar = []
return f"SELECT * FROM foydalanuvchilar WHERE {ifoda.sql(parametrlar)}", parametrlar
yosh, faol, shahar, ism = Ustun("yosh"), Ustun("faol"), Ustun("shahar"), Ustun("ism")
QATORLAR = [
{"ism": "Aziz", "yosh": 34, "faol": True, "shahar": "Toshkent"},
{"ism": "Dilnoza", "yosh": 17, "faol": True, "shahar": "Samarqand"},
{"ism": "Bobur", "yosh": 45, "faol": False, "shahar": "Buxoro"},
{"ism": "Malika", "yosh": 22, "faol": True, "shahar": "Buxoro"},
]
print("=== 1. To'g'ri yozilgan so'rovlar ===")
sorovlar = [
(yosh >= 18) & (faol == True),
((shahar == "Buxoro") | (shahar == "Samarqand")) & ~(yosh < 18),
shahar.ichida(["Toshkent", "Buxoro"]) & (18 <= yosh),
]
for s in sorovlar:
sql, parametrlar = kompilyatsiya(s)
mos = [q["ism"] for q in QATORLAR if s.py(q)]
print(f" {sql}")
print(f" parametrlar={parametrlar} → Python filtr: {mos}")
print(f" 18 <= yosh → Python aks ettirdi: {18 <= yosh!r}")
print("\n=== 2. ⚠️ Ustuvorlik: qavssiz & ===")
try:
yosh >= 18 & faol == True
except TypeError as e:
print(f" yosh >= 18 & faol == True → TypeError: {e}")
print(f" sababi: 18 & faol birinchi hisoblanadi → {18 & faol!r}, keyin zanjirli taqqoslash bool() chaqiradi")
print("\n=== 3. ⚠️ and / or / not ===")
for tavsif, amal in [("(yosh >= 18) and (faol == True)", lambda: (yosh >= 18) and (faol == True)),
("not (yosh < 18)", lambda: not (yosh < 18)),
("if yosh > 18: ...", lambda: 1 if yosh > 18 else 0),
("18 < yosh < 65", lambda: 18 < yosh < 65)]:
try:
amal()
print(f" {tavsif:32} jim o'tdi ⚠️")
except TypeError as e:
print(f" {tavsif:32} TypeError ✅ (xato yashirinmadi)")
print("\n=== 4. ⚠️ __eq__ ifoda qaytarsa: in, index, lug'at ===")
ustunlar = [yosh, faol]
try:
print(f" shahar in ustunlar: {shahar in ustunlar}")
except TypeError as e:
print(f" shahar in ustunlar → TypeError (ichida == va bool)")
print(f" yosh in ustunlar: {yosh in ustunlar} (avval `is` tekshiriladi — == gacha yetmadi)")
print(f" lug'at kaliti sifatida: { {yosh: 'butun son', faol: 'mantiqiy'}[yosh] } (__hash__ = object.__hash__)")
print(f" ✅ identifikator bo'yicha qidirish: {any(u is shahar for u in ustunlar)}")Natijaning muhim qismi:
=== 1. To'g'ri yozilgan so'rovlar ===
SELECT * FROM foydalanuvchilar WHERE (yosh >= ? AND faol = ?)
parametrlar=[18, True] → Python filtr: ['Aziz', 'Malika']
SELECT * FROM foydalanuvchilar WHERE ((shahar = ? OR shahar = ?) AND NOT yosh < ?)
parametrlar=['Buxoro', 'Samarqand', 18] → Python filtr: ['Bobur', 'Malika']
SELECT * FROM foydalanuvchilar WHERE (shahar IN (?, ?) AND yosh >= ?)
parametrlar=['Toshkent', 'Buxoro', 18] → Python filtr: ['Aziz', 'Bobur', 'Malika']
18 <= yosh → Python aks ettirdi: (yosh >= 18)
=== 2. ⚠️ Ustuvorlik: qavssiz & ===
yosh >= 18 & faol == True → TypeError: Taqqoslash'ni bool ga aylantirib bo'lmaydi — and/or/not o'rniga & | ~ ishlating va & | atrofiga qavs qo'ying
sababi: 18 & faol birinchi hisoblanadi → (18 AND faol), keyin zanjirli taqqoslash bool() chaqiradi
=== 3. ⚠️ and / or / not ===
(yosh >= 18) and (faol == True) TypeError ✅ (xato yashirinmadi)
not (yosh < 18) TypeError ✅ (xato yashirinmadi)
if yosh > 18: ... TypeError ✅ (xato yashirinmadi)
18 < yosh < 65 TypeError ✅ (xato yashirinmadi)
=== 4. ⚠️ __eq__ ifoda qaytarsa: in, index, lug'at ===
shahar in ustunlar → TypeError (ichida == va bool)
yosh in ustunlar: True (avval `is` tekshiriladi — == gacha yetmadi)
lug'at kaliti sifatida: butun son (__hash__ = object.__hash__)
✅ identifikator bo'yicha qidirish: FalseNima ko'rsatdi: 2.5-bo'lim — & va | taqqoslashdan yuqori ustuvorlikka ega, and/or/not/if va zanjirli taqqoslash bool() ni chaqiradi, shuning uchun __bool__ dagi TypeError jim xatolarni oldini oladi; in esa avval is bilan solishtirgani uchun ba'zan ishlaydi, ba'zan yo'q.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
"a += b har doim a ni joyida o'zgartiradi" |
Faqat o'zgaruvchan turda; o'zgarmasda yangi obyekt va qayta bog'lash |
"t[0] += [x] xato bersa, hech narsa o'zgarmaydi" |
Ro'yxat o'zgaradi, keyin kortejga yozish xato beradi |
"NotImplemented va raise TypeError bir xil" |
NotImplemented ikkinchi operandga imkon beradi |
"Pul uchun float yetarli, yumaloqlab chiqaramiz" |
Guruhlash buziladi, tiyinlar yo'qoladi |
"Pul / 3 — to'g'ri taqsimlash" |
Qoldiq yo'qoladi — ajratish kerak |
| "Operator istalgan narsa qaytara oladi — muammo yo'q" | bool, in, hash, zanjirli taqqoslash kutilmagan joyda ishlaydi |
"a >= 18 & b == 1 — ikki shart" |
a >= (18 & b) == 1 — zanjirli taqqoslash |
"__bool__ yozmasam, if ifoda: xato beradi" |
Jim True — xato yashirinadi |
6. Keng tarqalgan xatolar va yechimlari
1. O'zgarmas qiymat turiga __iadd__
class Vektor:
def __iadd__(self, b):
self.x += b.x # ❌ taxalluslar o'zgaradi, lug'at kaliti xeshi buziladi
return self __iadd__ yozmang — a += b avtomatik a = a + b bo'ladi.
2. Noma'lum turga TypeError
def __add__(self, b):
if not isinstance(b, Metr):
raise TypeError("...") # ❌ Santimetr.__radd__ ishlamaydi
def __add__(self, b):
if not isinstance(b, Metr):
return NotImplemented # ✅3. Pulda float
class Pul:
def __init__(self, summa: float): ... # ❌
class Pul:
def __init__(self, summa: str | Decimal | int): ... # ✅ float — TypeError4. Pulni bo'lish
ulush = jami / 3 # ❌ 33.33 × 3 = 99.99
ulushlar = jami.ajrat(3) # ✅ [33.34, 33.33, 33.33]5. __eq__ normallashtirilgan, __hash__ esa yo'q
def __eq__(self, b): return self.summa == b.summa # Decimal("1.0") == Decimal("1.00")
def __hash__(self): return hash(str(self.summa)) # ❌ "1.0" va "1.00" — turli xesh
def __hash__(self): return hash(self.summa) # ✅ eq bilan bir xil maydon6. DSL da qavssiz & / |
df[df.yosh >= 18 & df.faol] # ❌ ustuvorlik
df[(df.yosh >= 18) & df.faol] # ✅7. DSL ifodasida and / or
filtr = (yosh > 18) and (faol == True) # ❌ faqat o'ng tomon qoladi (yoki TypeError)
filtr = (yosh > 18) & (faol == True) # ✅8. Ifoda obyektida __bool__ ni ochiq qoldirish
if Foydalanuvchi.yosh > 18: # ❌ har doim True — obyekt "rost"
...
def __bool__(self):
raise TypeError("ifodani bool ga aylantirib bo'lmaydi") # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8.12–8.14-darslar (o'tilgan): operator hal qilish tartibi, taqqoslash, arifmetika, konteyner protokoli
- 10.13-dars (o'tilgan): assotsiativlik va neytral element —
reduceva parallel yig'ish - 10.15-dars: Python ma'lumotlar modeli — barcha dunder metodlar tizimi
- 17-qism: xossaga asoslangan testlar (
hypothesis) - 24-qism: NumPy va pandas — elementlab operatorlar,
&/|va "ambiguous truth value" - 23-qism: SQLAlchemy ifodalari — ushbu DSL ning to'liq versiyasi
8. Eng yaxshi amaliyotlar
Operator faqat tabiiy ma'noga ega bo'lsa.
+— qo'shish yoki birlashtirish,/— yo'l yoki bo'lish. Ajablantiradigan operator — yomon API.Qiymat turlari o'zgarmas.
__iadd__faqat konteyner va akkumulyatorlar uchun.Bilmagan turga —
NotImplemented, ma'nosiz amalga — aniq xabarliTypeError.Pul —
Decimalyoki butun birlik, bo'lish o'rniga ajratish, valyutalar aralashmasin.__eq__va__hash__bir xil, normallashtirilgan maydonlardan.Qonunlarni avtomatik test qiling — o'rin almashtirish, guruhlash, neytral, xesh mosligi.
DSL da
__bool__ni taqiqlang,__hash__ni aniq belgilang, hujjatda qavslarni talab qiling.Qaysi qonunlar bajarilmasligini hujjatlashtiring — masalan, "
+o'rin almashtirmaydi" yoki "floatbilan aralashtirilmaydi".
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
class V:
def __init__(self, x):
self.x = x
def __add__(self, o):
if isinstance(o, V):
return V(self.x + o.x)
if isinstance(o, int):
return V(self.x + o)
return NotImplemented
__radd__ = __add__
def __sub__(self, o):
return V(self.x - o.x) if isinstance(o, V) else NotImplemented
def __rsub__(self, o):
return V(o - self.x) if isinstance(o, int) else NotImplemented
def __repr__(self):
return f"V({self.x})"
class R(list):
def __iadd__(self, o):
print("iadd", end=" ")
return NotImplemented
1. print(V(1) + 2, 2 + V(1))
2. print(10 - V(3))
3. print(sum([V(1), V(2)]))
4. try:
V(1) - 5
except TypeError:
print("TypeError")
5. a = V(1)
b = a
a += V(1)
print(a is b, b)
6. a = [1]
b = a
a += [2]
print(a is b, b)
7. t = ([1],)
try:
t[0] += [2]
except TypeError:
print("TypeError", t)
8. r = R([1])
r += [2]
print(type(r).__name__, r)
9. print(0.1 + (0.2 + 0.3) == (0.1 + 0.2) + 0.3)
10. from fractions import Fraction as F
print(F(1, 10) + (F(2, 10) + F(3, 10)) == (F(1, 10) + F(2, 10)) + F(3, 10))
11. print(1 == 1.0, hash(1) == hash(1.0))
12. print(True + True, 3 | 5, ~0)Javoblar
V(3) V(3)— ikkinchisi__radd__V(7)—int.__sub__rad etdi,V.__rsub__(10)→10 - 3V(3)—0 + V(1)→__radd__butun sonni qabul qiladiTypeError—__sub__NotImplemented,intda__rsub__Vuchun yo'qFalse V(1)—__iadd__yo'q, yangi obyektTrue [1, 2]— ro'yxat joyida o'zgardiTypeError ([1, 2],)— ro'yxat o'zgardi, keyin kortejga yozish xato berdiiadd list [1, 2]—NotImplemented→list.__add__yangilistqaytardiFalse—floatguruhlashni aniq bajarmaydiTrueTrue True— teng sonlar teng xeshga ega2 7 -1
Vazifa 2: Xatolarni tuzating
1. class Harorat:
def __init__(self, c):
self.c = c
def __add__(self, o):
if not isinstance(o, Harorat):
raise TypeError("faqat Harorat")
return Harorat(self.c + o.c)
def __iadd__(self, o):
self.c += o.c
return self
def __eq__(self, o):
return self.c == o.c
2. class Pul:
def __init__(self, summa):
self.summa = round(float(summa), 2)
def bol(self, n):
return [Pul(self.summa / n)] * n
3. filtr = df.narx > 1000 | df.chegirma == True
4. class Belgi:
def __init__(self, nom):
self.nom = nom
def __eq__(self, o):
return Taqqoslash(self, o)
teglar = {Belgi("a"): 1}
5. def jami(tolovlar):
natija = tolovlar[0]
for t in tolovlar[1:]:
natija += t
return natija
# tolovlar — o'zgaruvchan Savatcha obyektlari ro'yxatiJavoblar
1. class Harorat:
__slots__ = ("c",)
def __init__(self, c):
object.__setattr__(self, "c", c)
def __add__(self, o):
if not isinstance(o, Harorat):
return NotImplemented # boshqa tur imkoniyat olsin
return Harorat(self.c + o.c)
# __iadd__ olib tashlandi — qiymat turi o'zgarmas
def __eq__(self, o):
return self.c == o.c if isinstance(o, Harorat) else NotImplemented
def __hash__(self): # __eq__ bor — xesh ham kerak
return hash(self.c)
2. class Pul:
def __init__(self, summa):
self.summa = Decimal(str(summa)).quantize(Decimal("0.01"))
def ajrat(self, n):
tiyin = int(self.summa * 100)
qismlar = [tiyin // n] * n
for i in range(tiyin % n):
qismlar[i] += 1
return [Pul(Decimal(q) / 100) for q in qismlar]
3. filtr = (df.narx > 1000) | (df.chegirma == True)
4. class Belgi:
def __init__(self, nom):
self.nom = nom
def __eq__(self, o):
return Taqqoslash(self, o)
__hash__ = object.__hash__ # __eq__ yuklangani uchun avtomatik None bo'lgan edi
def __bool__(self):
raise TypeError("Belgi ifodasini bool ga aylantirib bo'lmaydi")
5. def jami(tolovlar):
natija = Savatcha() # yangi obyekt — birinchi elementni o'zgartirmaymiz
for t in tolovlar:
natija += t
return natijaVazifa 3: Vaqt oralig'i turi
Oraliq(boshlanish, tugash) — sanalar oralig'i turini yozing:
a & b— kesishma (yo'q bo'lsaNoneemas, bo'sh oraliq obyekti)a | b— birlashma: kesishsa yoki yondosh bo'lsa bitta oraliq, aks holdaOraliqlarToplamia - b— ayirma (0, 1 yoki 2 ta oraliq)sana in oraliq,len(oraliq)— kunlar soni,bool(oraliq)- Qonunlarni Misol 3 uslubida tekshiring:
&va|o'rin almashtirish va guruhlash,a & a == a
Vazifa 4: Pulga konvertatsiya
Misol 2 dagi Pul ni kengaytiring:
Kurs(dan, ga, qiymat)turi:Pul("100", "USD") * Kurs("USD", "UZS", "12600")→PulUZS da; noto'g'ri yo'nalish —TypeErrorKurs @ Kurs— kurslar kompozitsiyasi (USD→UZS @ EUR→USD→EUR→UZS)ajratga salbiy summalarni qo'shing__round__(n)va__format__da"q"(qisqa:1.2 mln so'm) spec- Qonun testlari: taqsimlash (
k*(a+b) == k*a + k*b), ajratishdan keyin yig'indi saqlanishi
Vazifa 5: DSL ni kengaytiring
Misol 4 dagi so'rov quruvchiga qo'shing:
ustun.oraliqda(a, b)→BETWEEN,ustun.like("A%")- Arifmetik ifodalar:
(narx * soni) > 100_000—Arifmetiktugun, SQL va Python ga kompilyatsiya Ifoda.soddalashtir()—~~x→x,x & True→x, bir xil shartlarni birlashtirish__repr__va daraxtni chiroyli chop etish- SQL inyeksiyasiga qarshi test: qiymatlarda
'; DROP TABLEbo'lsa ham parametr sifatida qolishini tekshiring
Vazifa 6: Qonun tekshirgich kutubxonasi
Misol 3 ni kichik kutubxonaga aylantiring:
@qonundekoratori bilan qonunlarni ro'yxatga olish- Buzilgan holatda eng kichik qarshi misolni topish (qiymatlarni kichraytirib qayta tekshirish — "shrinking")
floatkabi turlar uchuntaqqoslash=math.iscloseparametriPul,Oraliq,Vektorvasetuchun qonunlar to'plamlari- Hisobot: har qonun uchun "", " qarshi misol" yoki " hujjatlashtirilgan istisno"
Vazifa 7: O'ylash
Java foydalanuvchi operatorlarini ortiqcha yuklashga umuman ruxsat bermaydi, C++ deyarli hamma operatorni (hatto , va && ni) yuklashga ruxsat beradi, Python esa oraliq yo'lni tanlagan: ko'p operatorlar yuklanadi, lekin and, or, not, is va tayinlash yo'q. Bu tanlovlarning har biri nimani himoya qiladi va nimani qurbon qiladi?
Javob
Qisqa javob: operator ortiqcha yuklash — ifodalilik va bashorat qilinuvchanlik orasidagi kelishuv. Java bashorat qilinuvchanlikni tanladi: a + b har doim o'qiladigan, lekin BigDecimal va vektorlar bilan kod og'ir. C++ ifodalilikni tanladi: matematik kutubxonalar tabiiy yoziladi, lekin && yoki , ni yuklash qisqa tutashuv va tartib kafolatlarini buzadi. Python operator ma'nosi yuklanadigan, lekin boshqaruv oqimi (and, or, not), identifikatsiya (is) va nomlash (tayinlash) yuklanmaydigan chegarani tanladi.
1. Java — yuklash yo'q
Java'da + faqat sonlar va String uchun ishlaydi. Java dizaynerlari C++ dagi operatorlarni suiiste'mol qilish tajribasini sabab qilib ko'rsatishgan.
- Har
a + b— ma'lum tur uchun ma'lum amal; kod o'quvchi hech narsa taxmin qilmaydi -
BigDecimalbilan pul:narx.multiply(soni).add(soliq).setScale(2, RoundingMode.HALF_EVEN) - Vektor, matritsa, kompleks son — ilmiy hisoblash uchun noqulay; shu sabab Kotlin (JVM tili)
operator funkonventsiyasi bilan belgilangan operatorlarni yuklashga ruxsat berdi
2. C++ — deyarli hamma narsa
- Matematik kutubxonalar (Eigen), iteratorlar (
*it,++it), aqlli ko'rsatkichlar (->), oqimlar (<<) tabiiy yoziladi -
operator&&vaoperator||yuklansa, qisqa tutashuv yo'qoladi — ikkala operand ham hisoblanadi -
operator,yuklansa, baholanish tartibi haqidagi taxminlar buziladi -
<<ning "chapga siljitish" va "oqimga yozish" ma'nolari aralashadi — ustuvorlik esa siljitishniki qoladi:cout << a & bkutilganidek ishlamaydi
C++ dagi ko'p ko'rsatmalar (masalan, "&&, || va , ni yuklamang") aynan shu muammolardan kelib chiqqan.
3. Python — ma'no yuklanadi, oqim yo'q
Yuklanadiganlar: arifmetika, bitlar, taqqoslash, indekslash, chaqirish, atributga murojaat, konteyner protokoli, kontekst menejeri va boshqalar.
Yuklanmaydiganlar va sababi:
| Yuklanmaydi | Himoya qiladi |
|---|---|
and, or |
Qisqa tutashuv: x and x.y — x yolg'on bo'lsa x.y hisoblanmaydi |
not |
Mantiqiy inkorning yagona ma'nosi (__bool__ orqali) |
is, is not |
Identifikatsiya — hech qachon "yolg'on gapirmaydi" |
= (tayinlash) |
Nom bog'lash — obyekt emas, nom maydoni ishi |
| Ustuvorlik va assotsiativlik | Kodni tur bilmasdan tahlil qilish mumkin |
PEP 335 (and/or ni yuklash taklifi) rad etilgan. Buning o'rniga NumPy va pandas &, |, ~ ni ishlatadi — va ularning ustuvorligi tuzog'i (Misol 4) shu qarorning narxi.
4. Boshqa tillar
- Swift, Scala, Haskell — butunlay yangi operatorlar yaratishga ruxsat beradi (
<|>,>>=,|>) va ularning ustuvorligini belgilash mumkin. Ifodalilik maksimal, lekin notanish kodni o'qish uchun operatorlar lug'atini bilish kerak - Kotlin, C#, Rust — faqat oldindan belgilangan operatorlar ro'yxati, aniq nomli metodlar/traitlar orqali (
operator fun plus,impl Add). Rust'da&&va||yuklanmaydi — Python bilan bir xil sabab
5. Tanlovning amaliy oqibatlari
- Python'da ilmiy hisoblash ekotizimi (NumPy, pandas, PyTorch) paydo bo'lishiga operatorlar katta hissa qo'shgan:
A @ B,df[df.x > 0],tensor * 2. PEP 465 aynan NumPy jamoasi talabi bilan@operatorini qo'shgan - Lekin qolgan cheklovlar —
ando'rniga&, qavslar,__bool__taqiqlari — har bir DSL foydalanuvchisi o'rganishi kerak bo'lgan "shevalar" yaratdi
6. Loyihalovchi uchun qoidalar (qaysi tilda bo'lmasin)
- Operator tabiiy ma'noga ega bo'lsin — "ajablantirmaslik tamoyili"
- Operatorning algebraik va'dalarini bajaring yoki hujjatlashtiring
- Boshqaruv oqimi va identifikatsiyaga ta'sir qiladigan narsalarni yuklamang
- Ustuvorlik o'zgarmasligini unutmang — foydalanuvchini qavsga majburlang yoki xatoni tez va aniq ko'rsating
- Nomli metod aniqroq bo'lsa, operatorni tanlamang (
pul.ajrat(3)—pul / 3dan yaxshi)
7. Xulosa
- Java bashorat qilinuvchanlikni, C++ ifodalilikni maksimal qilgan; ikkalasi ham o'z narxini to'laydi
- Python ma'no operatorlarini yuklashga ruxsat beradi, lekin qisqa tutashuv, identifikatsiya va tayinlashni himoya qiladi
- Bu chegara NumPy/pandas kabi kutubxonalarni imkonli qildi, lekin
&/|ustuvorligi tuzog'ini qoldirdi - Yangi operatorlar yaratishga ruxsat beruvchi tillar ifodalilikni o'qilishi hisobiga oshiradi
- Qaysi tilda bo'lmasin, operator — va'da: ma'no, qonunlar va kutilgan xatti-harakat
Nimani mustahkamlaydi: 2.1–2.5-bo'limlar.
Xulosa
Bu darsda operator ortiqcha yuklashni real turlarni loyihalash nuqtai nazaridan o'rgandik.
Eng muhim uch fikr:
Joyida amallar va rad etish — semantika, uslub emas.
a += bo'zgaruvchan turda taxalluslarni ham o'zgartiradi, o'zgarmasda yangi obyekt beradi;__iadd__danNotImplementedqaytsa__add__ishlaydi va natija boshqa turda bo'lishi mumkin;t[0] += [x]ro'yxatni o'zgartirib, keyin xato beradi. Qiymat turlarini o'zgarmas qiling. Bilmagan turgaNotImplemented, ma'nosiz amalga esa aniq xabarliTypeErrorqaytaring.Operator — algebraik va'da. Pul turi
Decimalyoki butun birlikda saqlanadi, valyutalar aralashmaydi, bo'lish o'rniga qoldiqsiz ajratish ishlatiladi,__eq__va__hash__bir xil normallashtirilgan maydonlardan hisoblanadi. O'rin almashtirish, guruhlash, neytral element va xesh mosligini tasodifiy qiymatlar bilan avtomatik tekshirish yashirin xatolarni bir necha qatorda topadi.Operatorlar DSL qura oladi, lekin Python grammatikasi o'zgarmaydi.
and,or,notyuklanmaydi —&,|,~ishlating; ularning ustuvorligi taqqoslashdan yuqori — qavs talab qiling. Ifoda obyektida__bool__ni taqiqlang, aks holdaif,andva zanjirli taqqoslash jim noto'g'ri ishlaydi;__eq__yuklangani uchun__hash__ni aniq belgilang vainning avvalisbilan solishtirishini hisobga oling.
Keyingi darsda Python ma'lumotlar modelini yaxlit ko'rib chiqamiz: obyekt, tur va qiymat; dunder metodlar qanday guruhlarga bo'linadi, interpretator ularni qachon va qaysi tartibda chaqiradi, va maxsus metodlar nega nusxada emas, turda qidiriladi.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!