Ачааллж байна...
Өмнөх хичээлийн төгсгөлд бид нэг шийдэгдээгүй асуудал үлдээсэн. Хүү class-д өөрийн __init__ бичихэд эцгийн __init__ нь ОГТ ажиллахаа больж, бүх өвлөсөн талбарууд алга болсон:
AttributeError: 'Nom' object has no attribute 'garchig'Энэ асуудал нь inheritance-ийг бараг ашиггүй болгож байна. Учир нь бодит амьдрал дээр хүү class нь БАРАГ ҮРГЭЛЖ өөрийн талбартай байдаг. Ном нь зохиолчтой, сэтгүүл нь дугаартай, DVD нь үргэлжлэх хугацаатай. Хэрэв өөрийн талбар нэмэхэд эцгийн бүх зүйл алга болдог бол — inheritance юунд хэрэгтэй юм бэ?
Шийдэл нь super() гэдэг built-in функц юм. Тэр нь хүү class-д "эцгийнхээ __init__ -ийг эхлээд ажиллуул" гэж хэлэх боломж олгодог.
Энэ хичээл богино боловч маш чухал. super() -гүйгээр inheritance нь бараг ямар ч практик утгагүй.
Байшин барих гэж төсөөлье.
Эцэг class нь суурь барьдаг гэж бодоорой: газраа ухаж, бетон цутгаж, ханын суурь тавина. Тэр ажил бүх байшинд ижилхэн.
Хүү class нь тэр суурь дээр өөрийн байшингаа барина: ханыг өөрөөр босгож, өөр дээвэр тавьж, өөр өнгө будна.
Одоо асуулт: хүү class нь ажлаа эхлэхдээ юу хийх ёстой вэ?
Буруу арга: суурийг үл тоомсорлож, шууд хана босгож эхлэх. Үр дүн: хана нь агаарт дүүжлэгдэнэ, байшин нурна. Энэ бол super() мартах явдал.
Зөв арга: эхлээд "эцэг ээ, суурийг чинь тавь" гэж хэлэх. Суурь бэлэн болсны дараа өөрийн ажлаа эхлүүлэх.
super().__init__(...) бол яг тэр дуудлага юм. Тэр нь эцгийн бүтээх ажлыг эхлээд гүйцэтгүүлж, дараа нь хүү өөрийн зүйлээ нэмнэ.
Эхлээд асуудлыг дахин харъя.
python
class Zuil:
def __init__(self, garchig: str, on: int) -> None:
self.garchig = garchig
self.on = on
self.zeeldsen = False
class Nom(Zuil):
def __init__(self, garchig: str, on: int, zokhiolch: str) -> None:
self.zokhiolch = zokhiolch
nom = Nom("Монголын нууц товчоо", 1240, "Тодорхойгүй")
print(nom.zokhiolch)
print(nom.garchig)Тодорхойгүй
Traceback (most recent call last):
File "main.py", line 12, in <module>
print(nom.garchig)
AttributeError: 'Nom' object has no attribute 'garchig'Nom.__init__ нь Zuil.__init__ -ийг бүрэн дарж бичсэн. Объект үүсэх үед зөвхөн Nom.__init__ ажиллаж, self.zokhiolch л үүссэн. self.garchig, self.on, self.zeeldsen нь хэзээ ч үүсээгүй.
Одоо super() -ээр засъя:
python
class Zuil:
def __init__(self, garchig: str, on: int) -> None:
self.garchig = garchig
self.on = on
self.zeeldsen = False
class Nom(Zuil):
def __init__(self, garchig: str, on: int, zokhiolch: str) -> None:
super().__init__(garchig, on)
self.zokhiolch = zokhiolch
nom = Nom("Монголын нууц товчоо", 1240, "Тодорхойгүй")
print(nom.garchig)
print(nom.on)
print(nom.zeeldsen)
print(nom.zokhiolch)Монголын нууц товчоо
1240
False
ТодорхойгүйБүх зүйл ажиллаж байна.
super().__init__(garchig, on) гэсэн мөрийг задалъя. Энэ бол хичээлийн зүрх.
super() гэдэг нь "миний ЭЦЭГ class" гэсэн утгатай. Энэ тохиолдолд Zuil.
.__init__(garchig, on) нь тэр эцэг class-ын __init__ -ийг дуудаж байна, түүнд хоёр аргумент дамжуулж.
Тэгэхээр бүхэл мөр нь: "Zuil-ийн __init__ -ийг ажиллуул, түүнд garchig ба on дамжуул."
Тэр дуудалт ажиллахад Zuil.__init__ нь self.garchig, self.on, self.zeeldsen -ийг үүсгэнэ. Дараа нь буцаж ирээд Nom.__init__ нь өөрийн self.zokhiolch -ыг нэмнэ.
Үр дүн: объект нь ДӨРВӨН талбартай — гурав нь эцгээс, нэг нь өөрөөс.
super().__init__(...) нь ХАМГИЙН ЭХЭНД байх ёстой. Яагаад?
python
class Nom(Zuil):
def __init__(self, garchig: str, on: int, zokhiolch: str) -> None:
self.zokhiolch = zokhiolch
super().__init__(garchig, on) # хамгийн сүүлдЭнэ тохиолдолд ажиллана — учир нь эдгээр талбарууд хоорондоо хамааралгүй.
Гэвч дараах тохиолдолд асуудал үүснэ:
python
class Nom(Zuil):
def __init__(self, garchig: str, on: int, zokhiolch: str) -> None:
self.tanilstsuulga = f"{self.garchig} — {zokhiolch}" # garchig хараахан алга!
super().__init__(garchig, on)
self.zokhiolch = zokhiolchTraceback (most recent call last):
File "main.py", line 12, in <module>
nom = Nom("Монголын нууц товчоо", 1240, "Тодорхойгүй")
File "main.py", line 8, in __init__
self.tanilstsuulga = f"{self.garchig} — {zokhiolch}"
AttributeError: 'Nom' object has no attribute 'garchig'self.garchig -ыг ашиглах гэж оролдсон, гэвч тэр хараахан үүсээгүй — super().__init__(...) нь ДАРАА нь дуудагдаж байна.
Дүрэм: super().__init__(...) -ыг ХАМГИЙН ЭХЭНД бич. Дараа нь эцгийн бүх талбар бэлэн болсон тул та тэднийг чөлөөтэй ашиглаж чадна.
Байшингийн зүйрлэл дахин ажиллаж байна: суурийг ЭХЛЭЭД тавь, дараа нь түүн дээр бар.
Одоо бүх номын сангийн class-уудыг зөв бичье.
python
class Zuil:
def __init__(self, garchig: str, on: int) -> None:
self.garchig = garchig
self.on = on
self.zeeldsen = False
def zeeldekh(self) -> bool:
if self.zeeldsen:
return False
self.zeeldsen = True
return True
def butsaakh(self) -> None:
self.zeeldsen = False
def baidal(self) -> str:
if self.zeeldsen:
return "зээлсэн"
return "боломжтой"
class Nom(Zuil):
def __init__(self, garchig: str, on: int, zokhiolch: str, khuudas: int) -> None:
super().__init__(garchig, on)
self.zokhiolch = zokhiolch
self.khuudas = khuudas
class Setguul(Zuil):
def __init__(self, garchig: str, on: int, dugaar: int) -> None:
super().__init__(garchig, on)
self.dugaar = dugaar
class DVD(Zuil):
def __init__(self, garchig: str, on: int, minut: int) -> None:
super().__init__(garchig, on)
self.minut = minutГурван хүү class бүгд ижил бүтэцтэй:
Өөрийн __init__ -д БҮХ параметрийг (эцгийнх + өөрийнх) хүлээж авна
super().__init__(...) -ээр эцгийн хэсгийг дамжуулна
Өөрийн талбаруудыг нэмнэ
Туршиж үзье:
python
nom = Nom("Монголын нууц товчоо", 1240, "Тодорхойгүй", 320)
setguul = Setguul("Шинжлэх ухаан", 2026, 7)
dvd = DVD("Аав", 2019, 95)
print(f"{nom.garchig}, {nom.zokhiolch}, {nom.khuudas} хуудас")
print(f"{setguul.garchig}, №{setguul.dugaar}")
print(f"{dvd.garchig}, {dvd.minut} минут")
print()
nom.zeeldekh()
print(f"{nom.garchig}: {nom.baidal()}")
print(f"{setguul.garchig}: {setguul.baidal()}")Монголын нууц товчоо, Тодорхойгүй, 320 хуудас
Шинжлэх ухаан, №7
Аав, 95 минут
Монголын нууц товчоо: зээлсэн
Шинжлэх ухаан: боломжтойГурван class бүгд өөрийн талбартай, БАС өвлөсөн method-уудтай. zeeldekh(), baidal() — эдгээрийг нэг ч удаа хуулаагүй, гэхдээ гурвуулаа ажиллаж байна.
Энэ бол inheritance-ийн жинхэнэ хүч: нийтлэг зүйл нэг газарт, өвөрмөц зүйл өөр өөрийн газарт.
python
nomiin_san: list[Zuil] = [
Nom("Монголын нууц товчоо", 1240, "Тодорхойгүй", 320),
Setguul("Шинжлэх ухаан", 2026, 7),
DVD("Аав", 2019, 95),
Nom("Цагаан хэрэм", 1971, "Б. Ринчен", 450),
]
nomiin_san[0].zeeldekh()
nomiin_san[2].zeeldekh()
for zuil in nomiin_san:
print(f"{zuil.garchig:<25} ({zuil.on}) {zuil.baidal()}")Монголын нууц товчоо (1240) зээлсэн
Шинжлэх ухаан (2026) боломжтой
Аав (2019) зээлсэн
Цагаан хэрэм (1971) боломжтойLoop нь дөрвөн ӨӨР төрлийн объект дээр ажиллаж байна, гэхдээ тэдгээрийн ялгааг мэдэх шаардлагагүй. Тэр зөвхөн garchig, on, baidal() ашиглаж байна — эдгээр нь Zuil -д байгаа тул бүх үр садад БАЙХ баталгаатай.
Бүлэг 3.2-ын keyword argument энд ч ажиллана:
python
class Nom(Zuil):
def __init__(self, garchig: str, on: int, zokhiolch: str, khuudas: int) -> None:
super().__init__(garchig=garchig, on=on)
self.zokhiolch = zokhiolch
self.khuudas = khuudasИжил үр дүн, гэхдээ илүү тод. Параметр олон бол keyword ашиглах нь дээр — учир нь тэр нь юу хаана очиж байгааг ХЭЛЖ байна.
Дашрамд, default утга (Бүлэг 3.1) ч ажиллана:
python
class Zuil:
def __init__(self, garchig: str, on: int = 0) -> None:
self.garchig = garchig
self.on = on
self.zeeldsen = False
class Nom(Zuil):
def __init__(
self, garchig: str, zokhiolch: str, on: int = 0, khuudas: int = 0
) -> None:
super().__init__(garchig, on)
self.zokhiolch = zokhiolch
self.khuudas = khuudas
nom = Nom("Шинэ ном", "Зохиолч")
print(f"{nom.garchig}, {nom.on} он, {nom.khuudas} хуудас")Шинэ ном, 0 он, 0 хуудасDefault утгын дүрэм хэвээрээ: default-гүй параметрүүд эхэнд, default-тай нь араас.
Inheritance нь ХОЁР түвшингээр хязгаарлагдахгүй. Хүү class нь өөрөө эцэг болж чадна.
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 "боломжтой"
class Nom(Zuil):
def __init__(self, garchig: str, on: int, zokhiolch: str) -> None:
super().__init__(garchig, on)
self.zokhiolch = zokhiolch
class AudioNom(Nom):
def __init__(
self, garchig: str, on: int, zokhiolch: str, unshigch: str, minut: int
) -> None:
super().__init__(garchig, on, zokhiolch)
self.unshigch = unshigch
self.minut = minut
audio = AudioNom("Монголын нууц товчоо", 1240, "Тодорхойгүй", "Б. Батаа", 480)
print(audio.garchig) # Zuil-ээс
print(audio.zokhiolch) # Nom-оос
print(audio.unshigch) # AudioNom-оос
print(audio.baidal()) # Zuil-ээсМонголын нууц товчоо
Тодорхойгүй
Б. Батаа
боломжтойГурван түвшний өвлөлт. AudioNom → Nom → Zuil.
AudioNom.__init__ дотор super() нь Nom -ыг заана (шууд эцэг). Дараа нь Nom.__init__ дотор super() нь Zuil -ыг заана. Гинж ажиллаж байна.
Тэр дуудалтын гинжийг мөр мөрөөр нь дагая:
AudioNom("Монголын...", 1240, "Тодорхойгүй", "Б. Батаа", 480) дуудагдав
AudioNom.__init__ нь super().__init__("Монголын...", 1240, "Тодорхойгүй") гэж Nom -ыг дуудав
Nom.__init__ нь super().__init__("Монголын...", 1240) гэж Zuil -ыг дуудав
Zuil.__init__ нь garchig, on, zeeldsen -ыг үүсгэв
Буцаж ирээд Nom.__init__ нь zokhiolch -ыг нэмэв
Буцаж ирээд AudioNom.__init__ нь unshigch, minut -ыг нэмэв
Гүнээс гадагш барих — суурийн зүйрлэл дахин ажиллаж байна.
isinstance ч бас гинжийг дагана:
python
print(isinstance(audio, AudioNom))
print(isinstance(audio, Nom))
print(isinstance(audio, Zuil))True
True
TrueАудио ном бол ном мөн, ном бол зүйл мөн. Логик нь байгалийн.
Практик анхааруулга: гурав, дөрөв, тавны түвшний inheritance гинж нь маш хурдан ойлгомжгүй болдог. Хэрэв та гурваас олон түвшин барьж эхэлбэл — зогсоод бодоорой. Ихэнхдээ тэр нь дизайн буруу байгаагийн шинж юм. Бүлэг 7.4-т бид энэ асуудлыг шударгаар ярина.
super() гэж бичдэг вэ, Zuil.__init__ гэж болохгүй гэж үү?Сайн асуулт. Үнэндээ энэ ч бас ажиллана:
python
class Nom(Zuil):
def __init__(self, garchig: str, on: int, zokhiolch: str) -> None:
Zuil.__init__(self, garchig, on) # ажиллана, гэхдээ...
self.zokhiolch = zokhiolchАжиллана. Гэхдээ ЁСГҮЙ. Гурван шалтгаантай.
Шалтгаан 1: эцгийн нэрийг ДАВТАЖ бичиж байна. Хэрэв та ирээдүйд эцэг class-ыг өөрчилбөл (жишээ нь Zuil -ийг NomiinSangiinZuil болгож нэрлэвэл), энэ мөрийг ч бас засах хэрэгтэй. super() нь тэр асуудалгүй — тэр нь "миний эцэг, тэр нь хэн ч байсан" гэсэн утгатай.
Шалтгаан 2: self -ийг ГАРААР дамжуулж байна. Zuil.__init__(self, ...) гэж бичихэд self -ийг та өөрөө өгөх ёстой. super().__init__(...) -д тэр нь автоматаар явдаг. Нэг зүйл цөөн бичих, нэг алдааны эх үүсвэр цөөн.
Шалтгаан 3 (техникийн): олон эцэгтэй inheritance (multiple inheritance) -ийн үед super() нь зөв дарааллыг олдог, харин гараар дуудвал буруу ажиллаж болно. Олон эцэгтэй inheritance нь энэ курсийн хамрах хүрээнээс гадуур, гэхдээ super() -ийн зуршил нь тэр тохиолдолд ч зөв ажиллана.
Дүрэм: ҮРГЭЛЖ super() ашигла. Эцгийн нэрийг гараар бичих ёсгүй.
super() нь эцгийн ЯМАР Ч method -ыг дуудаж чадна, зөвхөн __init__ биш.
python
class Zuil:
def medeelel(self) -> str:
return f"{self.garchig} ({self.on})"
class Nom(Zuil):
def medeelel(self) -> str:
undsen = super().medeelel()
return f"{undsen} — {self.zokhiolch}"Энд Nom.medeelel нь эцгийн medeelel -ийг дуудаад, дараа нь түүн дээр нэмж байна.
Гэхдээ энэ бол method override гэж нэрлэгддэг сэдэв бөгөөд түүнийг дараагийн хичээлд бүрэн үзнэ. Одоохондоо super() нь __init__ -ээс илүү өргөн хэрэглээтэй гэдгийг мэдэж байгаад өнгөрөөрэй.
Хэрэв сонирхвол: super().__init__(...) мөрийг зориудаар УСТГААД программаа ажиллуулж, AttributeError -ыг өөрийн нүдээр хараарай. Дараа нь буцааж нэмээд, ажиллахыг нь хараарай. Тэр хоёр туршилт нь super() -ийн үүргийг бүрэн ойлгуулна.
Мөн хүсвэл: super().__init__(...) -ыг функцийн ХАМГИЙН СҮҮЛД зөөгөөд, түүнээс өмнө self.garchig -ыг ашиглах гэж оролдоод үзээрэй. Дараалал яагаад чухал болохыг өөрийн нүдээр харна.
1. super().__init__() -ыг мартах
python
class Nom(Zuil):
def __init__(self, garchig: str, on: int, zokhiolch: str) -> None:
self.zokhiolch = zokhiolchAttributeError: 'Nom' object has no attribute 'garchig'Эцгийн __init__ огт ажиллаагүй. Хамгийн түгээмэл алдаа. Хэрэв хүү class-д __init__ бичвэл — super().__init__(...) -ыг ЗААВАЛ дууд.
2. Аргумент дамжуулахаа мартах
python
class Nom(Zuil):
def __init__(self, garchig: str, on: int, zokhiolch: str) -> None:
super().__init__() # аргументгүй!
self.zokhiolch = zokhiolchTypeError: Zuil.__init__() missing 2 required positional arguments: 'garchig' and 'on'missing 2 required positional arguments — эцгийн __init__ нь хоёр аргумент шаарддаг. Тэднийг дамжуул: super().__init__(garchig, on).
3. self -ийг super() -д дамжуулах
python
super().__init__(self, garchig, on)TypeError: Zuil.__init__() takes 3 positional arguments but 4 were givensuper() нь self -ийг АВТОМАТААР дамжуулдаг. Гараар бичих ёсгүй. Зөв: super().__init__(garchig, on).
4. Дарааллыг буруу тавих
python
def __init__(self, garchig: str, on: int, zokhiolch: str) -> None:
self.tanilstsuulga = f"{self.garchig} — {zokhiolch}"
super().__init__(garchig, on)AttributeError: 'Nom' object has no attribute 'garchig'super().__init__(...) -ыг ХАМГИЙН ЭХЭНД бич. Дараа нь эцгийн бүх талбар бэлэн.
5. Эцэг class-ын нэрийг гараар бичих
python
Zuil.__init__(self, garchig, on)Ажиллана, гэхдээ буруу зуршил. super() ашигла.
6. Хүү class-д __init__ БИЧИХГҮЙ байх (энэ нь зөв!)
python
class Nom(Zuil):
passХэрэв хүү class-д НЭМЭЛТ талбар хэрэггүй бол — __init__ бичих ШААРДЛАГАГҮЙ. Эцгийнх нь автоматаар ажиллана. Хэрэггүй код бичих нь алдаа биш ч, хэрэггүй.
Хүү class-д өөрийн __init__ бичвэл, эцгийнх нь ОГТ ажиллахгүй → AttributeError.
super() нь "миний эцэг class" гэсэн утгатай. super().__init__(...) нь эцгийн __init__ -ийг ажиллуулна.
Стандарт хэв маяг:
python
class Nom(Zuil):
def __init__(self, garchig: str, on: int, zokhiolch: str) -> None:
super().__init__(garchig, on) # ЭХЛЭЭД эцэг
self.zokhiolch = zokhiolch # ДАРАА НЬ өөрийнхsuper().__init__(...) -ыг ХАМГИЙН ЭХЭНД бич. Дараа нь эцгийн бүх талбар бэлэн болсон тул тэднийг чөлөөтэй ашиглаж чадна.
self -ийг super() -д дамжуулах ЁСГҮЙ — тэр автоматаар явна.
Эцгийн нэрийг гараар бүү бич (Zuil.__init__(self, ...)). super() нь илүү аюулгүй, илүү уян хатан.
Inheritance нь олон түвшинтэй байж болно: AudioNom → Nom → Zuil. super() нь шууд эцгийг заана, гинж автоматаар ажиллана.
Гурваас олон түвшин бол дизайны асуудлын шинж. Бүлэг 7.4-т энэ талаар шударгаар ярина.
Хүү class-д НЭМЭЛТ талбар хэрэггүй бол — __init__ бичих ШААРДЛАГАГҮЙ.
super() нь ЯМАР Ч method -ыг дуудаж чадна, зөвхөн __init__ биш — тэр нь дараагийн хичээлийн сэдэв.
Дараагийн хичээлд бид method override -ийг үзнэ. Тэр нь хүү class нь эцгийн method-ыг ӨӨРӨӨР хэрэгжүүлэх боломж юм. Жишээ нь Zuil.medeelel() нь гарчиг, оныг харуулдаг байхад Nom.medeelel() нь зохиолчийг ч нэмж харуулна. Тэр үед нэг л дуудалт (zuil.medeelel()) объектын жинхэнэ төрлөөс хамааран ӨӨР ажиллана — polymorphism-ийн хамгийн хүчирхэг илрэл.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.