အခန်း 18 · animation 2 ခု

Chapter 17: OOP – Polymorphism (အသွင်အမျိုးမျိုး)

808 Coder မှ တင်ဆက်သည်

အရင်အခန်းမှာ child class က parent ဆီက အမွေရတာ သင်ခဲ့ပြီးပြီ။ အခု မေးခွန်းတစ်ခု — child က အမွေရထားတဲ့ method ကို သူ့ဘာသာ ပုံစံပြောင်းပြီး သုံးချင်ရင်ရော? ဥပမာ Animal ရဲ့ speak() က "အသံတစ်ခုခု" လို့ ယေဘုယျပြောတယ်။ Dog ကတော့ "Woof!" လို့ ဟောင်ချင်တယ်။ Cat ကတော့ "Meow!" လို့ အော်ချင်တယ်။

Method နာမည်တူကို object အမျိုးအစားအလိုက် ပုံစံအမျိုးမျိုး နဲ့ အပြုအမူပြတာ — ဒါပါပဲ Polymorphism။ ဂရိဘာသာစကားက ဆင်းသက်လာတာ — poly (အများ) + morph (ပုံစံ) = "many forms"။

ဒီအခန်းပြီးရင်...

  • Polymorphism ဆိုတာ ဘာလဲ ရှင်းပြနိုင်မယ်
  • Method overriding ကို စည်းမျဉ်းတွေနဲ့အညီ ရေးနိုင်မယ်
  • super() သုံးပြီး parent ရဲ့ method ကို ဆက်ခေါ်နိုင်မယ် (အစားထိုးတာမဟုတ်၊ ဆက်ဖြည့်တာ)
  • Python မှာ method overloading တိုက်ရိုက်မရတာ၊ ဘယ်လို အစားထိုးရေးရမလဲ သိမယ်
  • Duck typing ဆိုတာ ဘာလဲ၊ ဥပမာနဲ့ ပြနိုင်မယ်
  • Hospital Appointment System လို polymorphism သုံးထားတဲ့ program ရေးနိုင်မယ်

Polymorphism ဆိုတာဘာလဲ

Polymorphism ဆိုတာ မတူညီတဲ့ object တွေက method နာမည်တူတစ်ခုတည်းကို သူ့အမျိုးအစားနဲ့လိုက်တဲ့ ပုံစံအမျိုးမျိုးနဲ့ တုံ့ပြန်နိုင်တဲ့ စွမ်းရည်ပါ။ OOP ရဲ့ အခြေခံတိုင် ၄ ခုထဲက တစ်ခုဖြစ်တယ်။

လက်တွေ့ ဥပမာ — "play" ခလုတ်ကို စဉ်းစားကြည့်။ Music player မှာ နှိပ်ရင် သီချင်းဖွင့်တယ်။ Video player မှာ နှိပ်ရင် ဗီဒီယိုဖွင့်တယ်။ Game မှာ နှိပ်ရင် ဂိမ်းစတယ်။ ခလုတ်နာမည် ("play") တူတူကို နှိပ်တဲ့အရာ (object) အလိုက် အပြုအမူ မတူဘူး။ ဒါဟာ polymorphism ရဲ့ စိတ်ကူးပဲ။

Python မှာ polymorphism အဓိက ၂ မျိုး ရှိတယ် —

  • Method Overriding (runtime polymorphism) — ဒီအခန်းရဲ့ အဓိကအကြောင်း
  • Method Overloading — Python မှာ တိုက်ရိုက်မရ၊ အစားထိုးနည်း ရှိတယ်

Compile-time နဲ့ runtime ဆိုတဲ့ စကားလုံးတွေလည်း ကြားရလိမ့်မယ် — method overloading ကို "compile-time polymorphism" (code ရေးတဲ့အချိန်မှာ ဘယ် version သုံးမလဲ ဆုံးဖြတ်တာ)၊ method overriding ကို "runtime polymorphism" (program run နေတုန်း object အမျိုးအစားကြည့်ပြီး ဆုံးဖြတ်တာ) လို့ ခေါ်တယ်။ Python မှာ overloading တိုက်ရိုက်မရတာမို့ runtime polymorphism (overriding + duck typing) က အဓိကပဲ။

