OOP ရဲ့ တိုင်ကြီး ၄ ခုထဲက ပထမတစ်ခုကို အခု အသေးစိတ် လေ့လာမယ် — Encapsulation။
ဆေးတောင့် (capsule) ကို စဉ်းစားကြည့်။ အထဲမှာ ဆေးမှုန့်တွေ ရှိတယ် — ဒါပေမယ့် နင်က ဆေးတောင့်ကို မြိုချရုံပဲ၊ အထဲက မှုန့်ကို လက်နဲ့ ဆွဲကိုင်စရာ မလိုဘူး။ အထဲရေးကို ဖုံးကွယ်ထားပြီး၊ သုံးဖို့လိုတဲ့ အပိုင်း (မြိုချတာ) ကိုပဲ ဖွင့်ပေးထားတာ။
Encapsulation ဆိုတာ ဒီသဘောပဲ — object ရဲ့ အတွင်းအချက်အလက်တွေကို ဖုံးကွယ်ထားပြီး၊ လိုအပ်တဲ့အပိုင်းကိုပဲ public interface (အများသုံးမျက်နှာစာ) ကနေ ဖွင့်ပေးတာ။ နောက်တစ်နည်း ပြောရရင် — data (variable) နဲ့ အဲဒီ data ပေါ် လုပ်တဲ့ code (method) တွေကို class တစ်ခုတည်းမှာ ထုပ်ပိုးစုစည်းထားတာ၊ ပြီးတော့ ဘယ်သူက ဘာကို ထိကိုင်ခွင့်ရှိလဲ ထိန်းချုပ်တာ။
ဒီအခန်းပြီးရင်...
- Encapsulation ဆိုတာ ဘာလဲ၊ ဘာကြောင့် အရေးကြီးလဲ ရှင်းပြနိုင်မယ်
- Access modifier ၃ မျိုး (public, protected, private) ကို ခွဲခြားသုံးနိုင်မယ်
- Private attribute တွေကို getter/setter နဲ့ လုံခြုံစွာ စီမံနိုင်မယ် (validation နဲ့အတူ)
@propertydecorator သုံးပြီး pythonic ဆန်ဆန် getter/setter ရေးနိုင်မယ်- PIN နဲ့ balance ကို ကာကွယ်ထားတဲ့ Bank Account System ရေးနိုင်မယ်
Encapsulation ဆိုတာဘာလဲ
အဓိပ္ပာယ် ၂ ထပ် ရှိတယ် —
- ထုပ်ပိုးခြင်း (Bundling) — data နဲ့ method တွေကို class တစ်ခုတည်းမှာ စုစည်းထားတာ။ အရင်အခန်းတွေမှာ လုပ်ခဲ့တာတွေအားလုံး ဒါပဲ (
Walletclass မှာbalancedata နဲ့deposit()method အတူရှိတာ)။ - ဖုံးကွယ်ခြင်း (Hiding) — အတွင်းရေးကို အပြင်ကနေ တိုက်ရိုက်ထိကိုင်ခွင့် မပေးဘဲ၊ method တွေကတဆင့်ပဲ ထိကိုင်ခွင့်ပေးတာ။
ဘာကြောင့် ဖုံးကွယ်ရတာလဲ? ဥပမာ — ဘဏ်အကောင့်ရဲ့ balance ကို ဘယ်သူမဆို တိုက်ရိုက်ပြောင်းလို့ရနေရင် (acc.balance = -999999) ပြဿနာတက်ပြီ။ Encapsulation နဲ့ balance ကို ဖုံးထားပြီး deposit()/withdraw() method တွေကတဆင့်ပဲ ပြောင်းခွင့်ပေးရင် — ငွေပမာဏ မှန်မမှန်၊ လက်ကျန် လောက်မလောက် စစ်ဆေးပြီးမှ လုပ်လို့ရတယ်။ Data ကို ကာကွယ်တာ၊ စည်းမျဉ်းနဲ့ ထိန်းချုပ်တာ — ဒါဟာ encapsulation ရဲ့ အဓိကအကျိုးပါ။
မှတ်ချက်
Encapsulation ကောင်းကောင်းလုပ်ထားတဲ့ class ကို သုံးတဲ့သူက အတွင်းပိုင်း ဘယ်လိုအလုပ်လုပ်လဲ သိစရာ မလိုဘူး — ဘယ် method ခေါ်ရင် ဘာဖြစ်မလဲ သိရင်ရပြီ။ ဒါကို abstraction နဲ့ တွဲပြီး မှတ်ထားပါ။
Encapsulation ကို အဆင့် ၃ ဆင့်နဲ့ တည်ဆောက်လေ့ရှိတယ် — ပထမ အတွင်းရေးကို private လုပ်ပြီး ဖုံးကွယ်တယ်၊ ဒုတိယ လိုအပ်တဲ့ လုပ်ဆောင်ချက်တွေကို public method အဖြစ် ဖွင့်ပေးတယ်၊ တတိယ အဲဒီ method တွေထဲမှာ validation နဲ့ စည်းမျဉ်းတွေ ထည့်တယ်။ ဒီအခန်းမှာ ဒီအဆင့် ၃ ဆင့်လုံးကို တစ်ခုချင်း လေ့လာမယ်။
Reference
- Programiz — OOP concepts — encapsulation သဘောတရား အကျဉ်းချုပ်
- Real Python — OOP in Python 3 — encapsulation ဥပမာများ
Encapsulation မရှိရင် ဘာဖြစ်မလဲ
Attribute တွေ အားလုံး public ဖြစ်နေရင် ဘယ်သူမဆို ဘာမဆို လုပ်နိုင်တယ် —
class UnsafeAccount:
def __init__(self, balance):
self.balance = balance # public — ဘယ်သူမဆို ထိနိုင်တယ်
a = UnsafeAccount(5000)
a.balance = -99999 # ဘယ်သူမှ တားမထားဘူး!
print(a.balance)
-99999
လက်ကျန် အနှုတ် ၉၉၉၉၉ ဖြစ်သွားတယ် — လက်တွေ့မှာ မဖြစ်သင့်တဲ့ အခြေအနေ။ ဘယ်သူက ဘယ်အချိန်မှာ ပြောင်းသွားလဲ လိုက်ရှာရလည်း ခက်မယ်။ Encapsulation က ဒီလိုမျိုး "တိုက်ရိုက်ထိခွင့်" ကို ပိတ်ပြီး၊ method တွေကတဆင့် စည်းမျဉ်းနဲ့ ဝင်ခွင့်ပေးတယ်။ အမှားနည်းတယ်၊ debug လုပ်ရလွယ်တယ်၊ စနစ်စိတ်ချရတယ်။ အထူးသဖြင့် အဖွဲ့လိုက်ရေးတဲ့ project တွေမှာ တစ်ယောက်ရဲ့ code က နောက်တစ်ယောက်ရဲ့ data ကို မတော်တဆ ဖျက်ဆီးမိတာ ကာကွယ်ပေးတယ်။
Access Modifier များ
Python မှာ attribute တစ်ခုကို ဘယ်လောက်အထိ ဖွင့်ပေးမလဲ ဆိုတာ နာမည်ရဲ့ အစမှာ underscore ခံပြီး ဖော်ပြတယ်။ ၃ မျိုး ရှိတယ် —
| အမျိုးအစား | ရေးနည်း | အဓိပ္ပာယ် |
|---|---|---|
| Public | self.name |
နေရာတိုင်းကနေ တိုက်ရိုက်သုံးလို့ရတယ်။ စံသတ်မှတ်ချက် (default)။ |
| Protected | self._name |
"အတွင်းပိုင်းသုံးဖို့" ဆိုတဲ့ သဘောတူညီချက် (convention)။ Class အပြင်က တိုက်ရိုက်မသုံးသင့်ဘူး၊ ဒါပေမယ့် subclass တွေက သုံးနိုင်တယ်။ |
| Private | self.__name |
Class အပြင်ကနေ တိုက်ရိုက်သုံးလို့ မရဘူး။ Python က နာမည်ကို ပြောင်းပစ်တယ် (name mangling)။ |
တစ်ခုချင်း ကြည့်ရအောင် —
class Staff:
def __init__(self, name, department, salary):
self.name = name # public
self._department = department # protected (convention)
self.__salary = salary # private
s = Staff("Ko Aung", "Sales", 800000)
print(s.name) # ရတယ် — public
print(s._department) # အလုပ်လုပ်တယ်၊ ဒါပေမယ့် မလုပ်သင့်ဘူး (convention ချိုးဖောက်တာ)
print(s.__salary) # AttributeError တက်မယ်!
Ko Aung
Sales
AttributeError: 'Staff' object has no attribute '__salary'
__salary ကို အပြင်ကနေ တိုက်ရိုက်ခေါ်ရင် Python က ရှာမတွေ့ဘူး။ တကယ်တော့ Python က သူ့ကို _Staff__salary လို့ နာမည်ပြောင်းပြီး သိမ်းထားတာ — ဒါကို name mangling လို့ ခေါ်တယ်။ Class အထဲက method တွေကတော့ self.__salary လို့ ပုံမှန်သုံးလို့ရတယ်။
သတိပြုရန်
Python ရဲ့ private/protected ဟာ တခြား ဘာသာစကားတွေလို တင်းကျပ်တဲ့ တားမြစ်ချက် မဟုတ်ဘူး — သဘောတူညီချက် (convention) နဲ့ name mangling သာဖြစ်တယ်။ _Staff__salary လို့ သိရင် အပြင်ကနေလည်း ဝင်ယူလို့ရတယ်။ ဒါကြောင့် "မထိသင့်ဘူး" ဆိုတဲ့ စည်းကမ်းကို လိုက်နာဖို့က programmer ရဲ့ တာဝန်ပါ။
Protected (_) ကို subclass မှာ ဘယ်လိုသုံးလဲ ကြည့်ရအောင် —
class Person:
def __init__(self, name):
self._name = name # protected — subclass သုံးဖို့ ရည်ရွယ်
class Employee(Person):
def show(self):
print("Employee:", self._name) # subclass ကနေ သုံးလို့ရ
e = Employee("Daw Hla")
e.show()
Employee: Daw Hla
Protected ရဲ့ သဘောတူညီချက်က — "class အပြင်လူ မထိနဲ့၊ ဒါပေမယ့် အမွေဆက်ခံတဲ့ subclass တွေက သုံးနိုင်တယ်"။ Private (__) ကတော့ subclass ကနေတောင် တိုက်ရိုက်မရဘူး။
Reference
- Python docs — Private variables — private variable နဲ့ name mangling တရားဝင်ရှင်းလင်းချက်
- W3Schools — Classes/Objects — attribute အမျိုးအစားများ
Private Attribute နှင့် Getter/Setter
Private attribute ကို အပြင်က တိုက်ရိုက်မထိနိုင်ဘူး — ဒါဆို ဘယ်လို ဖတ်/ပြောင်းမလဲ? Getter (ဖတ်တဲ့ method) နဲ့ Setter (ပြောင်းတဲ့ method) ဆိုတဲ့ public method တွေကနေ တဆင့် လုပ်တယ်။ Setter ရဲ့ အကောင်းဆုံးအချက်က — တန်ဖိုးမပြောင်းခင် validation (စစ်ဆေးမှု) ထည့်လို့ရတာ။
class Customer:
def __init__(self, name, credit):
self.__name = name # private
self.__credit = credit # private
def get_name(self):
return self.__name
def get_credit(self):
return self.__credit
def set_credit(self, credit):
if credit >= 0:
self.__credit = credit
else:
print("Credit must not be negative.")
c = Customer("Ma Ei", 10000)
print(c.get_name())
print(c.get_credit())
c.set_credit(15000)
print(c.get_credit())
c.set_credit(-500) # မမှန်တဲ့ တန်ဖိုး — ငြင်းပယ်ခံရမယ်
print(c.get_credit()) # အရင်တန်ဖိုးပဲ ကျန်မယ်
Ma Ei
10000
15000
Credit must not be negative.
15000
-500 လို့ ပေးလိုက်တာ setter က စစ်ပြီး ငြင်းလိုက်တယ် — __credit က အရင် 15000 အတိုင်း ကျန်နေတယ်။ __credit ကို public ထားခဲ့ရင် c.__credit = -500 လို့ ဘယ်သူမဆို တိုက်ရိုက်ဖျက်ဆီးလို့ရမှာ။ Setter က တံခါးစောင့်လိုပဲ — မမှန်တဲ့ data အဝင်မခံဘူး။
နောက်ထပ် ဥပမာ — ဝန်ထမ်းလစာ validation နဲ့ —
class Employee:
def __init__(self, name, salary):
self.__name = name
self.__salary = salary
def get_name(self):
return self.__name
def set_name(self, name):
self.__name = name
def get_salary(self):
return self.__salary
def set_salary(self, salary):
if salary >= 0:
self.__salary = salary
else:
print("Invalid salary!")
emp = Employee("U Kyaw", 500000)
print(emp.get_name())
emp.set_salary(600000)
print(emp.get_salary())
emp.set_salary(-1000)
print(emp.get_salary())
U Kyaw
600000
Invalid salary!
600000
Reference
- Python docs — Private variables — private attribute သုံးပုံ
- Real Python — OOP in Python 3 — getter/setter pattern များ
@property Decorator — Python ဆန်ဆန် နည်း
Getter/setter ရေးတာ အလုပ်ဖြစ်တယ် — ဒါပေမယ့် get_salary(), set_salary() လို့ method အဖြစ် ခေါ်ရတာ Python ဆန်မဆန်ဘူး။ Python မှာ ပိုသပ်ရပ်တဲ့နည်း ရှိတယ် — @property decorator။
@property သုံးရင် attribute လိုမျိုး တိုက်ရိုက်သုံးလို့ရတယ် (p.price) — ဒါပေမယ့် နောက်ကွယ်မှာ getter/setter အလုပ်လုပ်နေတာ။
class Product:
def __init__(self, price):
self.__price = price
@property
def price(self): # getter အဖြစ် လုပ်ဆောင်တယ်
return self.__price
@price.setter
def price(self, value): # setter အဖြစ် လုပ်ဆောင်တယ်
if value > 0:
self.__price = value
else:
print("Price must be positive.")
p = Product(100)
print(p.price) # getter ခေါ်တာ — method လို () မလိုဘူး
p.price = 150 # setter ခေါ်တာ — attribute လို ပေးလိုက်ရုံပဲ
print(p.price)
p.price = -50 # setter က ငြင်းမယ်
print(p.price)
100
150
Price must be positive.
150
သတိထားပါ — class ကို အရင် အပြည့်အစုံ ရေးပြီးမှ အောက်မှာ သုံးတဲ့ code (p = Product(100) စတာ) ရေးရတယ်။ p.price လို့ ဖတ်ရင် getter အလုပ်လုပ်၊ p.price = 150 လို့ ပေးရင် setter အလုပ်လုပ် — အပြင်လူက method ခေါ်နေမှန်း မသိဘူး။ သန့်ရှင်းတယ်၊ ဖတ်ရလွယ်တယ်၊ validation လည်း ပါသေးတယ်။
မှတ်ချက်
@property နဲ့ getter ပဲ ရေးပြီး setter မရေးရင် အဲဒီ attribute က read-only ဖြစ်သွားမယ် — ဖတ်လို့ပဲရမယ်၊ ပြောင်းလို့မရဘူး။ ပြောင်းဖို့ ကြိုးစားရင် AttributeError တက်မယ်။
Read-only property ဥပမာ —
class Circle:
def __init__(self, radius):
self.__radius = radius
@property
def radius(self):
return self.__radius
@property
def area(self):
return 3.14159 * self.__radius ** 2
c = Circle(5)
print(c.radius)
print(round(c.area, 2))
5
78.54
radius နဲ့ area ကို ဖတ်လို့ပဲရမယ် — c.radius = 10 လို့ ပြောင်းဖို့ကြိုးစားရင် AttributeError တက်မယ်။ တွက်ချက်ထားတဲ့ တန်ဖိုး (area) ကို attribute လိုမျိုး သန့်သန့်ရှင်းရှင်း ဖော်ပြနိုင်တာ @property ရဲ့ နောက်ထပ် အသုံးဝင်ပုံပဲ။
Reference
- Python docs — property() —
propertyတရားဝင်ရှင်းလင်းချက် - Real Python — Python property() —
@propertyအသုံးပြုပုံ အသေးစိတ်
Private ကို ဘယ်အချိန်သုံးသင့်လဲ
Attribute တိုင်းကို private လုပ်ပစ်ရမလား? မဟုတ်ဘူး။ စည်းမျဉ်းအတိုချုပ် —
- Private (
__) — အပြင်က တိုက်ရိုက်ထိရင် အန္တရာယ်ရှိတဲ့ data: password, PIN, balance, အတွင်း state။ Validation လိုတဲ့ data မှန်သမျှ။ - Public — တိုက်ရိုက်ဖတ်/ပြောင်းလို့ ပြဿနာမရှိတဲ့ data: name, title, color။
Python ရဲ့ ဒဿနက "we're all consenting adults" — programmer တွေ စည်းကမ်းလိုက်နာမယ်လို့ ယုံကြည်တယ်။ ဒါကြောင့် Python ရဲ့ private က "လုံးဝတားမြစ်ချက်" မဟုတ်ဘဲ "မထိသင့်ဘူး" ဆိုတဲ့ အချက်ပြသဘောပါ။ Java/C++ လို တင်းကျပ်တာ မဟုတ်ဘူး — ဒါကို အားနည်းချက်လို့ မမြင်ဘဲ Python ရဲ့ ရိုးရှင်းမှု (simplicity) လို့ မြင်ပါ။ စည်းကမ်းလိုက်နာဖို့က ကိုယ့်တာဝန်။
@property ရဲ့ လျှို့ဝှက်အစွမ်း: နောက်မှ ပြောင်းလဲလို့ရတာ
@property ရဲ့ အကောင်းဆုံးအချက်တစ်ခု — public attribute အဖြစ် စခဲ့တဲ့ code ကို နောက်မှ validation ထည့်ချင်ရင်၊ သုံးနေတဲ့ code တွေကို လုံးဝ မပြင်ဘဲ ပြောင်းလို့ရတယ် —
class Account:
def __init__(self):
self.__balance = 0
@property
def balance(self):
return self.__balance
@balance.setter
def balance(self, value):
if value >= 0:
self.__balance = value
else:
print("Balance cannot be negative.")
a = Account()
a.balance = 5000 # အရင်ကလို သုံးလို့ရတုန်းပဲ — ဘာမှ မကွဲဘူး
print(a.balance)
a.balance = -100 # အခု validation ပါပြီ
print(a.balance)
5000
Balance cannot be negative.
5000
အပြင်ကနေ a.balance လို့ သုံးတဲ့ code မှန်သမျှ ဆက်အလုပ်လုပ်တုန်းပဲ — ဒါပေမယ့် နောက်ကွယ်မှာ အခု validation ရှိနေပြီ။ ဒါကြောင့် @property ကို "နောင်တချိန် တင်းကျပ်ချင်လာရင် ပြင်ရလွယ်အောင် ကြိုတင်ပြင်ဆင်ထားတဲ့ တံခါး" လို့ မှတ်ထားပါ။
Encapsulation နဲ့ Abstraction — ညီအစ်ကိုနှစ်
တိုင် ၄ ခုထဲက ၂ ခု (encapsulation နဲ့ abstraction) ကို ရောထွေးတတ်တယ်။ ခွဲခြားနည်း —
- Encapsulation = "ဘယ်လို" ကာကွယ်မလဲ — အတွင်းရေးကို ဖုံးကွယ်တဲ့ ယန္တရား (private, getter/setter,
@property)။ - Abstraction = "ဘာ" ပြမလဲ — အသုံးပြုသူ မြင်ရတဲ့ ရိုးရှင်းတဲ့ မျက်နှာစာ (method နာမည်တွေက ရှင်းနေတာ၊ အတွင်းပိုင်း ဘယ်လိုအလုပ်လုပ်လဲ သိစရာ မလိုတာ)။
TV remote ကို စဉ်းစားကြည့် — ခလုတ်နှိပ်ရုံနဲ့ လိုင်းပြောင်းတယ် (abstraction: ရိုးရှင်းတဲ့ မျက်နှာစာ)။ အထဲက ဆားကစ်ပြားကို ဖွင့်ကြည့်ခွင့် မပေးဘူး (encapsulation: အတွင်းရေး ဖုံးကွယ်ခြင်း)။ တစ်ခုက ယန္တရား၊ တစ်ခုက ရလဒ် — နှစ်ခုတွဲမှ class ကောင်းတစ်ခု ဖြစ်တယ်။
မှတ်ချက်
Name mangling ဖြစ်သွားတဲ့ နာမည်ကို dir() နဲ့ ကြည့်နိုင်တယ် — dir(s) ထဲမှာ _Staff__salary ဆိုတာ တွေ့ရမယ်။ ဒါက "ဖုံးကွယ်ထားတယ်" ဆိုတာ သက်သေပဲ — ဒါပေမယ့် debug လုပ်ဖို့အတွက်ပဲ ကြည့်ပါ၊ သာမန် code မှာ အဲဒီနာမည်နဲ့ ဝင်မသုံးပါနဲ့။
Reference
- Real Python — Python property() —
@propertyအသုံးပြုပုံ အသေးစိတ် - Python docs — property() —
propertyတရားဝင်ရှင်းလင်းချက်
Mini Project: Bank Account Management System
Encapsulation ကို လက်တွေ့သုံးထားတဲ့ ဘဏ်အကောင့် စနစ်။ __pin နဲ့ __balance ကို private လုပ်ထားတယ် — PIN မှန်မှ ငွေထုတ်/လက်ကျန်ကြည့်ခွင့်ရမယ်။
class BankAccount:
def __init__(self, holder, pin, balance=0):
self.__holder = holder
self.__pin = pin
self.__balance = balance
def deposit(self, amount):
if amount > 0:
self.__balance += amount
print(str(amount) + " deposited successfully.")
else:
print("Invalid deposit amount.")
def withdraw(self, amount, pin):
if pin == self.__pin:
if 0 < amount <= self.__balance:
self.__balance -= amount
print(str(amount) + " withdrawn successfully.")
else:
print("Insufficient balance.")
else:
print("Incorrect PIN. Access denied.")
def get_balance(self, pin):
if pin == self.__pin:
return self.__balance
return "Incorrect PIN. Access denied."
def get_holder(self):
return self.__holder
account = BankAccount("Daw Mya", 2468, 5000)
account.deposit(1000)
account.withdraw(2000, 2468) # PIN မှန် — အောင်မြင်
account.withdraw(500, 9999) # PIN မှား — ငြင်းပယ်
print("Balance:", account.get_balance(2468)) # PIN မှန် — ပြမယ်
print("Balance:", account.get_balance(9999)) # PIN မှား — မပြဘူး
1000 deposited successfully.
2000 withdrawn successfully.
Incorrect PIN. Access denied.
Balance: 4000
Balance: Incorrect PIN. Access denied.
__balance ကို အပြင်ကနေ account.__balance လို့ တိုက်ရိုက်ထိလို့ မရဘူး — deposit(), withdraw(), get_balance() ဆိုတဲ့ public method တွေကတဆင့်ပဲ ကိုင်တွယ်ရတယ်။ ပြီးတော့ အရေးကြီးတဲ့ လုပ်ဆောင်ချက် ၂ ခု (withdraw, get_balance) က PIN စစ်ဆေးမှု ခံထားရတယ်။ ဒါဟာ encapsulation ရဲ့ လက်တွေ့တန်ဖိုးပဲ — အတွင်းရေးကို ဖုံးကွယ်ပြီး၊ စည်းမျဉ်းနဲ့ ဝင်ပေါက်ကို ထိန်းချုပ်ထားတာ။
Reference
- Python docs — Private variables — private data ကာကွယ်ပုံ
- Real Python — Python property() — validation ပါတဲ့ attribute design
Encapsulation မလုပ်ရင် ဖြစ်တတ်တဲ့ bug ၃ မျိုး
- မမှန်တဲ့ data ဝင်လာတာ — အသက်
-5၊ လစာ-1000၊ လက်ကျန်-99999။ Public attribute ဆို ဘယ်သူမှ တားမထားဘူး။ Setter validation က ဒါကို အဝင်ဝမှာ တားတယ်။ - Data နဲ့ logic ကွဲသွားတာ — ဥပမာ
is_borrowedကို အပြင်ကနေ တိုက်ရိုက်ပြောင်းလိုက်ရင် စာအုပ်အခြေအနေ မှန်ပေမယ့် ငှားရမ်းမှတ်တမ်း (log) မရေးဖြစ်ဘူး။ Method ကတဆင့်ပဲ ပြောင်းခွင့်ပေးရင် log ရေးတာပါ တစ်ပါတည်း လုပ်လို့ရတယ်။ - ဘယ်သူ့လက်ချက်လဲ မသိတာ — public attribute ကို program ရဲ့ နေရာအနှံ့က ပြောင်းနေရင် bug ရှာရတာ အိပ်မက်ဆိုး။ Private + method ဆို ပြောင်းတဲ့နေရာ (setter/method) တစ်ခုတည်းမှာ breakpoint ထောက်ပြီး ရှာလို့ရတယ်။
လက်တွေ့ဥပမာ: အစီရင်ခံစာကတ် (Report Card)
Private data + validation + @property တို့ကို တစ်ခုတည်း ပေါင်းသုံးထားတဲ့ ဥပမာ —
class ReportCard:
def __init__(self, name):
self.__name = name
self.__marks = {}
def add_mark(self, subject, mark):
if 0 <= mark <= 100:
self.__marks[subject] = mark
else:
print(f"Invalid mark for {subject}: must be 0-100.")
@property
def average(self):
if not self.__marks:
return 0
return sum(self.__marks.values()) / len(self.__marks)
def show(self):
print(f"Report card for {self.__name}:")
for subject, mark in self.__marks.items():
print(f" {subject}: {mark}")
print(f" Average: {self.average:.1f}")
r = ReportCard("Aung Khant")
r.add_mark("Myanmar", 85)
r.add_mark("English", 72)
r.add_mark("Maths", 105) # မမှန်တဲ့ အမှတ် — ငြင်းပယ်ခံရမယ်
r.show()
Invalid mark for Maths: must be 0-100.
Report card for Aung Khant:
Myanmar: 85
English: 72
Average: 78.5
__marks ကို အပြင်ကနေ တိုက်ရိုက်ထိလို့ မရဘူး — add_mark() ကတဆင့်ပဲ ထည့်ရပြီး 0–100 validation ခံရတယ်။ average က @property နဲ့ တွက်ချက်ပေးတဲ့ read-only attribute — သိမ်းထားစရာ မလိုဘဲ လိုအပ်တိုင်း အလိုအလျောက် တွက်ပေးတယ်။ ဒါဟာ encapsulation ရဲ့ အစုံအလင် ပုံစံပဲ။
__init__ မှာ validation ထည့်တာ
Setter မှာတင် မဟုတ်ဘူး — object စထုတ်ချင်းမှာတင် မမှန်တဲ့ data မဝင်အောင် __init__ မှာလည်း စစ်လို့ရတယ် —
class Student:
def __init__(self, name, age):
self.__name = name
if age > 0:
self.__age = age
else:
print("Age must be positive. Set to 0.")
self.__age = 0
@property
def age(self):
return self.__age
s1 = Student("Ma Thiri", 20)
s2 = Student("Ko Aung", -5)
print(s1.age)
print(s2.age)
Age must be positive. Set to 0.
20
0
Object မွေးဖွားလာကတည်းက data မှန်ကန်ဖို့ __init__ မှာ စစ်ဆေးတာ — "အစကောင်းမှ အနှောင်းသေချာ" ဆိုတဲ့ သဘောပဲ။ Setter validation နဲ့ တွဲလိုက်ရင် object ရဲ့ ဘဝတစ်လျှောက်လုံး data မှန်ကန်နေမယ်။
Encapsulation Checklist — ကိုယ့် class ကို ပြန်စစ်ဖို့
Class တစ်ခု ရေးပြီးတိုင်း ဒီမေးခွန်း ၄ ခု မေးကြည့်ပါ —
- အတွင်းရေးတွေ ဖုံးထားပြီလား? — အပြင်က တိုက်ရိုက်မထိသင့်တဲ့ attribute တွေကို
__(private) လုပ်ထားလား? - ဝင်ပေါက်တွေ စည်းမျဉ်းနဲ့လား? — Setter တွေမှာ validation ပါလား? (အနှုတ်၊ အလွတ်၊ မမှန်တဲ့ အမျိုးအစား ဝင်မလာနိုင်ဘူးလား?)
- Public interface ရှင်းလား? — သုံးတဲ့သူက ဘယ် method ခေါ်ရမယ် ဆိုတာ ချက်ချင်းသိလား?
- Python ဆန်လား? — Getter/setter အစား
@propertyသုံးသင့်တဲ့နေရာ သုံးထားလား?
၄ ခုလုံး "ဟုတ်ကဲ့" ဆိုရင် encapsulation ကောင်းကောင်းလုပ်ထားတဲ့ class ဖြစ်ပြီ။ ဒီအခန်းမှာ သင်ခဲ့တဲ့ ၄ တိုင်ထဲက ပထမတိုင် ပြီးသွားပြီ — နောက်အခန်းတွေမှာ ကျန်တဲ့ တိုင်တွေနဲ့ ဘယ်လိုတွဲဖက်သုံးရမလဲ ဆက်လေ့လာမယ်။
အနှစ်ချုပ်
ဒီအခန်းမှာ OOP တိုင် ၄ ခုထဲက ပထမတိုင်ကို အသေးစိတ် လေ့လာခဲ့ပြီးပြီ။ Encapsulation ရဲ့ အဓိကက "ဖုံးကွယ်ပြီး ထိန်းချုပ်တာ" — အတွင်းရေးကို private နဲ့ ဖုံးကွယ်တယ်၊ public method တွေကနေ စည်းမျဉ်းနဲ့ ဝင်ခွင့်ပေးတယ်၊ setter တွေမှာ validation ထည့်တယ်၊ @property နဲ့ pythonic ဆန်ဆန် ရေးတယ်။ ဒီအကျင့်က data ကို ကာကွယ်ပေးတယ်၊ bug နည်းစေတယ်၊ code ကို ရေရှည်ထိန်းသိမ်းရလွယ်စေတယ်။
- Encapsulation ဆိုတာ object ရဲ့ အတွင်းရေးကို ဖုံးကွယ်ပြီး လိုအပ်တဲ့အပိုင်းကိုပဲ public interface ကနေ ဖွင့်ပေးတာ။ အဓိပ္ပာယ် ၂ ထပ်: ထုပ်ပိုးခြင်း (bundling) + ဖုံးကွယ်ခြင်း (hiding)။
- Access modifier ၃ မျိုး — public (
self.name), protected (self._name, convention), private (self.__name, name mangling)။ - Getter/setter က private data ကို လုံခြုံစွာ ဖတ်/ပြောင်းပေးတယ်။ Setter မှာ validation ထည့်တာ အရေးအကြီးဆုံး အကျိုး။
@propertyက pythonic ဆန်ဆန် getter/setter — attribute လိုမျိုး သုံးလို့ရပြီး နောက်ကွယ်မှာ validation အလုပ်လုပ်တယ်။- Private ကို အန္တရာယ်ရှိတဲ့ data (PIN, balance, password) အတွက် သုံး — အရာတိုင်းကို private လုပ်ပစ်စရာ မလိုဘူး။
ဒီအနှစ်ချုပ်နဲ့ checklist ကို class ရေးတိုင်း ပြန်စစ်ဖို့ သုံးပါ — အကျင့်ဖြစ်သွားရင် encapsulation က အလိုအလျောက် ပါလာမယ်။ တစ်ချက်ချင်းစီကို ကိုယ့်ဘာသာ ဥပမာနဲ့ ရှင်းပြနိုင်ရင် ဒီအခန်း ကျွမ်းကျင်ပြီလို့ သတ်မှတ်နိုင်တယ်။
အမှားများတတ်တဲ့ အချက်များ
- Underscore တစ်ချောင်း/နှစ်ချောင်း ရောတာ —
_name(protected, convention) နဲ့__name(private, mangling) မတူဘူး။ @propertysetter ထဲမှာ infinite recursion — setter ထဲမှာself.price = valueလို့ ပြန်ရေးမိရင် setter က သူ့ကိုယ်သူ အဆုံးမသတ် ခေါ်နေပြီးRecursionErrorတက်မယ်။self.__price = valueလို့ private attribute ကို တိုက်ရိုက် ပေးရတယ်။- Private attribute ကို subclass က တိုက်ရိုက်သုံးဖို့ ကြိုးစားတာ — name mangling ကြောင့် subclass က
self.__salaryလို့ ခေါ်ရင် သူ့ကိုယ်ပိုင်_Subclass__salaryကို ရှာမယ်၊ parent ရဲ့_Parent__salaryကို မရဘူး။ - Validation မေ့တာ — setter ရေးပြီး
ifစစ်ဆေးမှု မထည့်ရင် getter/setter ရေးရကျိုး တစ်ဝက်ပဲ ရမယ်။
လေ့ကျင့်ခန်းများ
အောက်က လေ့ကျင့်ခန်းတွေကို ကိုယ်တိုင် အရင်ကြိုးစားရေးကြည့်ပါ — မှားမှာကို မကြောက်ပါနဲ့၊ မှားမှ သင်ယူရတာ။
၁။ Student class မှာ __name (private) နဲ့ __score (private) ထားပါ။ get_name(), get_score(), set_score(score) (0–100 အတွင်းမှ လက်ခံပါ) တို့ကို ရေးပါ။ Score 150 ပေးကြည့်ပြီး ငြင်းပယ်ခံရတာ စမ်းပြပါ။
၂။ Access modifier table ကို ကိုယ်တိုင်စမ်းပါ — Laptop class မှာ brand (public), _model (protected), __serial (private) ထားပြီး၊ object အပြင်ကနေ ၃ ခုလုံး တိုက်ရိုက်ဖတ်ကြည့်ပါ။ ဘယ်ဟာတွေ အလုပ်လုပ်/မလုပ်လဲ မှတ်သားပါ။
၃။ Temperature class ကို @property နဲ့ ရေးပါ — celsius attribute ကို -273.15 (absolute zero) အောက် မရောက်အောင် setter မှာ စစ်ပါ။
၄။ Bank Account project ကို တိုးချဲ့ပါ — change_pin(old_pin, new_pin) method ထည့်ပါ။ PIN အဟောင်း မှန်မှ အသစ်ပြောင်းခွင့်ပေးရမယ်။
၅။ (စိန်ခေါ်မှု) — __password ကို private လုပ်ထားတဲ့ User class ရေးပါ။ login(password) method က ပေးတဲ့ password မှန်ရင် "Login successful", မှားရင် "Login failed" ပြန်ပေးပါ။ Password ကို အပြင်ကနေ တိုက်ရိုက်ဖတ်လို့ မရတာ သက်သေပြပါ။