Ачааллж байна...
Бүлэг 7-ийн туршид нэг эвгүй зүйл бидний хажуугаар өнгөрсөн. Одоо түүнийг эцэст нь шийдэх цаг болжээ.
python
class Nom:
def __init__(self, garchig: str, zokhiolch: str) -> None:
self.garchig = garchig
self.zokhiolch = zokhiolch
nom = Nom("Монголын нууц товчоо", "Тодорхойгүй")
print(nom)<__main__.Nom object at 0x000001A2B3C4D5E0>Энэ юу вэ?
Ямар ч хүн үүнийг харахад ойлгохгүй. Тэнд номын гарчиг ч алга, зохиолч ч алга. Зөвхөн class-ын нэр, дараа нь санах ойн хаяг — програмчлалын дотоод хог.
Гэтэл Python-ы бусад төрлүүд гоё харагддаг:
python
print("Болд")
print(42)
print([1, 2, 3])
print({"ner": "Болд"})Болд
42
[1, 2, 3]
{'ner': 'Болд'}Тэдгээр бүгд ойлгомжтой. Тэгвэл яагаад ТАНЫ class муухай харагдаж байна вэ?
Хариулт нь: Python нь таны объектыг ЯАЖ ХАРУУЛАХАА мэдэхгүй байна. Тэр нь Nom -ын дотор юу байгааг мэддэг ч, тэднийг ямар байдлаар текст болгохыг мэдэхгүй. Гарчиг эхэнд үү? Зохиолч эхэнд үү? Хашилттай юу? Хаалттай юу?
Тиймээс Python нь хамгийн аюулгүй зүйлийг хийж байна: "энэ бол Nom төрлийн объект, ийм хаяг дээр байна" гэж хэлээд өнгөрч байна.
Шийдэл нь: түүнд ХЭЛЖ ӨГӨХ.
Тэр нь __str__ гэдэг special method юм.
Эхлээд нэр томьёогоо тодруулъя.
Special method гэдэг нь нэр нь давхар доогуур зураасаар эхэлж, төгсдөг method юм: __init__, __str__, __eq__, __len__.
Программистууд тэднийг ихэвчлэн "dunder method" гэж нэрлэдэг — "double underscore" гэсэн үгсийн товчлол. Та documentation, форум, бусдын кодод "dunder" гэсэн үг харах магадлалтай тул мэдэж байгаарай.
Special method-ын ГОЛ ОНЦЛОГ нь: тэдгээрийг та ШУУД дуудахгүй. Тэдгээр нь ТОДОРХОЙ НӨХЦӨЛД Python -оор АВТОМАТААР дуудагддаг.
Та үүнийг аль хэдийн мэднэ! __init__ -ыг санаж байна уу?
python
nom = Nom("Монголын нууц товчоо", "Тодорхойгүй")Та nom.__init__(...) гэж бичээгүй. Гэвч __init__ ажилласан. Объект үүсэх үед Python түүнийг өөрөө дуудсан.
__str__ нь яг ижил зарчмаар ажиллана: print(objekt) дуудахад Python нь objekt.__str__() -ыг өөрөө дуудна.
Special method-ууд бол Python-той харилцах гэрээ юм. Та тодорхой нэртэй method бичвэл, Python нь тодорхой нөхцөлд түүнийг ашиглана. Энэ бол Python-ы хамгийн гоё загваруудын нэг.
Хурал дээр байна гэж төсөөлье. Хэн нэгэн танаас "энэ хүн хэн бэ?" гэж асуув.
Хэрэв та тэр хүнийг мэдэхгүй бол — та зөвхөн ерөнхий зүйл хэлж чадна: "тэр бол хүн, тэнд зогсож байна." Ашиггүй, гэхдээ үнэн.
Тэр бол Python-ы анхдагч зан төлөв: <__main__.Nom object at 0x...> — "энэ бол Nom, тэнд байна."
Гэвч хэрэв тэр хүн өөрөө өөрийгөө танилцуулж чаддаг бол — "Сайн байна уу, би Болд, багш" — тэгвэл бүх зүйл өөрчлөгдөнө.
__str__ бол объектын өөрийгөө танилцуулах арга юм.
python
class Nom:
def __init__(self, garchig: str, zokhiolch: str, on: int) -> None:
self.garchig = garchig
self.zokhiolch = zokhiolch
self.on = on
def __str__(self) -> str:
return f"{self.garchig} — {self.zokhiolch} ({self.on})"
nom = Nom("Монголын нууц товчоо", "Тодорхойгүй", 1240)
print(nom)Монголын нууц товчоо — Тодорхойгүй (1240)Гоё боллоо.
__str__ -ийг задалъя.
Нэр: __str__ — яг ийм, давхар доогуур зураас хоёр талд нь.
Параметр: зөвхөн self. Өөр аргумент байхгүй.
Буцаах төрөл: -> str. ЗААВАЛ string байх ёстой. Хэрэв өөр зүйл буцаавал алдаа гарна (доор үзнэ).
Та түүнийг ШУУД дуудаагүй. print(nom) гэж бичсэн, print(nom.__str__()) ГЭЖ БИЧЭЭГҮЙ. Python нь тэр холбоосыг өөрөө хийсэн.
__str__ нь print() -ээс илүү олон газарт автоматаар дуудагддаг.
python
nom = Nom("Монголын нууц товчоо", "Тодорхойгүй", 1240)
print(nom) # 1. print()
print(str(nom)) # 2. str() функц
print(f"Ном: {nom}") # 3. f-stringМонголын нууц товчоо — Тодорхойгүй (1240)
Монголын нууц товчоо — Тодорхойгүй (1240)
Ном: Монголын нууц товчоо — Тодорхойгүй (1240)Гурвуулаа ижил. Учир нь гурвуулаа "энэ объектыг текст болго" гэсэн санаатай.
print(nom) — Python нь дотроо str(nom) дуудна
str(nom) — Python нь nom.__str__() дуудна
f"{nom}" — мөн адил
Тэгэхээр __str__ -ыг НЭГ л удаа бичвэл, тэр нь гурван газарт ажиллана. Энэ бол special method-ын гоо сайхан.
Одоо номын сангийн class-уудад __str__ нэмье.
python
class Zuil:
def __init__(self, garchig: str, on: int) -> None:
self.garchig = garchig
self.on = on
self.zeeldsen = False
def baidal(self) -> str:
return "зээлсэн" if self.zeeldsen else "боломжтой"
def __str__(self) -> str:
return f"{self.garchig} ({self.on}) — {self.baidal()}"
class Nom(Zuil):
def __init__(self, garchig: str, on: int, zokhiolch: str) -> None:
super().__init__(garchig, on)
self.zokhiolch = zokhiolch
def __str__(self) -> str:
return f"{super().__str__()} | Зохиолч: {self.zokhiolch}"
class DVD(Zuil):
def __init__(self, garchig: str, on: int, minut: int) -> None:
super().__init__(garchig, on)
self.minut = minut
def __str__(self) -> str:
return f"{super().__str__()} | {self.minut} минут"
nomiin_san: list[Zuil] = [
Nom("Монголын нууц товчоо", 1240, "Тодорхойгүй"),
DVD("Аав", 2019, 95),
]
nomiin_san[0].zeeldekh = True # түр зуурын туршилт
for zuil in nomiin_san:
print(zuil)Түр зогсъё — дээрх мөр буруу байна. zeeldekh бол method, түүнд True олгож болохгүй. Зөв нь nomiin_san[0].zeeldsen = True (талбар). Эсвэл method-оор нь дуудна.
Зөв кодыг ажиллуулъя:
python
nomiin_san: list[Zuil] = [
Nom("Монголын нууц товчоо", 1240, "Тодорхойгүй"),
DVD("Аав", 2019, 95),
]
nomiin_san[0].zeeldsen = True
for zuil in nomiin_san:
print(zuil)Монголын нууц товчоо (1240) — зээлсэн | Зохиолч: Тодорхойгүй
Аав (2019) — боломжтой | 95 минутХоёр зүйлийг анзаараарай.
Нэгдүгээрт, __str__ нь OVERRIDE хийгдэж чадна. Бүлэг 7.3-ын method override нь __str__ -д ч хамаарна. Nom.__str__ нь Zuil.__str__ -ыг дарж бичсэн.
Хоёрдугаарт, super().__str__() ажиллаж байна. Хүү class нь эцгийн __str__ -ыг дуудаж, түүн дээр нэмж байна. Яг Бүлэг 7.3-ын "солих биш, өргөтгөх" хэв маяг.
Гуравдугаарт, loop нь print(zuil) гэж НЭГ л удаа бичсэн. Polymorphism нь __str__ -тэй ч ажиллаж байна — объектын жинхэнэ төрлөөс хамааран өөр өөр текст гарч байна.
Одоо нэг гэнэтийн зүйлийг харъя.
python
nomiin_san: list[Zuil] = [
Nom("Монголын нууц товчоо", 1240, "Тодорхойгүй"),
DVD("Аав", 2019, 95),
]
print(nomiin_san)[<__main__.Nom object at 0x000001A2B3C4D5E0>, <__main__.DVD object at 0x000001A2B3C4D6F0>]Муухай зүйл БУЦАЖ ИРЛЭЭ.
Гэтэл бид __str__ бичсэн шүү дээ! Яагаад ажиллахгүй байна вэ?
Учир нь print(jagsaalt) нь жагсаалт ДОТОРХ элементүүд дээр __str__ -ыг дуудахгүй. Тэр нь өөр method — __repr__ -ыг дууддаг.
Энэ нь эхэндээ утгагүй мэт санагдана. Тэгвэл яагаад Python-д ХОЁР ижил төстэй method байдаг вэ?
Тэдгээр нь ХОЁР ӨӨР асуултад хариулж байна.
__str__ нь ХЭРЭГЛЭГЧИД зориулагдсан. Асуулт: "энэ объектыг хүн уншихад ямар харагдах ёстой вэ?" Гоё, цэвэрхэн, ойлгомжтой.
__repr__ нь ПРОГРАММИСТЫГ зориулагдсан. Асуулт: "энэ объектыг ЯАЖ ҮҮСГЭСЭН бэ? Түүний бүх өгөгдөл нь юу вэ?" Нарийн, бүрэн, техникийн.
Жишээгээр харъя. Python-ы өөрийн datetime төрөл:
python
from datetime import datetime
odoo = datetime(2026, 7, 13, 14, 30)
print(odoo) # __str__ — хүнд зориулсан
print(repr(odoo)) # __repr__ — программистад зориулсан2026-07-13 14:30:00
datetime.datetime(2026, 7, 13, 14, 30)Ялгааг мэдрэв үү?
__str__ нь уншихад гоё: 2026-07-13 14:30:00.
__repr__ нь кодыг харуулж байна: datetime.datetime(2026, 7, 13, 14, 30). Хэрэв та тэр мөрийг хуулж, кодод наавал — яг ижил объект үүснэ!
Энэ бол __repr__ -ийн ЗОРИЛГО: хэрэв боломжтой бол, объектыг дахин үүсгэх кодыг харуулах.
Одоо яагаад жагсаалт __repr__ ашигладаг нь тодорхой боллоо. Жагсаалт хэвлэх нь ихэвчлэн дебаг хийх зорилготой байдаг — та юу байгааг ХАРАХ гэж байна. Тэр үед техникийн нарийвчлал илүү ашигтай.
python
class Nom:
def __init__(self, garchig: str, zokhiolch: str, on: int) -> None:
self.garchig = garchig
self.zokhiolch = zokhiolch
self.on = on
def __str__(self) -> str:
return f"{self.garchig} — {self.zokhiolch} ({self.on})"
def __repr__(self) -> str:
return f"Nom(garchig={self.garchig!r}, zokhiolch={self.zokhiolch!r}, on={self.on})"
nom = Nom("Монголын нууц товчоо", "Тодорхойгүй", 1240)
print(nom)
print(repr(nom))
print([nom])Монголын нууц товчоо — Тодорхойгүй (1240)
Nom(garchig='Монголын нууц товчоо', zokhiolch='Тодорхойгүй', on=1240)
[Nom(garchig='Монголын нууц товчоо', zokhiolch='Тодорхойгүй', on=1240)]Жагсаалт ч гоё харагдаж байна.
__repr__ -ийг задалъя.
Формат нь: ClassNer(talbar=utga, talbar=utga). Энэ бол дэлхий нийтийн заншил. Хэрэв та тэр string-ийг хуулж, Python-д наавал — яг ижил объект үүснэ.
{self.garchig!r} — энэ бол шинэ зүйл. !r гэсэн тэмдэг нь f-string-д "энэ утгыг repr() -ээр форматла" гэсэн заавар.
Ялгааг харъя:
python
garchig = "Монголын нууц товчоо"
print(f"{garchig}") # __str__ хэрэглэнэ
print(f"{garchig!r}") # __repr__ хэрэглэнэМонголын нууц товчоо
'Монголын нууц товчоо'!r нь ХАШИЛТ нэмж байна.
Яагаад чухал вэ? Учир нь __repr__ нь "объектыг дахин үүсгэх код" харуулах ёстой. Кодод string нь ХАШИЛТТАЙ бичигддэг:
python
nom = Nom(garchig='Монголын нууц товчоо', ...) # хашилттай — ЗӨВ
nom = Nom(garchig=Монголын нууц товчоо, ...) # хашилтгүй — АЛДААТиймээс __repr__ дотор string талбар бүрд !r бич. Тоонд хэрэггүй (тоо нь хашилтгүй бичигддэг).
Одоо хамгийн чухал практик зөвлөгөө.
__repr__ -ыг ҮРГЭЛЖ бич. Тэр нь дебаг хийхэд, жагсаалт хэвлэхэд, алдаа хайхад зайлшгүй хэрэгтэй.
__str__ -ыг ЗӨВХӨН хэрэгтэй үед бич. Хэрэв объектыг хэрэглэгчид харуулах тусгай хэлбэр хэрэгтэй бол.
Яагаад ийм дараалал вэ? Учир нь __str__ байхгүй бол Python нь __repr__ -ыг ашигладаг!
python
class Nom:
def __init__(self, garchig: str, on: int) -> None:
self.garchig = garchig
self.on = on
def __repr__(self) -> str:
return f"Nom(garchig={self.garchig!r}, on={self.on})"
# __str__ БИЧЭЭГҮЙ
nom = Nom("Монголын нууц товчоо", 1240)
print(nom) # __str__ байхгүй → __repr__ хэрэглэнэ
print([nom])Nom(garchig='Монголын нууц товчоо', on=1240)
[Nom(garchig='Монголын нууц товчоо', on=1240)]Хоёулаа ажиллалаа. __str__ байхгүй тул Python нь __repr__ руу шилжсэн.
Гэхдээ эсрэгээр НЭ ажиллана:
python
class Nom:
def __init__(self, garchig: str, on: int) -> None:
self.garchig = garchig
self.on = on
def __str__(self) -> str:
return f"{self.garchig} ({self.on})"
# __repr__ БИЧЭЭГҮЙ
nom = Nom("Монголын нууц товчоо", 1240)
print(nom) # __str__ ажиллана
print([nom]) # __repr__ БАЙХГҮЙ → анхдагч муухай зүйлМонголын нууц товчоо (1240)
[<__main__.Nom object at 0x000001A2B3C4D5E0>]Жагсаалт дахин муухай.
Тэгэхээр:
Хэрэв ЗӨВХӨН НЭГИЙГ бичих гэж байгаа бол —
__repr__бич. Тэр нь хоёулангийнх нь үүргийг гүйцэтгэнэ.
Практикт мэргэжлийн код нь ихэвчлэн ХОЁУЛАНГ НЬ бичдэг:
__repr__ — техникийн, бүрэн, Nom(garchig='...', on=1240)
__str__ — гоё, хэрэглэгчид зориулсан, Монголын нууц товчоо (1240)
Одоо __repr__ -ийн жинхэнэ ашиг тусыг харуулъя.
python
class Zarlaga:
def __init__(self, ner: str, dun: int, angilal: str) -> None:
self.ner = ner
self.dun = dun
self.angilal = angilal
def __repr__(self) -> str:
return f"Zarlaga(ner={self.ner!r}, dun={self.dun}, angilal={self.angilal!r})"
def __str__(self) -> str:
return f"{self.ner}: {self.dun:,} төгрөг ({self.angilal})"
zarlaguud = [
Zarlaga("Ном", 15000, "боловсрол"),
Zarlaga("Автобус", 1000, "тээвэр"),
Zarlaga("Хоол", 8500, "хоол"),
]
# Хэрэглэгчид харуулах
print("ЗАРЛАГУУД:")
for z in zarlaguud:
print(f" {z}")
print()
# Дебаг хийх
print("ДЕБАГ:")
print(zarlaguud)ЗАРЛАГУУД:
Ном: 15,000 төгрөг (боловсрол)
Автобус: 1,000 төгрөг (тээвэр)
Хоол: 8,500 төгрөг (хоол)
ДЕБАГ:
[Zarlaga(ner='Ном', dun=15000, angilal='боловсрол'), Zarlaga(ner='Автобус', dun=1000, angilal='тээвэр'), Zarlaga(ner='Хоол', dun=8500, angilal='хоол')]Хоёр өөр зорилго, хоёр өөр гаралт.
Дээд хэсэг нь тайланд тохирно — цэвэрхэн, уншихад тааламжтай.
Доод хэсэг нь дебагт тохирно — бүх өгөгдөл харагдаж байна, ямар ч зүйл нуугдаагүй.
Хэрэв программ буруу ажиллаж байвал та зөвхөн print(zarlaguud) гэж бичээд, БҮХ өгөгдлийг нэг дор харна. Энэ бол алдаа хайхад маш их цаг хэмнэдэг.
Бүлэг 6-ийн зарлагын бүртгэлийг санаж байна уу? Тэнд бид dictionary ашигласан:
python
{"ner": "Ном", "dun": 15000, "angilal": "боловсрол"}Одоо class ашиглавал:
python
Zarlaga(ner='Ном', dun=15000, angilal='боловсрол')Бараг ижил харагдаж байна! Гэхдээ class нь ХАМААГҮЙ илүү: type hint нь тодорхой (z.dun нь ҮРГЭЛЖ int), method нэмж болно, editor нь бүрэн тусална.
Бүлэг 9-д dataclass ашиглавал энэ бүх __init__, __repr__ -ыг АВТОМАТААР авна — та зөвхөн талбаруудыг зарлана. Тэр нь энэ бүлгийн эцсийн шагнал юм.
Хэрэв сонирхвол: өөрийн class бичээд __str__ -гүйгээр print() дуудаж, муухай гаралтыг өөрийн нүдээр хараарай. Дараа нь __str__ нэмээд, ялгааг мэдрээрэй. Дараа нь print([objekt]) гэж жагсаалт хэвлээд, муухай зүйл дахин гарч ирэхийг харж, __repr__ нэмээд засаарай.
Тэр гурван алхам нь энэ хичээлийн бүх агуулгыг бататгана.
Мөн хүсвэл: Python-ы built-in төрлүүд дээр str() ба repr() -ийг харьцуулж үзээрэй.
python
print(str("Болд"), repr("Болд"))
print(str(42), repr(42))
print(str([1, 2]), repr([1, 2]))String дээр ялгаа гарна (хашилт), тоо дээр гарахгүй.
1. String биш зүйл буцаах
python
class Nom:
def __str__(self) -> str:
return self.on # тоо буцаалаа!Traceback (most recent call last):
File "main.py", line 8, in <module>
print(nom)
TypeError: __str__ returned non-string (type int)__str__ returned non-string (type int) гэдэг нь "__str__ нь string биш зүйл буцаалаа" гэсэн үг. __str__ ба __repr__ нь ЗААВАЛ string буцаах ёстой. Засвар: return str(self.on) эсвэл f-string ашигла.
2. print -ыг __str__ дотор дуудах
python
class Nom:
def __str__(self) -> str:
print(f"{self.garchig}") # print биш, return!
return ""Ажиллана, гэхдээ буруу. __str__ нь string БУЦААХ ёстой, хэвлэх ёсгүй. Хэвлэх ажлыг print() өөрөө хийнэ.
3. Нэрийг буруу бичих
python
class Nom:
def __st__(self) -> str: # __str__ биш!
return f"{self.garchig}"Алдаа гарахгүй. Гэхдээ print(nom) нь ДАХИН муухай зүйл хэвлэнэ, учир нь Python нь __str__ -ыг хайж, олохгүй байна. Бүлэг 7.3-ын "нэр буруу бичих" алдаа дахин гарч ирлээ. Нэрийг АНХААРАЛТАЙ бич: давхар доогуур зураас хоёр талд нь.
4. Ганц доогуур зураас бичих
python
def _str_(self) -> str: # ганц зураас — БУРУУ__str__ гэж ХОЁР зураас, хоёр талд нь. _str_ нь энгийн method болж үлдэнэ, хэзээ ч автоматаар дуудагдахгүй.
5. __repr__ -д !r мартах
python
def __repr__(self) -> str:
return f"Nom(garchig={self.garchig}, on={self.on})"Nom(garchig=Монголын нууц товчоо, on=1240)Хашилт байхгүй. Хэрэв та энэ мөрийг хуулж, кодод наавал — АЛДАА гарна. String талбарт !r бич.
6. __str__ -ыг ШУУД дуудах
python
print(nom.__str__())Ажиллана, гэхдээ БУРУУ ЗУРШИЛ. Special method-ыг шууд дуудах ёсгүй. print(nom) эсвэл str(nom) гэж бич — Python нь холбоосыг өөрөө хийнэ.
Special method (эсвэл dunder method) нь давхар доогуур зураастай нэртэй: __init__, __str__, __repr__.
Тэдгээрийг ШУУД дуудахгүй. Python нь ТОДОРХОЙ НӨХЦӨЛД тэднийг АВТОМАТААР дуудна.
Анхдагчаар print(objekt) нь <__main__.Nom object at 0x...> гэсэн муухай зүйл хэвлэдэг — учир нь Python нь таны объектыг яаж харуулахаа мэдэхгүй.
__str__(self) -> str — ХЭРЭГЛЭГЧИД зориулсан, гоё, уншихад тааламжтай. print(), str(), f-string дээр дуудагдана.
__repr__(self) -> str — ПРОГРАММИСТАД зориулсан, техникийн, бүрэн. Жагсаалт, dictionary доторх объектууд дээр дуудагдана, мөн repr() дээр.
__repr__ -ийн заншил: ClassNer(talbar=utga, talbar=utga) — объектыг ДАХИН ҮҮСГЭХ код.
!r нь f-string-д "repr() ашигла" гэсэн заавар — string-д ХАШИЛТ нэмнэ. __repr__ дотор string талбар бүрд бич.
__str__ байхгүй бол Python нь __repr__ -ыг ашиглана. Эсрэгээр НЭ ажиллана.
Тиймээс: зөвхөн НЭГИЙГ бичих гэж байгаа бол — __repr__ бич.
Хоёулаа override хийгдэж чадна (Бүлэг 7.3), мөн super().__str__() ажиллана.
Хоёулаа ЗААВАЛ string буцаах ёстой → эс тэгвээс TypeError: __str__ returned non-string.
Дараагийн хичээлд бид __eq__ -ыг үзнэ. Асуулт нь: хоёр объект ХЭЗЭЭ тэнцүү вэ? Анхдагчаар Python нь хоёр объектыг зөвхөн тэдгээр нь ЯГ ИЖИЛ объект байвал л тэнцүү гэж үздэг — хэдийгээр тэдний бүх талбар ижил байсан ч. Тэр нь ихэвчлэн бидний хүссэн зүйл БИШ. __eq__ нь "тэнцүү" гэдэг нь юу гэсэн үг болохыг ӨӨРӨӨ тодорхойлох боломж өгдөг.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.