Reference

Polymorphism ကို နေ့စဉ်တွေ့နေရတာ

တကယ်တော့ polymorphism ကို class မရေးဘဲနေ့တိုင်း သုံးနေပြီးသား —

print(len("hello"))    # စာကြောင်း အရှည်
print(len([1, 2, 3]))  # list အရေအတွက်

print(5 + 3)           # ကိန်းပေါင်းခြင်း
print("Py" + "thon")   # စာကြောင်းဆက်ခြင်း
5
3
8
Python

len() တစ်ခုတည်းက str ရော list ရော လက်ခံတယ်။ + တစ်ခုတည်းက ကိန်းဂဏန်းရော စာကြောင်းရော ကိုင်တွယ်တယ်။ နာမည်တူ (သို့) သင်္ကေတတူကို အမျိုးအစားအလိုက် မတူညီစွာ အပြုအမူပြတာ — ဒါဟာ polymorphism ပဲ။ Method overriding က ဒီသဘောကို ကိုယ်တိုင် class တွေမှာ ဖန်တီးတဲ့ နည်းဖြစ်တယ်။

Method Overriding

Method overriding ဆိုတာ subclass က superclass မှာ ရှိပြီးသား method ကို ကိုယ့်ဘာသာ ပြန်ရေးပြီး အစားထိုးတာ။ Program run နေတုန်း ဘယ် version အလုပ်လုပ်မလဲ ဆုံးဖြတ်တာမို့ runtime polymorphism လို့လည်း ခေါ်တယ်။

စည်းမျဉ်း ၃ ခု ရှိတယ် —

  1. Method နာမည် တူရမယ်
  2. Method signature (parameter အရေအတွက်/အမျိုးအစား) တူသင့်တယ်
  3. Parent ရဲ့ အပြုအမူကို ပြုပြင်တာ (သို့) ဖြည့်စွက်တာ အတွက် သုံးတယ်
class Animal:
    def speak(self):
        print("Some animal sound")

class Dog(Animal):
    def speak(self):      # parent ရဲ့ speak() ကို override လုပ်တာ
        print("Woof woof!")

class Cat(Animal):
    def speak(self):      # ဒါလည်း override ပဲ
        print("Meow!")

animals = [Animal(), Dog(), Cat()]
for a in animals:
    a.speak()
Some animal sound
Woof woof!
Meow!

speak() နာမည်တူကို ၃ ကြိမ် ခေါ်တယ် — ဒါပေမယ့် object အမျိုးအစားအလိုက် ထွက်လာတဲ့ အသံ ၃ မျိုး ကွဲတယ်။ Loop ထဲမှာ a က ဘာအမျိုးအစားလဲ ဆိုတာကို Python က run-time မှာ ကြည့်ပြီး မှန်ကန်တဲ့ version ကို ရွေးပေးတယ်။ ဒါဟာ polymorphism ရဲ့ အလှပဲ — ခေါ်တဲ့သူက object အမျိုးအစား ခွဲခြားစရာ မလိုဘူး။ ဒီအခန်းမှာ ဒီသဘောကို ကိုယ့် class တွေမှာ ဘယ်လိုဖန်တီးမလဲ အဆင့်ဆင့် လေ့လာမယ်။

Same method name, different behavior speak() Animal "Some animal sound" Dog "Woof woof!" Cat "Meow!" polymorphism: one interface, many forms
speak() တစ်ခုတည်းက အပြုအမူ မတူညီပုံ (polymorphism)

သတိပြုရန်

Override လုပ်တဲ့အခါ method နာမည် စာလုံးပေါင်း မှန်ရမယ်။ speak ကို speek လို့ မှားရေးမိရင် override ဖြစ်မသွားဘဲ method အသစ်တစ်ခု ဖြစ်သွားမယ် — Python က အမှားလည်း ပြမှာ မဟုတ်ဘူး၊ တိတ်တိတ်လေး မှားနေမယ်။ ဒါကြောင့် စာလုံးပေါင်း သေချာစစ်ပါ။

