Ачааллж байна...
Өмнөх хичээлд бид __str__ ба __repr__ -ээр объектыг ХАРУУЛАХ асуудлыг шийдсэн. Одоо өөр нэг үндсэн асуудал руу орно: объектуудыг ХАРЬЦУУЛАХ.
Асуулт нь энгийн мэт санагдана: хоёр объект хэзээ тэнцүү вэ?
Курс 1-ээс == оператор танил. Тэр нь бүх энгийн төрөл дээр байгалиар ажилладаг:
python
print("Болд" == "Болд")
print(42 == 42)
print([1, 2] == [1, 2])
print({"ner": "Болд"} == {"ner": "Болд"})True
True
True
TrueДөрвүүлээ True. Логиктой — агуулга нь ижил тул тэнцүү.
Одоо өөрийн class дээр туршъя:
python
class Nom:
def __init__(self, garchig: str, zokhiolch: str) -> None:
self.garchig = garchig
self.zokhiolch = zokhiolch
nom1 = Nom("Монголын нууц товчоо", "Тодорхойгүй")
nom2 = Nom("Монголын нууц товчоо", "Тодорхойгүй")
print(nom1 == nom2)FalseГэнэтийн зүйл.
Хоёр номын гарчиг ижил, зохиолч ижил — БҮХ талбар ижил. Гэвч Python нь тэднийг ТЭНЦҮҮ БИШ гэж хэлж байна.
Энэ хичээлийн зорилго бол тэр зан төлөвийг ойлгож, засах явдал юм. Мөн зам дунд бид Python-ы нэг гүнзгий санаа — "ижил зүйл" ба "ижил утгатай зүйл" -ийн ялгааг эцэст нь тодруулна.
Асуултыг бодит амьдрал дээр тавья.
Танд "Монголын нууц товчоо" гэсэн ном байна. Найзад тань ч бас "Монголын нууц товчоо" байна.
Асуулт: та хоёр ижил номтой юу?
Хариулт нь ХОЁР УТГАТАЙ:
Утга 1: "Тийм — ижил номтой." Гарчиг нь ижил, агуулга нь ижил, зохиолч нь ижил. Тэдгээр нь ИЖИЛ ном.
Утга 2: "Үгүй — өөр номтой." Таны ном таны тавиур дээр, найзын ном найзын тавиур дээр. Тэдгээр нь ХОЁР ӨӨР эд юм. Хэрэв та өөрийнхөө номд тэмдэглэл бичвэл, найзын ном хөндөгдөхгүй.
Хоёулаа ЗӨВ. Асуулт нь буруу тавигдсан: та "ижил" гэдгээр юуг хэлж байгаагаа тодруулаагүй.
Python-д яг ижил хоёр асуулт байдаг бөгөөд хоёр өөр оператор байдаг:
is — "ЯГ ТЭР объект уу?" (нэг л ном, нэг л тавиур дээр)
== — "ИЖИЛ УТГАТАЙ юу?" (хоёр ном, ижил агуулга)
Анхдагчаар Python-ы class-ууд нь == -ыг is -тэй ИЖИЛ ажиллуулдаг. Тэр л дээрх гэнэтийн үр дүнгийн шалтгаан юм.
is ба == -ийн ялгааТодруулъя:
python
class Nom:
def __init__(self, garchig: str) -> None:
self.garchig = garchig
nom1 = Nom("Монголын нууц товчоо")
nom2 = Nom("Монголын нууц товчоо")
nom3 = nom1
print(nom1 is nom2) # ЯГ тэр объект уу?
print(nom1 is nom3) # ЯГ тэр объект уу?
print(nom1 == nom2) # Ижил утгатай юу?
print(nom1 == nom3) # Ижил утгатай юу?False
True
False
TrueЗадалъя.
nom1 is nom2 → False. Тэдгээр нь ХОЁР ӨӨР объект. Nom(...) -ыг хоёр удаа дуудсан тул хоёр өөр объект үүссэн, санах ойн хоёр өөр байрлалд.
nom1 is nom3 → True. nom3 = nom1 гэсэн мөр нь ХУУЛБАР үүсгээгүй — зөвхөн ХОЁР ДАХЬ НЭР өгсөн. (Бүлэг 2.4-ийн slicing хичээлийн huulbar = anhnii -тэй ЯГ ижил зарчим.) Нэг л объект, хоёр нэр.
nom1 == nom2 → False. Энэ бол ГЭНЭТИЙН хэсэг. Талбар нь ижил ч гэсэн False.
Учир нь: анхдагчаар Python-ы class-ууд нь == -ыг is шиг ажиллуулдаг. Өөрөөр хэлбэл "тэнцүү" гэдэг нь "яг тэр объект" гэсэн утгатай болдог. Талбаруудыг харьцуулах ч үгүй.
nom1 == nom3 → True. Учир нь тэдгээр нь ЯГ тэр объект.
Тэгэхээр анхдагч зан төлөв нь: == нь is -тэй ижил. Тэр нь ихэнхдээ бидний хүссэн зүйл БИШ.
Шударга асуулт: яагаад Python нь автоматаар талбаруудыг харьцуулдаггүй юм бэ?
Учир нь Python нь ЮУ нь чухал болохыг мэдэхгүй.
Жишээ авъя. Танд Nom class байна, дараах талбартай: garchig, zokhiolch, on, zeeldsen, khuudas.
Хоёр ном тэнцүү мөн үү, хэрэв тэдгээр нь ижил гарчиг, зохиолчтой ч, нэг нь ЗЭЭЛЭГДСЭН, нөгөө нь үгүй бол?
Хамаарна. Хэрэв та номын САНГИЙН БҮРТГЭЛ ярьж байвал — тэдгээр нь ХОЁР ӨӨР хувь, зээлсэн байдал нь чухал. Хэрэв та номын АГУУЛГА ярьж байвал — тэдгээр нь ИЖИЛ ном, зээлсэн байдал нь хамаагүй.
Python нь таны бодлыг мэдэхгүй. Тиймээс тэр юу ч таамаглахгүй — зөвхөн хамгийн аюулгүй хариултыг өгнө: "хоёр өөр объект бол тэнцүү биш."
Шийдэл: түүнд ХЭЛЖ ӨГӨХ. Тэр нь __eq__ special method юм.
python
class Nom:
def __init__(self, garchig: str, zokhiolch: str) -> None:
self.garchig = garchig
self.zokhiolch = zokhiolch
def __eq__(self, busad: object) -> bool:
if not isinstance(busad, Nom):
return NotImplemented
return self.garchig == busad.garchig and self.zokhiolch == busad.zokhiolch
def __repr__(self) -> str:
return f"Nom(garchig={self.garchig!r}, zokhiolch={self.zokhiolch!r})"
nom1 = Nom("Монголын нууц товчоо", "Тодорхойгүй")
nom2 = Nom("Монголын нууц товчоо", "Тодорхойгүй")
nom3 = Nom("Цагаан хэрэм", "Б. Ринчен")
print(nom1 == nom2)
print(nom1 == nom3)
print(nom1 is nom2)True
False
FalseОдоо зөв ажиллаж байна.
nom1 == nom2 → True (ижил утгатай).nom1 is nom2 → False (өөр объект).
Хоёр асуулт, хоёр өөр хариулт. Яг бидний хүссэн зүйл.
Одоо __eq__ -ыг мөр мөрөөр нь задалъя.
def __eq__(self, busad: object) -> bool:
self нь зүүн талын объект (nom1). busad нь баруун талын объект (nom2).
Hint нь busad: object гэдгийг анзаараарай, busad: Nom ГЭЖ БИЧЭЭГҮЙ. Яагаад?
Учир нь Python нь ЯМАР Ч зүйлийг баруун талд нь тавихыг зөвшөөрдөг:
python
print(nom1 == "Болд")
print(nom1 == 42)
print(nom1 == None)Эдгээр бүгд ажиллах ЁСТОЙ (алдаа гаргахгүйгээр), тиймээс __eq__ нь ямар ч төрлийг хүлээж авах ёстой. object бол Python-ы хамгийн ерөнхий төрөл — БҮХ зүйл түүнээс өвлөдөг (Бүлэг 7.1-ээс санаж байгаа байх).
if not isinstance(busad, Nom): return NotImplemented
Энэ бол ХАМГААЛАЛТ. Хэрэв баруун талын зүйл нь Nom биш бол — бид түүнийг харьцуулж чадахгүй.
NotImplemented гэдэг нь тусгай утга бөгөөд "би энэ харьцуулалтыг хийж чадахгүй" гэсэн үг. Python нь түүнийг харвал, эсрэг чиглэлээр оролдоно (баруун талын объектын __eq__ -ыг дуудна), тэр ч бүтэлгүйтвэл False буцаана.
Анхаар: NotImplemented нь NotImplementedError -тэй ИЖИЛ БИШ. Эхнийх нь утга, хоёр дахь нь алдаа. Нэрс нь төстэй тул андуурч болзошгүй.
Хэрэв та энэ хамгаалалтыг бичихгүй бол:
python
def __eq__(self, busad: object) -> bool:
return self.garchig == busad.garchig # хамгаалалтгүйpython
print(nom1 == "Болд")Traceback (most recent call last):
File "main.py", line 12, in <module>
print(nom1 == "Болд")
File "main.py", line 7, in __eq__
return self.garchig == busad.garchig
AttributeError: 'str' object has no attribute 'garchig'AttributeError: 'str' object has no attribute 'garchig' — string-д garchig талбар байхгүй. Программ унасан.
Тиймээс isinstance шалгалт нь ЗААВАЛ хэрэгтэй.
return self.garchig == busad.garchig and self.zokhiolch == busad.zokhiolch
Энд бид АЛЬ талбарууд чухал болохыг ӨӨРСДӨӨ шийдэж байна. Гарчиг ба зохиолч ижил бол — тэнцүү.
Хэрэв бид on ба zeeldsen -ыг ч оруулахыг хүсвэл — тэднийг нэмнэ. Тэр бол ДИЗАЙНЫ шийдвэр, Python-ы асуудал биш.
in ажиллаж эхэллээОдоо __eq__ -ийн жинхэнэ ашиг тусыг харъя. Тэр нь зөвхөн == -д биш, олон газарт нөлөөлдөг.
python
nomnuud: list[Nom] = [
Nom("Монголын нууц товчоо", "Тодорхойгүй"),
Nom("Цагаан хэрэм", "Б. Ринчен"),
]
khaij_bui = Nom("Цагаан хэрэм", "Б. Ринчен")
print(khaij_bui in nomnuud)Truein оператор ажиллаж байна!
Гэвч khaij_bui нь жагсаалт доторх номтой ЯГ ТЭР объект БИШ — тэр нь шинээр үүсгэсэн, огт өөр объект. Тэгвэл яаж олдов?
Учир нь in нь дотроо == -ыг ашигладаг. Тэр нь жагсаалтын элемент бүрийг эргүүлж, element == khaij_bui гэж шалгадаг. __eq__ бичсэн тул харьцуулалт нь агуулгаар явж, тохирсон нэгийг олсон.
Хэрэв __eq__ бичээгүй бол:
FalseУчир нь анхдагч == нь is шиг ажилладаг тул ямар ч тохирол олдохгүй.
Ижил зүйл .index() ба .remove() дээр:
python
baiirlal = nomnuud.index(Nom("Цагаан хэрэм", "Б. Ринчен"))
print(f"Байрлал: {baiirlal}")
nomnuud.remove(Nom("Цагаан хэрэм", "Б. Ринчен"))
print(nomnuud)Байрлал: 1
[Nom(garchig='Монголын нууц товчоо', zokhiolch='Тодорхойгүй')]Гайхалтай: та номыг АГУУЛГААР нь олж, устгаж чадаж байна. Түүний ЯГ ТЭР объект гарт байх шаардлагагүй.
Энэ бол special method-ын жинхэнэ хүч: та НЭГ method бичсэн, гэвч Python-ы ОЛОН хэрэгсэл автоматаар ажиллаж эхэлсэн. ==, !=, in, .index(), .remove(), .count() — бүгд __eq__ дээр баригдсан.
!= автоматаар ирдэгpython
print(nom1 != nom3)TrueБид __ne__ (not equal) гэж юу ч бичээгүй. Гэвч != ажиллаж байна.
Учир нь Python нь автоматаар "!= бол == -ийн эсрэг" гэж үздэг. Тэгэхээр __eq__ бичихэд != нь ҮНЭГҮЙ ирнэ.
(Python 2-т ингэж байгаагүй — тэнд __ne__ -ыг тусад нь бичих хэрэгтэй байсан. Python 3 нь үүнийг зассан.)
Одоо чухал асуулт: __eq__ -д АЛЬ талбаруудыг оруулах вэ?
Энэ нь ХЭЗЭЭ Ч техникийн асуулт биш. Энэ бол ДИЗАЙНЫ асуулт. Та өөрөө шийдэх ёстой.
Жишээ 1: бүх талбар чухал.
python
class Tsegleg:
def __init__(self, x: int, y: int) -> None:
self.x = x
self.y = y
def __eq__(self, busad: object) -> bool:
if not isinstance(busad, Tsegleg):
return NotImplemented
return self.x == busad.x and self.y == busad.y
def __repr__(self) -> str:
return f"Tsegleg(x={self.x}, y={self.y})"
print(Tsegleg(10, 20) == Tsegleg(10, 20))
print(Tsegleg(10, 20) == Tsegleg(10, 21))True
FalseКоординат бол ЗӨВХӨН x ба y. Хоёулаа чухал. Энгийн.
Жишээ 2: зөвхөн ID чухал.
python
class Gishuun:
def __init__(self, id: int, ner: str, utas: str) -> None:
self.id = id
self.ner = ner
self.utas = utas
def __eq__(self, busad: object) -> bool:
if not isinstance(busad, Gishuun):
return NotImplemented
return self.id == busad.id # ЗӨВХӨН id!
def __repr__(self) -> str:
return f"Gishuun(id={self.id}, ner={self.ner!r})"
g1 = Gishuun(1001, "Болд", "99112233")
g2 = Gishuun(1001, "Болд", "88445566") # утас өөрчлөгдсөн!
print(g1 == g2)TrueУтас нь өөр ч гэсэн ТЭНЦҮҮ.
Яагаад? Учир нь id нь тэр ХҮНИЙГ ТОДОРХОЙЛЖ байна. Хэрэв хүн утсаа сольсон бол — тэр хэвээрээ ТЭР ХҮН. id нь өөрчлөгдөөгүй.
Энэ бол маш түгээмэл, маш зөв дизайн. Өгөгдлийн сан ашигладаг систем бүхэн ингэж ажилладаг: ID нь ижил бол — ижил зүйл, бусад талбар өөрчлөгдсөн ч.
Жишээ 3: зарим талбар хамаагүй.
python
class Nom:
def __init__(self, isbn: str, garchig: str, zeeldsen: bool = False) -> None:
self.isbn = isbn
self.garchig = garchig
self.zeeldsen = zeeldsen
def __eq__(self, busad: object) -> bool:
if not isinstance(busad, Nom):
return NotImplemented
return self.isbn == busad.isbn # zeeldsen нь ХАМААГҮЙ
def __repr__(self) -> str:
return f"Nom(isbn={self.isbn!r}, garchig={self.garchig!r})"
nom1 = Nom("978-1", "Монголын нууц товчоо", zeeldsen=False)
nom2 = Nom("978-1", "Монголын нууц товчоо", zeeldsen=True)
print(nom1 == nom2)TrueНэг нь зээлэгдсэн, нөгөө нь үгүй. Гэвч ТЭНЦҮҮ, учир нь ISBN нь ижил — тэдгээр нь ИЖИЛ НОМ.
zeeldsen бол ТӨЛӨВ (state), ОНОШ (identity) биш. Төлөв нь өөрчлөгддөг, онош нь өөрчлөгддөггүй.
Практик дүрэм: __eq__ -д зөвхөн ОНОШ тодорхойлдог талбаруудыг л оруул. Өөрчлөгдөж болох төлөвийг оруулах нь ихэвчлэн буруу.
Одоо чухал анхааруулга.
Бүлэг 5-т бид set ба dictionary-ийн key нь hash ашигладаг гэж хэлсэн. Тэр нь тэдгээрийн хурдны нууц байсан.
Хэрэв та __eq__ бичвэл — Python нь тэр объектыг set-д хийх, dictionary-ийн key болгохыг ХОРИГЛОНО:
python
class Nom:
def __init__(self, garchig: str) -> None:
self.garchig = garchig
def __eq__(self, busad: object) -> bool:
if not isinstance(busad, Nom):
return NotImplemented
return self.garchig == busad.garchig
nomnuud = {Nom("Монголын нууц товчоо")}Traceback (most recent call last):
File "main.py", line 12, in <module>
nomnuud = {Nom("Монголын нууц товчоо")}
TypeError: unhashable type: 'Nom'TypeError: unhashable type: 'Nom' — Бүлэг 5-аас танил алдаа! Тэнд бид list-ийг set-д хийж чадахгүй гэж үзсэн. Одоо ижил алдаа өөрийн class дээр гарч байна.
Яагаад? Логик нь ойлгомжтой. Set ба dictionary нь "хоёр зүйл тэнцүү бол — ижил hash-тай байх ЁСТОЙ" гэж таамагладаг. Хэрэв та __eq__ -ыг өөрчилвөл, тэр таамаглал эвдэрч болно.
Тиймээс Python нь болгоомжтой ханддаг: __eq__ бичсэн class нь автоматаар hashable БИШ болно.
Засвар: __hash__ -ыг ч бас бич.
python
class Nom:
def __init__(self, garchig: str, zokhiolch: str) -> None:
self.garchig = garchig
self.zokhiolch = zokhiolch
def __eq__(self, busad: object) -> bool:
if not isinstance(busad, Nom):
return NotImplemented
return self.garchig == busad.garchig and self.zokhiolch == busad.zokhiolch
def __hash__(self) -> int:
return hash((self.garchig, self.zokhiolch))
def __repr__(self) -> str:
return f"Nom(garchig={self.garchig!r}, zokhiolch={self.zokhiolch!r})"
nomnuud: set[Nom] = {
Nom("Монголын нууц товчоо", "Тодорхойгүй"),
Nom("Монголын нууц товчоо", "Тодорхойгүй"), # ДАВХАРДАЛ
Nom("Цагаан хэрэм", "Б. Ринчен"),
}
print(len(nomnuud))
print(nomnuud)2
{Nom(garchig='Монголын нууц товчоо', zokhiolch='Тодорхойгүй'), Nom(garchig='Цагаан хэрэм', zokhiolch='Б. Ринчен')}Set нь давхардлыг устгалаа! Гурван ном хийсэн, хоёр л үлдсэн — учир нь эхний хоёр нь ТЭНЦҮҮ.
__hash__ -ийг задалъя:
return hash((self.garchig, self.zokhiolch))
hash() бол built-in функц. Тэр нь ямар ч immutable зүйлээс тоо гаргадаг.
Бид TUPLE дамжуулж байгааг анзаараарай: (self.garchig, self.zokhiolch). Хоёр дугуй хаалт байгаа — гаднах нь функцийн дуудалт, дотоод нь tuple.
Яагаад tuple вэ? Учир нь бид ХОЁР талбарыг НЭГ hash болгож нэгтгэх хэрэгтэй. Tuple нь immutable тул hash хийж болно (Бүлэг 5.1-ээс санаж байгаа байх).
ХАМГИЙН ЧУХАЛ ДҮРЭМ: __hash__ нь __eq__ -тэй ЯГ ИЖИЛ талбаруудыг ашиглах ёстой.
Хэрэв __eq__ нь garchig ба zokhiolch -ыг харьцуулж байвал — __hash__ нь ч бас тэр хоёрыг ашиглах ёстой. Хэрэв тэдгээр нь зөрвөл — set ба dictionary нь ЧИМЭЭГҮЙ буруу ажиллана. Тэр бол хамгийн муу төрлийн алдаа.
Санамж: __hash__ дотор ЗӨВХӨН immutable талбар ашигла. Хэрэв объектын талбар өөрчлөгдөж байвал, hash нь өөрчлөгдөж, set нь түүнийг олохоо болино.
Одоо шударгаар хэлье: та энэ хичээлийн турш маш их код бичлээ.
Нэг энгийн class-д:
__init__ — талбарууд
__repr__ — харуулах
__str__ — гоё харуулах
__eq__ — харьцуулах, isinstance шалгалттай
__hash__ — set-д ажиллуулах, __eq__ -тэй тааруулах
Тавхан method, бараг бүгд механик. Мөн тэдгээр нь class бүрд ДАВТАГДАНА.
Хэрэв танд арван class байвал — тавь method бичих хэрэгтэй. Мөн тэдгээрийн ихэнх нь бараг ижилхэн байх болно.
Энэ бол ЯГ ТЭР асуудал бөгөөд Бүлэг 9-ийн dataclass түүнийг шийднэ.
Тэнд та зөвхөн ингэж бичнэ:
python
from dataclasses import dataclass
@dataclass(frozen=True)
class Nom:
garchig: str
zokhiolch: strТэгээд л болоо. __init__, __repr__, __eq__, __hash__ — БҮГД автоматаар үүснэ.
Тэр @ тэмдэг нь decorator бөгөөд түүнийг Бүлэг 10-д бүрэн тайлбарлана. Одоохондоо тэр нь "энэ class-д ид шид нэм" гэсэн заавар гэж ойлгоод өнгөрөөрэй.
Гэвч энэ хичээл нь ХЭРЭГГҮЙ байсан гэсэн үг БИШ. Эсрэгээр: dataclass юу хийж байгааг ойлгохын тулд та эхлээд гараар бичиж үзэх ЁСТОЙ. Тэгвэл dataclass нь илбэ биш, харин таны бичих байсан кодыг АВТОМАТААР бичдэг хэрэгсэл гэдгийг мэдэх болно.
Хэрэв сонирхвол: __eq__ -гүй class үүсгээд, хоёр ижил объект харьцуулж False гарахыг өөрийн нүдээр хараарай. Дараа нь __eq__ нэмээд, True болохыг хараарай. Дараа нь in, .index(), .remove() -ыг туршаад — тэдгээр нь бүгд гэнэт ажиллаж эхэлснийг мэдрээрэй.
Дараа нь тэр объектыг set-д хийж үзээрэй → TypeError: unhashable type. Дараа нь __hash__ нэмээд, ажиллахыг нь хараарай.
Тэр дараалал нь энэ хичээлийн бүх агуулгыг бататгана.
1. isinstance шалгалт мартах
python
def __eq__(self, busad: object) -> bool:
return self.garchig == busad.garchigAttributeError: 'str' object has no attribute 'garchig'nom == "Болд" гэж харьцуулахад унана. isinstance шалгалтыг ЗААВАЛ бич.
2. NotImplemented -ийн оронд False буцаах
python
def __eq__(self, busad: object) -> bool:
if not isinstance(busad, Nom):
return False # NotImplemented байх ёстой
...Ихэнхдээ ажиллана, гэхдээ зөв биш. NotImplemented нь Python-д "эсрэг чиглэлээр оролдоод үз" гэж хэлдэг. False нь "тэнцүү биш, эргэлзээгүй" гэж хэлдэг. Эхнийх нь илүү уян хатан.
3. __hash__ -ыг мартах
TypeError: unhashable type: 'Nom'__eq__ бичсэн class нь автоматаар hashable биш болно. Хэрэв set/dictionary-д ашиглах бол __hash__ ч бас бич.
4. __hash__ ба __eq__ -ийн талбарууд зөрөх
python
def __eq__(self, busad: object) -> bool:
...
return self.garchig == busad.garchig and self.zokhiolch == busad.zokhiolch
def __hash__(self) -> int:
return hash(self.garchig) # zokhiolch БАЙХГҮЙ!Алдаа гарахгүй. Гэхдээ set ба dictionary нь ЧИМЭЭГҮЙ буруу ажиллана. __hash__ нь __eq__ -тэй ЯГ ижил талбаруудыг ашиглах ёстой.
5. is ба == -ыг андуурах
python
if nom1 is nom2: # "ЯГ тэр объект уу?"
if nom1 == nom2: # "Ижил утгатай юу?"Ихэнх тохиолдолд танд == хэрэгтэй. is нь зөвхөн None шалгахад л ашиглагдана (Бүлэг 4.3-аас: if utga is None:).
6. __eq__ дотор өөрчлөгдөж болох талбар ашиглах
python
def __eq__(self, busad: object) -> bool:
...
return self.isbn == busad.isbn and self.zeeldsen == busad.zeeldsenzeeldsen нь ТӨЛӨВ, онош биш. Ном зээлэгдэхэд тэр ӨӨР ном болдоггүй. __eq__ -д зөвхөн ОНОШ тодорхойлдог талбаруудыг л оруул.
Анхдагчаар == нь is шиг ажилладаг — өөрөөр хэлбэл "яг тэр объект уу?" Тиймээс ижил талбартай хоёр объект нь False гэж гарна.
is = "ЯГ ТЭР объект уу?" == = "ИЖИЛ УТГАТАЙ юу?"
__eq__(self, busad: object) -> bool нь == -ийн зан төлөвийг тодорхойлно.
busad: object гэж hint бич — ямар ч төрөл ирж болно.
isinstance шалгалт ЗААВАЛ: if not isinstance(busad, Nom): return NotImplemented.
NotImplemented нь "би энэ харьцуулалтыг хийж чадахгүй" гэсэн тусгай утга. (NotImplementedError -тэй андуурч болохгүй.)
__eq__ бичихэд ОЛОН зүйл автоматаар ажиллаж эхэлнэ: ==, !=, in, .index(), .remove(), .count().
!= нь ҮНЭГҮЙ ирнэ — __ne__ бичих шаардлагагүй.
__eq__ -д АЛЬ талбарууд орох нь ДИЗАЙНЫ шийдвэр. Зөвхөн ОНОШ тодорхойлдог талбаруудыг оруул. ТӨЛӨВ (zeeldsen) -ийг бүү оруул. ID байвал ихэвчлэн зөвхөн түүнийг л ашигла.
__eq__ бичсэн class нь автоматаар hashable БИШ болно → TypeError: unhashable type. Set/dictionary-д ашиглах бол __hash__ ч бас бич.
__hash__ нь __eq__ -тэй ЯГ ИЖИЛ талбаруудыг ашиглах ЁСТОЙ. Зөрвөл set/dictionary чимээгүй буруу ажиллана.
__hash__ -д tuple ашигла: return hash((self.garchig, self.zokhiolch)).
Энэ бүх код нь ЯДАРГААТАЙ. Бүлэг 9-ийн dataclass нь __init__, __repr__, __eq__, __hash__ -ыг БҮГДИЙГ автоматаар үүсгэнэ.
Дараагийн хичээлд бид __len__ -ыг үзэж, дараа нь илүү гүнзгий санаа руу орно: протокол. Яагаад len() нь string, list, dictionary, tuple, set — бүгд дээр ажилладаг вэ? Тэдгээр нь огт өөр төрлүүд шүү дээ. Хариулт нь: тэд бүгд НЭГ ижил гэрээг биелүүлдэг. Тэр гэрээг ойлгосноор та len(), for, in, [] — Python-ы бүх үндсэн үйлдлүүд яаж ажилладгийг ойлгох болно.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.