Python Encapsulation & Access Modifiers: Data ආරක්ෂා කරමු! – SC Guide

කොහොමද යාලුවනේ, ඔයාලා හැමෝම හොඳින් ඉන්නවා කියලා හිතනවා. අද අපි කතා කරන්න යන්නේ Software Engineering වල තියෙන fundamental concepts වලින් එකක් ගැන. ඒ තමයි Object-Oriented Programming (OOP) වල වැදගත්ම කොටසක් වෙන Encapsulation සහ ඒකට අදාල Access Modifiers ගැන. විශේෂයෙන්ම, අපි මේක Python වල කොහොමද implement වෙන්නේ කියලා, Lankan vibe එකට, හොඳටම පැහැදිලිව කතා කරමු. මොකද Python වල මේක ටිකක් අනිත් languages වලට වඩා වෙනස් විදියට තමයි operate වෙන්නේ.
ඔයාලා දැන් codes ලියනකොට, data protect කරන එක කොච්චර වැදගත්ද කියලා දන්නවනේ. අන්න ඒ වැඩේට තමයි Encapsulation කියන concept එක අපිට උදව් කරන්නේ. එහෙනම්, කෙලින්ම වැඩේට බහිමු!
මොකක්ද මේ Encapsulation කියන්නෙ? (What is this Encapsulation?)
හරි, මුලින්ම බලමු මේ Encapsulation කියන්නේ මොකක්ද කියලා. සරලවම කිව්වොත්, Encapsulation කියන්නේ data එකයි, ඒ data එක manage කරන methods ටිකයි එක තැනකට, පොඩි පොට්ටනියක් වගේ, එකට බැඳලා තියෙන එක. හරියට අපි බත් පාර්සලයක් ගත්තම, බත් එක, ව්යංජන, කරවල, පොල් සම්බෝල ඔක්කොම එකට පොට්ටනියක දාලා දෙනවා වගේ. ඒක ඇතුලේ මොනවද තියෙන්නේ, ඒවට මොනවද කරන්නේ කියලා, ඒ පොට්ටනිය ඇතුලේම තියෙනවා.
Software world එකේදී මේක තේරුම් ගන්න හොඳම උදාහරණය තමයි, ඔයාලා පාවිච්චි කරන TV remote control එක. Remote එකේ buttons තියෙනවා channel මාරු කරන්න, volume අඩු වැඩි කරන්න. ඒත් ඒ buttons වැඩ කරන්නේ කොහොමද, ඇතුලේ circuits මොනවද කරන්නේ කියලා අපිට දැනගන්න ඕනේ නැහැ. අපි දන්නේ channel up button එක එබුවම channel එක වැඩි වෙනවා කියලා විතරයි. මෙන්න මේ විදියට, data (TV channel number, volume level) සහ ඒ data එකට කරන operations (change channel, adjust volume) එකට බඳින එක තමයි Encapsulation කියන්නේ. ඒ වගේම, ඇතුලේ තියෙන complex details user ට පෙන්නන්නේ නැතුව hide කරනවා. මේකට අපි කියනවා Data Hiding කියලා.
ඇයි මේක මෙච්චර වැදගත් වෙන්නේ?
- Data Integrity: වැරදි විදියට data වෙනස් කරන එක වළක්වනවා. හරියට ඔයාගේ බැංකු ගිණුමේ ශේෂය, ඔයාට ඕන විදියට වෙනස් කරන්න බැරි වුණා වගේ. ඒකට methods (withdrawal, deposit) තියෙනවා.
- Flexibility: අනාගතයේදී code එකේ ඇතුලේ වෙනස්කම් කරනකොට, පිටින් පේන interface එකට ඒක බලපාන්නේ නැහැ. Remote එකේ ඇතුල වෙනස් කළත්, button එකේ වැඩේ වෙනස් වෙන්නේ නැහැ.
- Modularity: Code එකේ parts වෙන වෙනම තියාගෙන develop කරන්න, maintain කරන්න, test කරන්න පුළුවන්.
- Security: අනවශ්ය විදියට sensitive data access කරන එකෙන් වළක්වනවා.
Python වල Access Modifiers? (Access Modifiers in Python?)
දැන් ඔයාලා Java, C++ වගේ languages වල code කරලා තියෙනවා නම්, දැකලා ඇති public
, protected
, private
වගේ keywords. මේවාට කියන්නේ Access Modifiers කියලා. මේවා පාවිච්චි කරලා අපිට define කරන්න පුළුවන් class එකක attributes (variables) සහ methods පිටින් access කරන්න පුළුවන්ද, බැරිද කියලා.
ඒත් අයියෝ, Python වල එහෙම keywords නැහැ! ඇත්තටම Python වල මේ වගේ strict access modifiers concept එකක් නැහැ. Python කියන්නේ 'අපි programmersලට freedom දෙනවා, අපිව විශ්වාස කරනවා' කියන philosophy එකේ වැඩ කරන language එකක්. ඒ නිසා Python වල Encapsulation implement කරන්නේ conventions පාවිච්චි කරලා.
ඒ කියන්නේ, අපි attribute එකක් හෝ method එකක් public ද, protected ද, private ද කියලා define කරන්න පාවිච්චි කරන්නේ ඒවා නම් කරන විදිය. ප්රධාන වශයෙන් මේ විදියට category තුනකට බෙදන්න පුළුවන්:
- Public: (No prefix) - මේවා ඕනෑම තැනකින් access කරන්න පුළුවන්.
- Protected: (Single underscore
_
prefix) - මේවා convention එකක් විදියට class එක ඇතුලෙන් සහ sub-classes (inherited classes) ඇතුලෙන් විතරක් පාවිච්චි කරන්න කියලා suggest කරනවා. පිටින් access කරන්න පුළුවන් වුණත්, අපි එහෙම කරන්නේ නැහැ. - Private: (Double underscore
__
prefix) - මේවා class එක ඇතුලෙන් විතරක් පාවිච්චි කරන්න කියලා strong suggestion එකක් දෙනවා. ඇත්තටම මේවා පිටින් access කරන එක ටිකක් අමාරුයි, මොකද Python මේවාට Name Mangling කියන technique එක පාවිච්චි කරනවා.
චුට්ටක් බලමු මේවා individually කොහොමද වැඩ කරන්නේ කියලා.
Public Attributes (Default): ඕනෑම කෙනෙකුට ගෝචර (Accessible to Anyone)
Python වල අපි කිසිම prefix එකක් නැතුව attribute එකක් හෝ method එකක් declare කළොත්, ඒක public වෙනවා. ඒ කියන්නේ, ඒක class එකෙන් පිටත ඕනෑම තැනකින් direct access කරන්න පුළුවන්.
උදාහරණයක්:
class Car:
def __init__(self, brand, model, year):
self.brand = brand # Public attribute
self.model = model # Public attribute
self.year = year # Public attribute
def get_car_info(self):
return f"Brand: {self.brand}, Model: {self.model}, Year: {self.year}"
my_car = Car("Toyota", "Corolla", 2020)
print(f"My car's brand: {my_car.brand}") # Direct access to public attribute
print(my_car.get_car_info())
my_car.year = 2022 # Can directly modify public attribute
print(my_car.get_car_info())
මේකේදී brand
, model
, year
කියන attributes public. ඒ නිසා අපිට my_car.brand
වගේ direct access කරන්නත්, my_car.year = 2022
වගේ direct modify කරන්නත් පුළුවන්. මේක හොඳයි ඉක්මනින් වැඩ කරන applications වලට, ඒත් sensitive data manage කරනකොට මේක එච්චර හොඳ option එකක් නෙවෙයි.
Protected Attributes (_): කවදද මේක පාවිච්චි කරන්නෙ? (Protected Attributes: When to use this?)
Protected attributes කියන්නේ Python වල 'gentleman's agreement' එකක් වගේ. අපි attribute එකක නමට ඉස්සරහින් single underscore (_
) එකක් දැම්මොත්, ඒක Protected කියලා denote කරනවා. ඒ කියන්නේ, අපි developersලා විදියට මේක class එක ඇතුලෙන් සහ inherited classes ඇතුලෙන් විතරක් පාවිච්චි කරනවා කියන එකට commit වෙනවා. ඒත්, Python මේක පිටින් access කරන එක block කරන්නේ නැහැ. අපිට ඕන නම් පිටින් access කරන්න පුළුවන්. ඒත් අපි එහෙම කරන්නේ නැහැ. (Good practices අනුව)
උදාහරණයක්:
class BankAccount:
def __init__(self, account_holder, initial_balance):
self._account_holder = account_holder # Protected attribute (by convention)
self._balance = initial_balance # Protected attribute (by convention)
def deposit(self, amount):
if amount > 0:
self._balance += amount
print(f"Deposited {amount}. New balance: {self._balance}")
else:
print("Deposit amount must be positive.")
def withdraw(self, amount):
if 0 < amount <= self._balance:
self._balance -= amount
print(f"Withdrew {amount}. New balance: {self._balance}")
else:
print("Invalid withdrawal amount or insufficient balance.")
def get_balance(self):
return self._balance
# Using the class
my_account = BankAccount("Kasun Perera", 1000)
print(f"Account holder (conventionally accessed): {my_account._account_holder}")
# Even though it's protected, Python allows direct access:
# my_account._balance = -500 # Don't do this! It breaks encapsulation
my_account.deposit(200)
my_account.withdraw(500)
print(f"Current balance: {my_account.get_balance()}")
මේ උදාහරණයේදී, _account_holder
සහ _balance
කියන attributes protected විදියට හඳුන්වා දීලා තියෙනවා. අපිට my_account._balance
කියලා access කරන්න පුළුවන් වුණත්, හොඳ practice එකක් විදියට අපි ඒකට කෙලින්ම access කරන්නේ නැතුව deposit()
, withdraw()
, get_balance()
වගේ methods පාවිච්චි කරලා ඒ data එක manage කරනවා. මේක තමයි Python වල Encapsulation වලට යොදාගන්න ප්රධාන ක්රමවේදයක්.
Private Attributes (__): ඇත්තටම Private ද? (Private Attributes: Really Private?)
Private attributes තමයි Python වල Encapsulation වල තියෙන ටිකක් complicate වුණත්, පට්ටම feature එකක්. අපි attribute එකක නමට ඉස්සරහින් double underscore (__
) එකක් දැම්මොත්, ඒක Private කියලා denote කරනවා. ඒ කියන්නේ මේ attribute එක class එක ඇතුලෙන් විතරක් access කරන්න පුළුවන් වෙන්න ඕනේ. Python මේක implement කරන්න Name Mangling කියන technique එක පාවිච්චි කරනවා.
Name Mangling කියන්නේ, Python interpreter එක attribute එක compile කරනකොට, ඒකට class එකේ නම add කරලා ඒ attribute එකේ නම වෙනස් කරනවා. උදාහරණයක් විදියට, __secret_code
කියන attribute එක _ClassName__secret_code
කියලා rename කරනවා. මේ නිසා පිටින් direct access කරන එක ටිකක් අමාරු වෙනවා (ඒත් completely impossible නෙවෙයි).
උදාහරණයක්:
class Employee:
def __init__(self, name, emp_id, salary):
self.name = name
self.__emp_id = emp_id # Private attribute
self.__salary = salary # Private attribute
def get_employee_details(self):
return f"Name: {self.name}, Employee ID: {self.__emp_id}, Salary: {self.__salary}"
def __calculate_tax(self):
# This is a private method
return self.__salary * 0.10
def get_net_salary(self):
return self.__salary - self.__calculate_tax()
# Using the class
emp1 = Employee("Nimal Siriwardana", "EMP001", 50000)
print(emp1.get_employee_details())
print(f"Net Salary: {emp1.get_net_salary()}")
# Try to access private attributes directly (will result in AttributeError)
try:
print(emp1.__emp_id)
except AttributeError as e:
print(f"Error: {e} - Cannot access __emp_id directly!")
# However, due to name mangling, you can technically access it like this (not recommended!)
print(f"Accessed via name mangling: {emp1._Employee__emp_id}")
# Try to access private method directly
try:
print(emp1.__calculate_tax())
except AttributeError as e:
print(f"Error: {e} - Cannot access __calculate_tax directly!")
මේ code එකේ __emp_id
සහ __salary
කියන attributes Private. ඒ වගේම __calculate_tax()
කියන method එකත් Private. ඔයාලට පේනවා ඇති, අපි emp1.__emp_id
කියලා access කරන්න හැදුවම AttributeError
එකක් එනවා. ඒත් emp1._Employee__emp_id
කියලා access කරන්න පුළුවන්. මේක තමයි Name Mangling එකේ සූක්ෂම වැඩේ. ඒ නිසා Python වල __
කියන්නේ 'true privacy' එකක් නෙවෙයි, ඒත් accidental access එකක් වළක්වන්න සහ sub-classes වලට attribute names overwrite කරන්න නොදී ආරක්ෂා කරන්න හොඳ ක්රමයක්.
Getters, Setters and Property Decorator: Smart විදියට data Manage කරමු (Getters, Setters and Property Decorator: Let's manage data smartly)
හරි, දැන් අපි Public, Protected, Private attributes ගැන කතා කළානේ. අපි දැක්කා direct access කරන එක සමහර වෙලාවට හොඳ නැහැ කියලා. අන්න ඒකට තමයි Getters (data ගන්න methods) සහ Setters (data set කරන්න methods) පාවිච්චි කරන්නේ.
සාමාන්ය Getters/Setters:
class Product:
def __init__(self, name, price):
self._name = name # Conventionally protected
self._price = 0 # Conventionally protected
self.set_price(price) # Use setter during initialization
# Getter for price
def get_price(self):
return self._price
# Setter for price with validation
def set_price(self, new_price):
if new_price > 0:
self._price = new_price
else:
print("Price cannot be negative or zero!")
# Getter for name
def get_name(self):
return self._name
# Using the class
item = Product("Laptop", 120000)
print(f"Product: {item.get_name()}, Price: {item.get_price()}")
item.set_price(130000) # Valid price update
print(f"New Price: {item.get_price()}")
item.set_price(-50) # Invalid price update - will print error message
print(f"Price after invalid update attempt: {item.get_price()}")
මේකේදී, අපි _price
කියන attribute එක direct access කරන්නේ නැතුව get_price()
සහ set_price()
කියන methods හරහා access කරනවා. set_price()
method එක ඇතුලේ අපිට validation (new_price > 0
) වගේ දේවල් කරන්න පුළුවන්. මේක Encapsulation වලට ගොඩක් වැදගත්.
Pythonic Way: The @property
Decorator
Getters/Setters හොඳ වුණත්, මේක තව smart විදියට කරන්න Python වල සුපිරි feature එකක් තියෙනවා: ඒ තමයි @property
decorator එක. මේකෙන් අපිට methods වගේ පෙනුනත්, attributes වගේ use කරන්න පුළුවන්. ඒ කියන්නේ, direct attribute access කරනවා වගේ පෙනුනත්, ඇත්තටම ඒක පිටිපස්සේ තියෙන getter/setter logic එක call වෙනවා.
class Student:
def __init__(self, name, age):
self._name = name
self._age = 0 # Initialize with a default or invalid value
self.age = age # Use the setter to validate and set age
@property # This makes 'age' act like a getter
def age(self):
print("Getting age...")
return self._age
@age.setter # This defines the setter for 'age'
def age(self, new_age):
print(f"Setting age to {new_age}...")
if 0 < new_age <= 120:
self._age = new_age
else:
print("Invalid age! Age must be between 1 and 120.")
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
if len(new_name) > 2:
self._name = new_name
else:
print("Name must be at least 3 characters long.")
# Using the class
student1 = Student("Amal", 20)
print(f"Student: {student1.name}, Age: {student1.age}") # Accesses via getter
student1.age = 25 # Sets via setter
print(f"Student: {student1.name}, Age: {student1.age}")
student1.age = 150 # Invalid age - will print error message
print(f"Student: {student1.name}, Age: {student1.age}")
student1.name = "Al"
print(f"Student: {student1.name}, Age: {student1.age}")
දැන් බලන්න! අපි student1.age = 25
කියලා assign කරනකොට, ඒක ඇත්තටම @age.setter
යටතේ තියෙන method එක call කරන්නේ. ඒ වගේම student1.age
කියලා access කරනකොට, ඒක @property
decorator එක යටතේ තියෙන getter method එක call කරනවා. මේක තමයි Pythonic way එක. මේකෙන් code එක readability වැඩි වෙනවා වගේම, data validation වගේ දේවල් කරන්නත් පට්ටම පහසුයි.
Summing it Up: අවසාන වචනය (Summary & Final Words)
ඉතින් යාලුවනේ, අද අපි Python වල Encapsulation සහ Access Modifiers ගැන ගොඩක් දේවල් කතා කළා. මුලින්ම Encapsulation කියන්නේ මොකක්ද, ඒකේ වැදගත්කම මොකක්ද කියලා තේරුම් ගත්තා. ඊට පස්සේ, Python වල strict access modifiers නැති වුණත්, public
, _protected
, __private
කියන convention තුන පාවිච්චි කරලා data protect කරන හැටි අපි දැක්කා.
විශේෂයෙන්ම මතක තියාගන්න ඕනේ දේවල්:
- Python Trusts You: Python වල strict privacy එකක් නැහැ, ඒක developersලාව විශ්වාස කරනවා. ඒ නිසා conventions follow කරන එක වැදගත්.
_
(Single Underscore): මේක Protected attribute එකක් කියලා signify කරනවා. Class එකෙන් පිටින් access නොකර ඉන්න කියන 'gentleman's agreement' එකක් වගේ.__
(Double Underscore): මේක Private කියලා signify කරනවා. Name Mangling නිසා පිටින් direct access කරන්න අමාරුයි, ඒත් impossible නෙවෙයි. මේක ප්රධාන වශයෙන් class එක ඇතුලේ internal use වලට සහ inheritance වලදී name clashes වළක්වන්න පාවිච්චි කරනවා.@property
Decorator: මේක තමයි Python වල Encapsulation implement කරන්න තියෙන වඩාත්ම elegant සහ powerful tool එක. මේකෙන් code එක clean වෙනවා වගේම, data access කරනකොට complex logic (validation වගේ) add කරන්නත් පුළුවන්.
Encapsulation කියන්නේ හොඳ, maintainable, secure software එකක් හදන්න අත්යවශ්ය concept එකක්. මේක තේරුම් ගෙන වැඩ කරන එක ඔයාලගේ coding skills වලට පට්ට ගැම්මක් තමයි දෙන්නේ.
ඔයාලා දැන් මේ concepts අරගෙන, ඔයාලගේම project එකක පොඩි class එකක් හදලා මේවා apply කරලා බලන්න. එතකොට තමයි මේක ඇත්තටම ඔලුවට යන්නේ. මේ ගැන ඔයාලට තව මොනවා හරි දැනගන්න ඕන නම්, ප්රශ්න තියෙනවා නම්, නැත්නම් මේකේ මොනවා හරි add කරන්න පුළුවන් කියලා හිතෙනවා නම්, comment section එකේ කියන්න. අපි කතා කරමු. Programming journey එකට සුබ පැතුම්!