Reference

အစားထိုးတာ vs ဆက်ဖြည့်တာ — ဘယ်ဟာရွေးမလဲ

နှိုင်းယှဉ်ကြည့်ရအောင် —

class Alarm:
    def ring(self):
        print("Ring ring!")

# ပုံစံ ၁: လုံးဝအစားထိုးတာ
class SilentAlarm(Alarm):
    def ring(self):
        print("(vibration only)")

# ပုံစံ ၂: ဆက်ဖြည့်တာ
class LoudAlarm(Alarm):
    def ring(self):
        super().ring()
        print("RING RING RING!")

SilentAlarm().ring()
LoudAlarm().ring()
(vibration only)
Ring ring!
RING RING RING!

Parent ရဲ့ အပြုအမူ လုံးဝ မလိုချင်ရင် ပုံစံ ၁၊ အခြေခံအပြုအမူကို ထိန်းပြီး ထပ်ဖြည့်ချင်ရင် ပုံစံ ၂ သုံးပါ။ ရွေးချယ်မှုက ရည်ရွယ်ချက်အပေါ် မူတည်တယ် — တစ်ခုမှ အမြဲမှန်/အမြဲမှား မရှိဘူး။ တစ်ခါတလေ တတိယနည်းလည်း သုံးလို့ရတယ် — အခြေအနေအလိုက် parent method ကို ရွေးခေါ်တာ (ဥပမာ — အချို့အခြေအနေမှာ super().ring() ခေါ်၊ အချို့မှာ မခေါ်ဘဲ ကိုယ့်ဘာသာ လုပ်တာ)။

super() နဲ့ Parent Method ကို ဆက်သုံးခြင်း

Overriding ဆိုတာ အစားထိုးတာ — ဒါပေမယ့် တခါတလေ parent ရဲ့ အလုပ်ကို လုံးဝစွန့်ပစ်ချင်တာ မဟုတ်ဘဲ၊ ဆက်ဖြည့်ချင်တာ။ အဲဒီအခါ super() သုံးပြီး parent ရဲ့ method ကို အရင် ခေါ်လိုက်တယ်၊ ပြီးမှ ကိုယ့်အပိုင်း ဆက်ရေးတယ်။

class Vehicle:
    def start(self):
        print("Vehicle engine starting...")

class Motorbike(Vehicle):
    def start(self):
        super().start()              # parent ရဲ့ start() ကို အရင်ခေါ်
        print("Motorbike is ready to ride!")

m = Motorbike()
m.start()
Vehicle engine starting...
Motorbike is ready to ride!

ဘဏ်ဥပမာနဲ့လည်း ကြည့်ရအောင် —

class BankAccount:
    def interest_rate(self):
        print("Base interest rate: 5%")

class SavingsAccount(BankAccount):
    def interest_rate(self):
        super().interest_rate()
        print("Savings bonus applied: total 7%")

acc = SavingsAccount()
acc.interest_rate()
Base interest rate: 5%
Savings bonus applied: total 7%

မှတ်ချက်

super().method() ကို မခေါ်ဘဲ method ကို လုံးဝ ပြန်ရေးလိုက်ရင် parent ရဲ့ အပြုအမူ ပျောက်သွားမယ်။ ခေါ်လိုက်ရင်တော့ "အမွေဆက်ခံ + ကိုယ်ပိုင်ဖြည့်စွက်" ဖြစ်သွားတယ်။ ဘယ်ဟာ လိုချင်လဲ ဆိုတာကို ရည်ရွယ်ချက်အလိုက် ရွေးပါ။

Reference

Method Overloading — Python မှာ တိုက်ရိုက်မရ

