خانه / آموزش‌ها / دسکریپتورها در پایتون (Python Descriptors)

دسکریپتورها در پایتون (Python Descriptors)

🐍 HomeOfPython
|
📅 1404/10/22

سطح مقدماتی (Beginner Level)

دسکریپتورها (Descriptors) یکی از قدرتمندترین ویژگی‌های پایتون هستند که اغلب در پس‌زمینه کار می‌کنند، اما درک آن‌ها دید شما را نسبت به نحوه کارکرد کلاس‌ها تغییر می‌دهد.

به زبان ساده، دسکریپتور شیئی است که رفتار دسترسی به ویژگی‌ها (Attributes) را تغییر می‌دهد. یعنی وقتی شما می‌نویسید obj.x، obj.x = 10 یا del obj.x، دسکریپتور تصمیم می‌گیرد چه اتفاقی بیفتد.

این دقیقاً همان مکانیزمی است که @property از آن استفاده می‌کند تا متدها را مانند متغیرها صدا بزنید.

مفهوم اول: چرا به دسکریپتور نیاز داریم؟

فرض کنید یک کلاس برای موجودی حساب بانکی دارید و می‌خواهید مطمئن شوید موجودی هرگز منفی نمی‌شود. در حالت عادی شاید از getter و setter استفاده کنید، اما دسکریپتور راهی تمیزتر برای مدیریت این منطق است.

Python

مفهوم دوم: استفاده از Property (ساده‌ترین دسکریپتور)

قبل از نوشتن دسکریپتورهای سفارشی، باید بدانید که تابع داخلی property() خودش یک دسکریپتور است.

Python

سطح پیشرفته (Professional Level)

در سطح حرفه‌ای، دسکریپتورها کلاس‌هایی هستند که پروتکل دسکریپتور (Descriptor Protocol) را پیاده‌سازی می‌کنند. این پروتکل شامل سه متد جادویی است:

  1. __get__(self, obj, objtype)
  2. __set__(self, obj, value)
  3. __delete__(self, obj)

اگر کلاسی حتی یکی از این‌ها را داشته باشد، یک دسکریپتور است.

انواع دسکریپتورها

  1. Data Descriptor: متدهای __set__ یا __delete__ را دارد.
  2. Non-Data Descriptor: فقط متد __get__ را دارد (مثل متدها یا functools.cached_property).

ساخت یک دسکریپتور اعتبارسنجی (Validator)

فرض کنید می‌خواهید در چندین کلاس مختلف، چک کنید که یک عدد همیشه مثبت باشد. به جای تکرار @property در هر کلاس، یک کلاس دسکریپتور می‌سازیم.

Python

متد __set_name__ (جدید در Python 3.6+)

در مثال بالا مجبور بودیم نام متغیر ("price") را دستی به دسکریپتور بدهیم. متد __set_name__ این کار را خودکار می‌کند. این متد زمانی صدا زده می‌شود که دسکریپتور در یک کلاس تعریف می‌شود.

Python

نکات فنی و Performance

  1. اولویت (Precedence): یک Data Descriptor همیشه بر محتویات __dict__ نمونه (Instance) اولویت دارد. اما یک Non-Data Descriptor (مثل متدها) می‌تواند توسط __dict__ سایه (Shadow) شود.
  2. حافظه: استفاده از __slots__ همراه با دسکریپتورها می‌تواند مصرف حافظه را به شدت کاهش دهد.
  3. کاربرد: ORMها (مثل Django و SQLAlchemy) به شدت از دسکریپتورها برای تبدیل داده‌های دیتابیس به اشیاء پایتون استفاده می‌کنند.
python
# Static Snippet: How methods are actually descriptors
class MyClass:
    def method(self):
        pass

# When you access MyClass.method, it returns the function itself.
# When you access instance.method, the function's __get__ binds it to the instance.