کپسوله‌سازی در پایتون

آموزش Encapsulation، ویژگی‌های خصوصی، Getter و Setter در Python

در درس‌های قبلی با 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 را بهتر سازمان‌دهی کنیم و نحوه دسترسی به داده‌ها را کنترل کنیم.

در این درس یاد گرفتیم:

درس بعدی: در درس بعدی با چندریختی (Polymorphism) آشنا می‌شویم؛ مفهومی که یکی از بخش‌های مهم OOP و ادامه طبیعی بحث وراثت است.