Java လို ဘာသာစကားတွေမှာ method overloading ရှိတယ် — နာမည်တူ method ကို parameter အရေအတွက် မတူညီစွာနဲ့ အများအပြား ရေးလို့ရတာ။ Python မှာတော့ ဒါ တိုက်ရိုက်မရပါဘူး။ နာမည်တူ method ကို နှစ်ခါ ရေးမိရင် နောက်ဆုံးရေးထားတဲ့ တစ်ခုကပဲ ကျန်မယ် (အရင်ဟာ ပျောက်သွားမယ်)။

ဒါပေမယ့် အစားထိုးနည်း ၂ ခု ရှိတယ် —

နည်း ၁: Default parameter သုံးတာ

class Calculator:
    def add(self, a, b=0, c=0):
        return a + b + c

calc = Calculator()
print(calc.add(5))        # 5
print(calc.add(5, 3))     # 8
print(calc.add(5, 3, 2))  # 10
5
8
10

Method တစ်ခုတည်းနဲ့ ခေါ်ပုံ ၃ မျိုး ရတယ် — overloading လိုပဲ အလုပ်လုပ်တယ်။

နည်း ၂: *args သုံးတာ

class Calculator:
    def add(self, *numbers):
        return sum(numbers)

calc = Calculator()
print(calc.add(5))
print(calc.add(5, 3))
print(calc.add(5, 3, 2, 10))
5
8
20

Parameter အရေအတွက် အကန့်အသတ်မရှိ လက်ခံတယ်။

မှတ်ချက်

Overriding (အမွေဆက်ခံထားတဲ့ method ကို ပြန်ရေးတာ) နဲ့ Overloading (နာမည်တူ method ကို parameter မတူညီစွာ အများအပြားရေးတာ) ကို မရောထွေးပါနဲ့။ Python မှာ overriding က native ရပြီး၊ overloading ကို အထက်ပါနည်းတွေနဲ့ အတုယူရတယ်။

Reference

Duck Typing

Python ရဲ့ နာမည်ကြီး အယူအဆတစ်ခု — duck typing။ ဆိုရိုးရှိတယ်: "If it walks like a duck and quacks like a duck, then it must be a duck." ဆိုလိုတာက — object တစ်ခုက ဘယ် class ကလဲ ဆိုတာ အရေးမကြီးဘူး၊ လိုတဲ့ method ပါရင် ရပြီ။

class BankAccount:
    def interest_rate(self):
        return 5

class SavingsAccount(BankAccount):
    def interest_rate(self):
        return 7

class FixedDeposit:          # BankAccount ကနေ အမွေမခံထားဘူး!
    def interest_rate(self):
        return 9

def show_interest(account):
    print(f"Interest rate: {account.interest_rate()}%")

show_interest(BankAccount())
show_interest(SavingsAccount())
show_interest(FixedDeposit())
Interest rate: 5%
Interest rate: 7%
Interest rate: 9%

FixedDeposit က BankAccount နဲ့ ဘာဆက်နွယ်မှုမှ မရှိဘူး — ဒါပေမယ့် interest_rate() method ပါလို့ show_interest() က လက်ခံတယ်။ Function က "နင်ဘယ်အမျိုးအစားလဲ" လို့ မမေးဘူး၊ "နင့်မှာ interest_rate() ရှိလား" လို့ပဲ စစ်တယ်။ ဒါဟာ polymorphism ရဲ့ အပျော့စား၊ Python ဆန်ဆန် ပုံစံပဲ။

သတိပြုရန်

Duck typing မှာ method နာမည် တူဖို့ အလွန်အရေးကြီးတယ်။ interest_rate() ရှိတဲ့ object ကို interestRate() လို့ ခေါ်မိတဲ့ function ထဲ ထည့်ရင် AttributeError တက်မယ်။

နောက်ထပ် ဥပမာတစ်ခု —

class Guitar:
    def play(self):
        print("Strumming the guitar...")

class Drum:
    def play(self):
        print("Beating the drum...")

def perform(instrument):
    instrument.play()

perform(Guitar())
perform(Drum())
Strumming the guitar...
Beating the drum...

