البرمجة

دليل المزخرفات في بايثون

المزخرفات (Decorators) في بايثون: دليل شامل ومفصل

تُعد المزخرفات (Decorators) أحد المفاهيم المتقدمة في لغة بايثون، وتمثل جزءًا محوريًا من قوة اللغة ومرونتها، حيث توفر طريقة أنيقة وفعالة لتعديل وظائف أو أصناف أو طرق دون تغيير الكود الأصلي لها. يسمح هذا المفهوم للمبرمجين بتطبيق مبادئ التصميم النظيفة مثل فصل الاهتمامات (Separation of Concerns) وإعادة الاستخدام (Reusability) بطريقة بسيطة ومقروءة.

يندرج مفهوم المزخرفات ضمن نمط التصميم Decorator Pattern في البرمجة الكائنية التوجه، وهو نمط يُستخدم لتوسيع سلوك الكائنات بشكل ديناميكي، دون الحاجة إلى تعديل الكائنات نفسها. في بايثون، تتخذ المزخرفات شكلاً بسيطًا لكنها قوية، باستخدام معاملات الدوال.


أولاً: الفهم الأساسي للمزخرفات

في بايثون، الدوال هي كائنات من الدرجة الأولى (First-Class Objects)، مما يعني أنه يمكن تمريرها كوسائط لدوال أخرى، إرجاعها من دوال، أو حتى تخزينها في متغيرات. هذه الميزة تُمكّن المزخرفات من العمل.

ما هو المزخرف (Decorator)؟

المزخرف في بايثون هو دالة تأخذ دالة أخرى كوسيط وتُعيد دالة جديدة تُعدل أو توسّع سلوك الدالة الأصلية.

الشكل العام لمزخرف بسيط:

python
def decorator_function(original_function): def wrapper_function(): print("قبل تنفيذ الدالة") original_function() print("بعد تنفيذ الدالة") return wrapper_function @decorator_function def say_hello(): print("مرحباً") say_hello()

في هذا المثال، say_hello مزخرفة باستخدام decorator_function عبر التعليمة @decorator_function. تقوم الدالة wrapper_function بإحاطة الدالة الأصلية say_hello وإضافة سلوك قبل وبعد تنفيذها.


ثانياً: المزخرفات والدوال ذات الوسائط

غالباً ما تحتوي الدوال على وسائط. لتكون المزخرفات قابلة لإعادة الاستخدام مع دوال متعددة بوسائط مختلفة، ينبغي استخدام معاملات ديناميكية باستخدام *args و **kwargs.

مثال عملي:

python
def my_decorator(func): def wrapper(*args, **kwargs): print("تم استدعاء الدالة") result = func(*args, **kwargs) print("تم إنهاء الدالة") return result return wrapper @my_decorator def add(x, y): return x + y print(add(5, 3))

هذا المزخرف يمكن استخدامه مع أي دالة تحتوي على عدد متغير من الوسائط.


ثالثاً: مزخرفات تقبل وسائط

في بعض الأحيان، نرغب بتمرير وسائط للمزخرف نفسه. لتنفيذ ذلك، يجب إنشاء دالة تُعيد مزخرفًا.

مثال توضيحي:

python
def repeat(n): def decorator(func): def wrapper(*args, **kwargs): for _ in range(n): func(*args, **kwargs) return wrapper return decorator @repeat(3) def greet(name): print(f"مرحباً {name}") greet("أحمد")

في هذا المثال، repeat هو مزخرف يقبل وسيطًا (عدد التكرار)، ويعيد مزخرفًا داخليًا.


رابعاً: استخدام المزخرفات في تطبيقات حقيقية

تُستخدم المزخرفات في العديد من التطبيقات البرمجية، مثل تسجيل الأحداث، التفويض، التحقق من الصلاحيات، والقياس الزمني لأداء الدوال.

1. تسجيل الأحداث (Logging)

python
def log(func): def wrapper(*args, **kwargs): print(f"تشغيل الدالة: {func.__name__}") return func(*args, **kwargs) return wrapper @log def process_data(): print("جاري معالجة البيانات...") process_data()

2. التحقق من الصلاحيات (Authorization)

python
def requires_permission(role): def decorator(func): def wrapper(user_role, *args, **kwargs): if user_role != role: print("لا تملك الصلاحيات اللازمة") return return func(*args, **kwargs) return wrapper return decorator @requires_permission("admin") def delete_database(): print("تم حذف قاعدة البيانات") delete_database("guest") # سيتم رفض الوصول delete_database("admin") # سيتم التنفيذ

3. قياس وقت تنفيذ الدوال

python
import time def timing(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"المدة الزمنية للتنفيذ: {end - start:.5f} ثانية") return result return wrapper @timing def slow_function(): time.sleep(2) print("انتهت العملية البطيئة") slow_function()

خامساً: مزخرفات مدمجة في بايثون

توفر بايثون مزخرفات مدمجة تُستخدم مع الأصناف وخصائصها. ومن أشهر هذه المزخرفات:

المزخرف الوظيفة
@staticmethod تعريف دالة ثابتة لا تعتمد على الحالة الداخلية للكائن
@classmethod تعريف دالة مرتبطة بالصنف وليس بالكائن
@property تحويل دالة إلى خاصية يمكن الوصول لها دون استخدام الأقواس

مثال توضيحي:

python
class Circle: def __init__(self, radius): self._radius = radius @property def diameter(self): return self._radius * 2 @staticmethod def unit_circle(): return Circle(1) @classmethod def from_diameter(cls, diameter): return cls(diameter / 2) c = Circle.unit_circle() print(c.diameter) c2 = Circle.from_diameter(10) print(c2.diameter)

سادساً: استخدام functools.wraps للحفاظ على بيانات الدالة

عند استخدام المزخرفات، قد تضيع بيانات تعريف الدالة الأصلية مثل اسمها أو توثيقها. لحل ذلك، تُستخدم دالة functools.wraps من المكتبة القياسية functools.

مثال:

python
from functools import wraps def log(func): @wraps(func) def wrapper(*args, **kwargs): print(f"تشغيل {func.__name__}") return func(*args, **kwargs) return wrapper @log def process(): """هذه دالة تقوم بمعالجة البيانات""" print("معالجة...") print(process.__name__) # بدون wraps: 'wrapper'، مع wraps: 'process' print(process.__doc__)

سابعاً: تكديس المزخرفات (Chaining Decorators)

يمكن استخدام أكثر من مزخرف مع نفس الدالة، حيث يتم تطبيقها بالتسلسل من الأقرب إلى الدالة إلى الأبعد.

python
def decorator1(func): def wrapper(*args, **kwargs): print("تشغيل المزخرف الأول") return func(*args, **kwargs) return wrapper def decorator2(func): def wrapper(*args, **kwargs): print("تشغيل المزخرف الثاني") return func(*args, **kwargs) return wrapper @decorator1 @decorator2 def hello(): print("مرحباً بالعالم") hello()

الإخراج سيكون:

تشغيل المزخرف الأول تشغيل المزخرف الثاني مرحباً بالعالم

ثامناً: المزخرفات في إطار العمل Django

Retry