تخطَّ إلى المحتوى

🐍 شرح Python

الديكوريتورز (Decorators)

الدرس 29 من 30· ⏱ 3 دقائق قراءة

ما هو الديكوريتور؟

في بايثون، الدوال كائنات مثل أي قيمة أخرى — يمكن تمريرها لدالة أخرى أو إعادتها من دالة. الديكوريتور يستغل هذا: دالة تأخذ دالة أخرى وتُرجع نسخة معدَّلة منها، تضيف سلوكًا إضافيًّا دون لمس كودها الأصلي.

رمز @ مجرّد اختصار (syntactic sugar):

@my_decorator
def say_hello():
    print("أهلًا")

# يكافئ تمامًا:
def say_hello():
    print("أهلًا")
say_hello = my_decorator(say_hello)

كتابة ديكوريتور بسيط

def log_call(func):
    def wrapper(*args, **kwargs):
        print(f"استدعاء: {func.__name__}")
        result = func(*args, **kwargs)
        print(f"انتهى: {func.__name__}")
        return result
    return wrapper

@log_call
def add(a, b):
    return a + b

print(add(3, 4))
# استدعاء: add
# انتهى: add
# 7

💡 *args و**kwargs في الدالة الداخلية wrapper ضروريان — بدونهما لن يعمل الديكوريتور مع أي دالة تأخذ معاملات مختلفة.

تكديس عدّة ديكوريتورز

يمكن وضع أكثر من ديكوريتور فوق نفس الدالة. الترتيب مهم: يُطبَّق الأقرب للدالة أولًا، ثم الذي فوقه:

@f1
@f2
def func():
    pass

# يكافئ:
func = f1(f2(func))

ديكوريتورز جاهزة داخل الأصناف

بايثون توفّر ثلاثة ديكوريتورز مدمجة شائعة جدًا في البرمجة الكائنية:

@staticmethod — دالة لا تحتاج self

class MathUtils:
    @staticmethod
    def is_even(n):
        return n % 2 == 0

print(MathUtils.is_even(4))   # True — بلا الحاجة لإنشاء كائن

مناسبة لدالة منطقيًّا تابعة للصنف لكنها لا تقرأ أو تعدّل بيانات الكائن.

@classmethod — دالة تستقبل الصنف نفسه (cls)

class Student:
    school_name = "مدرسة النور"

    def __init__(self, name):
        self.name = name

    @classmethod
    def change_school(cls, new_name):
        cls.school_name = new_name

Student.change_school("مدرسة الأمل")
print(Student.school_name)   # مدرسة الأمل

تستقبل الصنف cls بدل الكائن self — مفيدة لتعديل بيانات مشتركة بين كل الكائنات، أو كطريقة بديلة لإنشاء كائنات.

@property — التعامل مع دالة كأنها خاصية عادية

تتيح تشغيل كود عند القراءة أو الكتابة، بينما يبدو الاستخدام من الخارج كوصول عادي لخاصية بدون أقواس:

class Circle:
    def __init__(self, radius):
        self._radius = radius     # خاصية داخلية بشرطة سفلية

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value <= 0:
            raise ValueError("نصف القطر يجب أن يكون موجبًا")
        self._radius = value

    @property
    def area(self):
        return 3.14159 * self._radius ** 2

c = Circle(5)
print(c.radius)   # 5   — بدون أقواس، مثل خاصية عادية
print(c.area)      # 78.53975  — محسوبة تلقائيًا عند كل قراءة

c.radius = 10      # يستدعي الـ setter ويتحقّق من القيمة

⚠️ سمّ الخاصية الداخلية باسم مختلف (self._radius) عن اسم الـ property (radius). لو كتبت return self.radius داخل الـ property نفسه، ستحصل على استدعاء لا نهائي لنفسه (خطأ تجاوز حدّ العمق التكراري).

جرّب بنفسك

🐍 محرّر بايثون
📥 المدخلات (input) — قيمة في كل سطر
وحدة التحكّم
اضغط «تشغيل» لرؤية النتيجة هنا.

أخطاء شائعة

  • نسيان *args, **kwargs عند كتابة wrapper داخل ديكوريتور مخصّص، فيفشل مع أي دالة بمعاملات مختلفة.
  • استخدام نفس اسم الخاصية الداخلية والـ property فيسبّب استدعاءً لا نهائيًا لنفسه.
  • استخدام @staticmethod لدالة تحتاج فعليًّا للوصول لبيانات الكائن — استخدم دالة عادية بـ self بدلًا منها.

🎯 التالي: الخلاصة وما بعد بايثون.

شرح الديكوريتورز (Decorators) — Python بالعربي
الديكوريتورز (Decorators)Python بالعربي · The Code Fix

📚 لمزيد من التعمّق في Python، راجِع التوثيق الرسمي لـ Python.

هل كان هذا الدرس مفيدًا؟