Guitar နဲ့ Drum မှာ ဘုံအမွေ ဘာမှ မရှိဘူး — ဒါပေမယ့် play() ပါလို့ perform() က နှစ်ခုလုံး လက်ခံတယ်။ Duck typing က inheritance မလိုဘဲ polymorphism ရစေတယ်။ ဒါကြောင့် Python programmer တွေက "inheritance ထက် duck typing ကို ဦးစားပေး" လို့ ပြောလေ့ရှိတယ် — တကယ်လိုအပ်မှ inheritance သုံး၊ မလိုရင် method နာမည်တူရုံနဲ့ လုံလောက်တယ်။

Operator Overloading ကို အကျဉ်းချုပ်

Polymorphism ရဲ့ နောက်ထပ် မျက်နှာတစ်ခု — operator overloading။ +, -, == လို operator တွေကို ကိုယ့် class အတွက် အဓိပ္ပာယ်ဖွင့်ပေးတာ။ Python မှာ အထူး method (__add__, __str__ စတဲ့ double-underscore method) တွေ ရေးပြီး လုပ်တယ် —

class Money:
    def __init__(self, kyats):
        self.kyats = kyats

    def __add__(self, other):
        return Money(self.kyats + other.kyats)

    def __str__(self):
        return f"{self.kyats} kyats"

m1 = Money(5000)
m2 = Money(3000)
print(m1 + m2)
8000 kyats

m1 + m2 လို့ ရေးလိုက်တာနဲ့ Python က m1.__add__(m2) ကို အလိုအလျောက် ခေါ်တယ်။ print() က __str__ ကို ခေါ်တယ်။ __eq__, __lt__ စတာတွေလည်း ရှိသေးတယ် — ==, < တို့ကို ကိုယ့် class အတွက် အဓိပ္ပာယ်ဖွင့်ပေးနိုင်တယ်။ Operator တူတူကို object အမျိုးအစားအလိုက် မတူညီစွာ အပြုအမူပြတာ — polymorphism ပဲ။ ဒါကို အသေးစိတ် မသင်သေးဘူး၊ "ဒါလည်း polymorphism အမျိုးအစားပဲ" ဆိုတာ သိထားရုံပါပဲ။

အတွေ့ရများဆုံး override: __str__

လက်တွေ့မှာ အများဆုံး override လုပ်ရတဲ့ method က __str__ — object ကို print() နဲ့ ထုတ်တဲ့အခါ ဘယ်လိုပေါ်မလဲ သတ်မှတ်တာ —

class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def __str__(self):
        return f"{self.name} ({self.grade})"

s = Student("Aung Khant", 85)
print(s)
Aung Khant (85)

__str__ မရေးထားရင် print(s) က <__main__.Student object at 0x7f8b8c0d5e50> လို နားမလည်နိုင်တဲ့ စာထုတ်မယ်။ __str__ override လုပ်ထားတော့ လူဖတ်ရလွယ်တဲ့ ပုံစံထွက်လာတယ်။ ဒါက override ရဲ့ အရိုးရှင်းဆုံး၊ အသုံးအဝင်ဆုံး ဥပမာပဲ — ကိုယ့် class တိုင်းမှာ ထည့်ရေးဖို့ စဉ်းစားပါ။

Reference

Reference

ထပ်မံဥပမာ: ပုံစံအမျိုးမျိုးရဲ့ ဧရိယာ

Polymorphism ကို loop နဲ့ တွဲသုံးတဲ့အခါ အစွမ်းပိုထင်ရှားတယ် —

class Shape:
    def area(self):
        return 0

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14159 * self.radius ** 2

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

shapes = [Circle(5), Rectangle(4, 6), Circle(3)]
total = 0
for s in shapes:
    a = s.area()
    print(f"{type(s).__name__}: {round(a, 2)}")
    total += a
print(f"Total area: {round(total, 2)}")
Circle: 78.54
Rectangle: 24
Circle: 28.27
Total area: 130.81

