در درسهای قبلی با Class، Object، __init__، Method و Inheritance آشنا شدیم.
حالا میخواهیم یکی دیگر از مفاهیم مهم برنامهنویسی شیءگرا یعنی Encapsulation یا کپسولهسازی را یاد بگیریم.
کپسولهسازی چیست؟
کپسولهسازی یعنی دادهها و رفتارهای مربوط به یک Object را داخل یک کلاس قرار دهیم و در صورت نیاز، نحوه دسترسی مستقیم به بعضی از دادهها را محدود کنیم.
هدف این است که کنترل بیشتری روی نحوه استفاده و تغییر دادههای داخلی Object داشته باشیم.
تصور کنید یک دستگاه را استفاده میکنید. لازم نیست بدانید تمام اجزای داخلی دستگاه چگونه کار میکنند؛ بلکه از رابطی که برای شما فراهم شده استفاده میکنید.
ویژگی عمومی (Public)
در پایتون اگر یک ویژگی را به شکل معمولی تعریف کنیم، به طور معمول از بیرون کلاس قابل دسترسی است.
class Student:
def __init__(self, name):
self.name = name
student1 = Student("Ali")
print(student1.name)
خروجی:
Ali
ویژگی با یک _
در پایتون اگر نام یک ویژگی را با یک
_
شروع کنیم، معمولاً این موضوع را نشان میدهد که ویژگی
برای استفاده داخلی کلاس در نظر گرفته شده است.
class Student:
def __init__(self, name):
self._name = name
student1 = Student("Ali")
print(student1._name)
یک underscore به تنهایی دسترسی را واقعاً مسدود نمیکند. بیشتر یک قرارداد بین برنامهنویسان است که میگوید: «این ویژگی بهتر است مستقیماً از بیرون استفاده نشود.»
ویژگی با دو __
اگر نام یک ویژگی را با دو underscore شروع کنیم، پایتون رفتار متفاوتی با آن دارد.
class Student:
def __init__(self, name):
self.__name = name
student1 = Student("Ali")
حالا دسترسی مستقیم با:
print(student1.__name)
باعث ایجاد
AttributeError
میشود.
دلیل آن این است که پایتون برای ویژگیهایی که با دو underscore شروع میشوند، از مکانیزمی به نام Name Mangling استفاده میکند.
چگونه به ویژگی خصوصی دسترسی داشته باشیم؟
یکی از روشهای رایج، استفاده از متدهایی برای خواندن یا تغییر مقدار است. این متدها معمولاً با نامهای Getter و Setter شناخته میشوند.
Getter چیست؟
Getter متدی است که مقدار یک ویژگی را برمیگرداند.
class Student:
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
student1 = Student("Ali")
print(student1.get_name())
خروجی:
Ali
Setter چیست؟
Setter متدی است که برای تغییر مقدار یک ویژگی استفاده میشود.
class Student:
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
def set_name(self, new_name):
self.__name = new_name
student1 = Student("Ali")
print(student1.get_name())
student1.set_name("Reza")
print(student1.get_name())
خروجی:
Ali Reza
مزیت Getter و Setter
یکی از مزایای مهم Getter و Setter این است که میتوانیم قبل از تغییر مقدار، اطلاعات را بررسی کنیم.
class Student:
def __init__(self, age):
self.__age = age
def get_age(self):
return self.__age
def set_age(self, age):
if age >= 0:
self.__age = age
else:
print("Age cannot be negative")
student1 = Student(20)
student1.set_age(25)
print(student1.get_age())
در این مثال اجازه نمیدهیم سن یک مقدار منفی شود.
مثال حساب بانکی
یکی از مثالهای مناسب برای درک کپسولهسازی، حساب بانکی است.
فرض کنید موجودی حساب نباید مستقیماً از بیرون تغییر کند.
class BankAccount:
def __init__(self, balance):
self.__balance = balance
def get_balance(self):
return self.__balance
def deposit(self, amount):
if amount > 0:
self.__balance += amount
account1 = BankAccount(1000000)
print(account1.get_balance())
account1.deposit(500000)
print(account1.get_balance())
خروجی:
1000000 1500000
__balanceداده داخلی حساب است.get_balance()موجودی را برمیگرداند.deposit()برای افزایش موجودی استفاده میشود.- در متد
deposit()مقدار ورودی بررسی میشود.
کپسولهسازی به معنی امنیت کامل نیست
یک نکته مهم درباره پایتون این است که ویژگیهایی که با
__
شروع میشوند، به معنای واقعی کلمه غیرقابل دسترسی نیستند.
پایتون بیشتر روی قراردادها و جلوگیری از دسترسی ناخواسته تأکید دارد و مکانیزم Name Mangling را برای نامهای دارای دو underscore به کار میبرد.
بنابراین بهتر است به جای اینکه کپسولهسازی را «مخفیسازی کامل اطلاعات» بدانیم، آن را روشی برای کنترل و سازماندهی دسترسی به دادهها در نظر بگیریم.
Property در پایتون
در پایتون روش دیگری نیز برای ساخت Getter و Setter وجود دارد که از
@property
استفاده میکند.
class Student:
def __init__(self, age):
self.__age = age
@property
def age(self):
return self.__age
@age.setter
def age(self, value):
if value >= 0:
self.__age = value
student1 = Student(20)
print(student1.age)
student1.age = 25
print(student1.age)
این روش در پروژههای واقعی بسیار کاربردی است، اما برای شروع یادگیری کپسولهسازی بهتر است ابتدا مفهوم Getter و Setter را به خوبی درک کنیم.
تفاوت Public، _ و __
- name → ویژگی معمولی و عمومی
- _name → قرارداد برای استفاده داخلی یا محافظتشده
- __name → استفاده از Name Mangling برای جلوگیری از دسترسی مستقیم معمول
چرا کپسولهسازی مهم است؟
- کنترل بهتر روی دادهها
- جلوگیری از تغییرات ناخواسته
- اعتبارسنجی دادهها قبل از ذخیره
- سازماندهی بهتر کد
- نگهداری سادهتر پروژههای بزرگ
تمرین
یک کلاس به نام BankAccount ایجاد کنید.
این کلاس باید یک ویژگی خصوصی به نام
__balance
داشته باشد.
سپس دو متد ایجاد کنید:
-
deposit()برای افزایش موجودی -
get_balance()برای نمایش موجودی
در متد
deposit()
اجازه ندهید مقدار صفر یا منفی به موجودی اضافه شود.
جمعبندی
کپسولهسازی یکی از مفاهیم مهم OOP است که به ما کمک میکند دادهها و رفتارهای Object را بهتر سازماندهی کنیم و نحوه دسترسی به دادهها را کنترل کنیم.
در این درس یاد گرفتیم:
- کپسولهسازی چیست.
- تفاوت ویژگی عمومی و ویژگیهای با underscore را بررسی کردیم.
- با
__nameو Name Mangling آشنا شدیم. - Getter و Setter را یاد گرفتیم.
- با
@propertyآشنا شدیم. - یک مثال واقعی از حساب بانکی ساختیم.