سطح مقدماتی (Beginner Level)
در برنامهنویسی شیگرا (OOP)، یکی از اصول مهم کپسولهسازی (Encapsulation) است؛ یعنی محدود کردن دسترسی مستقیم به متغیرهای داخلی یک کلاس.
در زبانهای قدیمیتر مثل جاوا، برای خواندن یا تغییر یک متغیر از متدهای get_variable() و set_variable() استفاده میشد. اما در پایتون، ما از ابزاری قدرتمند و تمیزتر به نام Property استفاده میکنیم.
چرا از Property استفاده کنیم؟
فرض کنید یک کلاس دارید که سن کاربر را ذخیره میکند. اگر کاربر عدد منفی وارد کند چه میشود؟
- دسترسی مستقیم (بد): هیچ کنترلی روی داده ورودی ندارید.
- متد Getter/Setter (روش قدیمی): کد را شلوغ و خوانایی را کم میکند.
- Property (روش پایتونیک): هم ظاهر کد ساده است (مثل دسترسی مستقیم) و هم منطق اعتبارسنجی اجرا میشود.
تعریف Getter و Setter
با استفاده از دکوریتور @property میتوانیم یک متد را طوری تعریف کنیم که مثل یک ویژگی (Attribute) رفتار کند.
@property: برای خواندن مقدار (Getter).@name.setter: برای مقداردهی (Setter).
ویژگیهای فقط خواندنی (Read-Only)
اگر فقط @property را تعریف کنید و بخش .setter را ننویسید، آن ویژگی غیرقابل تغییر میشود. این برای مقادیری که محاسبه میشوند (مثل مساحت) عالی است.
# Static Snippet: Syntax structure
class MyClass:
@property
def my_var(self):
# Logic to retrieve
pass
@my_var.setter
def my_var(self, value):
# Logic to set/validate
pass
سطح پیشرفته (Professional Level)
در سطح حرفهای، property فراتر از یک سینتکس ساده است. درک نحوه کارکرد آن در سطح کلاس و تعامل آن با Descriptorها، مدیریت حافظه و Deleterها ضروری است.
حذف ویژگی (Deleter)
مشابه Getter و Setter، میتوانید یک deleter تعریف کنید که هنگام اجرای دستور del object.attribute فراخوانی شود. این برای پاکسازی منابع (مثل بستن فایل یا کانکشن دیتابیس) هنگام حذف یک ویژگی مفید است.
استفاده از تابع property() (روش کلاسیک)
دکوریتورها صرفاً "Syntactic Sugar" هستند. در پایتون میتوان property را مستقیماً به عنوان یک تابع در کلاس صدا زد. این روش در کدهای قدیمی یا زمانی که میخواهید ویژگیها را به صورت داینامیک بسازید کاربرد دارد.
سینتکس: property(fget, fset, fdel, doc)
نکات فنی و Best Practiceها
- هزینه محاسباتی (Computed Properties): اگر منطق داخل
@propertyسنگین است (مثل کوئری دیتابیس)، بهتر است آن را کش (Cache) کنید یا از یک متد معمولی استفاده کنید تا برنامهنویس بداند که اجرای آن هزینهبر است. دسترسی به Attribute معمولاً باید سریع (O(1)) باشد. - ارثبری (Inheritance): پراپرتیها ارثبری میشوند، اما اگر بخواهید فقط Setter را در کلاس فرزند تغییر دهید، سینتکس کمی پیچیده میشود (
@ParentClass.attr.setter). - Naming Convention: متغیر واقعی که داده را نگه میدارد معمولاً با یک آندرلاین شروع میشود (
self._x) تا با نام پراپرتی (x) تداخل پیدا نکند و نشاندهنده "محافظت شده" بودن آن باشد.
# Advanced Snippet: Abstract Property
from abc import ABC, abstractmethod
class Base(ABC):
@property
@abstractmethod
def value(self):
"""باید در کلاس فرزند پیادهسازی شود"""
pass