Loop ထဲမှာ s က တစ်ခါ Circle၊ တစ်ခါ Rectangle — ဒါပေမယ့် s.area() တစ်မျိုးတည်း ခေါ်ရုံပဲ။ ပုံစံအသစ် (ဥပမာ Triangle) ထပ်တိုးချင်ရင် class အသစ်ရေးပြီး list ထဲ ထည့်လိုက်ရုံပဲ — loop ကို ထိစရာ မလိုဘူး။ ဒါဟာ polymorphism က program ကို တိုးချဲ့ရလွယ်အောင် လုပ်ပေးပုံပဲ။ ဒီပုံစံကို "open for extension, closed for modification" လို့ ခေါ်တယ် — ရှိပြီးသား code ကို မထိဘဲ အသစ်တိုးချဲ့နိုင်တာ။

Polymorphism ရဲ့ အကျိုး ၃ ခု

  1. Code သန့်ရှင်းတယ် — object အမျိုးအစား ခွဲခြားတဲ့ if/elif အရှည်ကြီးတွေ မလိုတော့ဘူး။ Method နာမည်တူခေါ်ရုံပဲ။
  2. တိုးချဲ့ရလွယ်တယ် — class အသစ်ထပ်ထည့်ရင် ရှိပြီးသား code ကို ထိစရာ မလိုဘူး (လေ့ကျင့်ခန်း ၅ မှာ စမ်းကြည့်ပါ)။
  3. Flexible ဖြစ်တယ် — duck typing ကြောင့် inheritance မရှိတဲ့ class တွေနဲ့ပါ အလုပ်လုပ်တယ်။

Mini Project: Hospital Appointment System

Polymorphism ကို လက်တွေ့သုံးထားတဲ့ ဆေးရုံ appointment စနစ် ကြည့်ရအောင်။ ဆရာဝန်အမျိုးအစားအလိုက် consultation_fee() က မတူညီတဲ့ တန်ဖိုးပြန်ပေးမယ် — ဒါပေမယ့် appointment စနစ်က ဆရာဝန်အမျိုးအစား ခွဲခြားစရာ မလိုဘူး။

class Doctor:
    def __init__(self, name, department):
        self.name = name
        self.department = department

    def consultation_fee(self):
        return 0

    def get_details(self):
        return "Dr. " + self.name + " (" + self.department + ")"


class GeneralPhysician(Doctor):
    def consultation_fee(self):
        return 300


class Specialist(Doctor):
    def consultation_fee(self):
        return 800


class AppointmentSystem:
    def __init__(self):
        self.doctors = []
        self.appointments = []

    def add_doctor(self, doctor):
        self.doctors.append(doctor)

    def show_doctors(self):
        print("\nAvailable Doctors:")
        for i, doc in enumerate(self.doctors, start=1):
            print(f"{i}. {doc.get_details()} - Fee: {doc.consultation_fee()} kyats")

    def book_appointment(self, choice, patient_name, patient_age):
        if 1 <= choice <= len(self.doctors):
            doc = self.doctors[choice - 1]
            appointment = {
                "patient": patient_name,
                "age": patient_age,
                "doctor": doc.name,
                "department": doc.department,
                "fee": doc.consultation_fee()
            }
            self.appointments.append(appointment)
            print("Appointment booked with Dr. " + doc.name +
                  " (" + doc.department + "). Fee: " +
                  str(doc.consultation_fee()) + " kyats")
        else:
            print("Invalid choice.")

    def show_appointments(self):
        if not self.appointments:
            print("No appointments yet.")
            return
        print("\nAll Appointments:")
        for a in self.appointments:
            print(f"- {a['patient']} ({a['age']} yrs) -> Dr. {a['doctor']} "
                  f"[{a['department']}], Fee: {a['fee']} kyats")


# Demo
system = AppointmentSystem()
system.add_doctor(GeneralPhysician("Aung Myo", "General"))
system.add_doctor(Specialist("Su Wai", "Cardiology"))
system.add_doctor(Specialist("Zaw Htet", "Orthopedics"))

system.show_doctors()
system.book_appointment(2, "Ma Thida", 30)
system.book_appointment(1, "Ko Paing", 45)
system.show_appointments()
Available Doctors:
1. Dr. Aung Myo (General) - Fee: 300 kyats
2. Dr. Su Wai (Cardiology) - Fee: 800 kyats
3. Dr. Zaw Htet (Orthopedics) - Fee: 800 kyats
Appointment booked with Dr. Su Wai (Cardiology). Fee: 800 kyats
Appointment booked with Dr. Aung Myo (General). Fee: 300 kyats

All Appointments:
- Ma Thida (30 yrs) -> Dr. Su Wai [Cardiology], Fee: 800 kyats
- Ko Paing (45 yrs) -> Dr. Aung Myo [General], Fee: 300 kyats

သတိထားမိလား — book_appointment() ထဲမှာ doc.consultation_fee() လို့ တစ်မျိုးတည်း ခေါ်ထားတယ်။ doc က GeneralPhysician လား Specialist လား ခွဲခြားစရာ မလိုဘူး — Python က object အမျိုးအစားအလိုက် မှန်ကန်တဲ့ fee ကို အလိုအလျောက် ရွေးပေးတယ်။ ဒါဟာ polymorphism က code ကို ဘယ်လောက် သန့်ရှင်းစေလဲ ဆိုတဲ့ သက်သေပဲ။

The system calls one method; each object answers its own way AppointmentSystem consultation_fee() GeneralPhysician 300 consultation_fee() Specialist 800 kyats no if/else needed: Python picks the right method
consultation_fee() က object အမျိုးအစားအလိုက် မတူညီတဲ့ fee ပြန်ပေးပုံ

Reference

အနှစ်ချုပ်

ဒီအခန်းမှာ polymorphism ရဲ့ အနှစ်သာရကို လေ့လာခဲ့ပြီးပြီ — နာမည်တူကို ပုံစံအမျိုးမျိုးနဲ့ အပြုအမူပြတာ။ Method overriding က inheritance နဲ့ တွဲပြီး အလုပ်လုပ်တယ်၊ super() က parent ရဲ့ အလုပ်ကို ဆက်သုံးနိုင်စေတယ်၊ overloading ကို Python မှာ အစားထိုးနည်းနဲ့ ရေးတယ်၊ duck typing က inheritance မလိုဘဲ polymorphism ရစေတယ်။ ဒီအယူအဆတွေက code ကို သန့်ရှင်းစေတယ်၊ တိုးချဲ့ရလွယ်စေတယ်။ နောက်အခန်းမှာ တိုင် ၄ ခုထဲက နောက်ဆုံးတစ်ခုဖြစ်တဲ့ encapsulation ကို လေ့လာမယ်။

  • Polymorphism = "many forms" — method နာမည်တူကို object အမျိုးအစားအလိုက် မတူညီတဲ့ အပြုအမူနဲ့ တုံ့ပြန်တာ။
  • Method overriding — subclass က parent ရဲ့ method ကို ပြန်ရေးပြီး အစားထိုးတာ။ စည်းမျဉ်း: နာမည်တူရမယ်၊ signature တူသင့်တယ်။
  • Parent ကို ဆက်ဖြည့်ချင်ရင် super() သုံး — super().method() ကို ခေါ်ပြီးမှ ကိုယ့်အပိုင်း ဆက်ရေးတယ်။
  • Method overloading က Python မှာ တိုက်ရိုက်မရ — default parameter (သို့) *args နဲ့ အစားထိုးရေးတယ်။
  • Duck typing — "method ပါရင် ရပြီ"၊ inheritance မလိုဘူး။ Python ဆန်ဆန် polymorphism။
  • Polymorphism က code သန့်ရှင်းစေတယ်၊ တိုးချဲ့ရလွယ်စေတယ်၊ flexible ဖြစ်စေတယ်။

ဒီအနှစ်ချုပ်ကို ပြန်ဖတ်ပြီး တစ်ချက်ချင်းစီ ကိုယ့်ဘာသာ ဥပမာနဲ့ ရှင်းပြနိုင်ရင် polymorphism ကျွမ်းကျင်ပြီ။ Overriding ရဲ့ စည်းမျဉ်း ၃ ခု၊ overloading အစားထိုးနည်း ၂ ခု၊ duck typing ရဲ့ ဆိုရိုးစကား — ဒါတွေကို မှတ်မိဖို့ အရေးကြီးတယ်။

အမှားများတတ်တဲ့ အချက်များ

  1. Override လုပ်ချင်တဲ့ method နာမည် စာလုံးပေါင်းမှားတာ — Python က အမှားမပြဘဲ method အသစ်ဖြစ်သွားမယ်။ တိတ်တိတ်လေး မှားတဲ့ အမှားမို့ အန္တရာယ်အကြီးဆုံး။
  2. Signature မတူတာ — parent က speak(self) ဆို child ကလည်း speak(self) ဖြစ်ရမယ်။ Parameter အပိုထည့်မိရင် ခေါ်တဲ့အခါ TypeError တက်နိုင်တယ်။
  3. super() မလိုဘဲ အမြဲခေါ်တာ / လိုရဲ့သားနဲ့ မခေါ်တာ — လုံးဝအစားထိုးချင်ရင် မခေါ်နဲ့၊ ဆက်ဖြည့်ချင်ရင် ခေါ်။ ရည်ရွယ်ချက် ရှင်းရှင်းထားပါ။
  4. Overloading ကို Java လို ရေးဖို့ကြိုးစားတာ — နာမည်တူ method နှစ်ခါ ရေးရင် နောက်ဆုံးတစ်ခုကပဲ ကျန်မယ်။ Default parameter နည်း သုံးပါ။

လေ့ကျင့်ခန်းများ

အောက်က လေ့ကျင့်ခန်းတွေကို ကိုယ်တိုင် အရင်ကြိုးစားရေးကြည့်ပါ — မှားမှာကို မကြောက်ပါနဲ့၊ မှားမှ သင်ယူရတာ။

၁။ Shape parent class မှာ area() method ("Area not defined" print) ရေးပါ။ Circle(Shape) က area() ကို override လုပ်ပြီး pi * r * r ပြန်ပေးပါ။ Rectangle(Shape) ကလည်း override လုပ်ပြီး width * height ပြန်ပေးပါ။ Object ၂ ခုစလုံးကနေ area() ခေါ်ပြပါ။

၂။ Employee class ရဲ့ get_salary() ("Base salary") ကို Manager subclass က override လုပ်ပါ။ super().get_salary() ကို ခေါ်ပြီး "Manager bonus added" ဆိုတဲ့ စာကြောင်းပါ ထပ်ဖြည့်ပါ။

၃။ Printer class မှာ print_info() method ကို default parameter သုံးပြီး overloading အတုယူပါ — print_info(name) ဆို နာမည်ပဲ ထုတ်ပြ၊ print_info(name, age) ဆို နာမည်နဲ့ အသက် နှစ်ခုလုံး ထုတ်ပြပါ။

၄။ Duck typing demo — MP3Player class (play() → "Playing MP3") နဲ့ Radio class (play() → "Playing radio") နှစ်ခုရေးပြီး၊ နှစ်ခုလုံးကို လက်ခံတဲ့ start_music(device) function တစ်ခုရေးပါ။

၅။ (စိန်ခေါ်မှု) — Hospital project ကို တိုးချဲ့ပါ။ Surgeon(Doctor) class အသစ်ထည့်ပြီး consultation_fee() က 1500 ပြန်ပေးပါစေ။ AppointmentSystem ရဲ့ code တစ်ခုမှ မပြင်ဘဲ surgeon အတွက် appointment တင်နိုင်တာ ပြပါ။ (Polymorphism ရဲ့ အစွမ်းကို ဒီမှာ တွေ့ရမယ်။)