نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
المقالات/Python
Python

Python Decorators: من الفهم العميق إلى الاستخدام الاحترافي في الإنتاج

اكتشف كيف تحول الـ Decorators في بايثون من مجرد أداة تجميلية إلى سلاح سري في تحسين الأداء، إدارة الأخطاء، ومراقبة الإنتاج. أمثلة عملية من شركات حقيقية وفخاخ يجب تجنبها.

فريق نوفيل٢٠ أغسطس ٢٠٢٦8 دقائق قراءة٩ مشاهدة

في أحد المشاريع الكبيرة الذي عملت عليه مع فريق من ١٢ مطوراً، كنا نواجه مشكلة غريبة: السيرفر كان يتجمد تماماً كل يومين دون أي خطأ ظاهر في الـ Logs. بعد أيام من البحث، اكتشفنا أن أحد الـ Decorators الذي كتبه مطور مبتدئ كان يحجز ٢٠ ميغابايت من الذاكرة في كل طلب دون تحريرها. المشكلة لم تكن في الـ Decorator نفسه، بل في كيفية استخدامه لـ nonlocal variables داخل nested functions. هذه الحادثة جعلتني أدرك أن الـ Decorators في بايثون ليست مجرد سكر نحوي (syntactic sugar)، بل أداة قوية تتطلب فهماً عميقاً لكيفية عملها خلف الكواليس.

الـ Decorators هي واحدة من أقوى ميزات بايثون، لكنها أيضاً واحدة من أكثر الميزات سوء فهم. الكثير من المطورين يستخدمونها لإضافة logging أو timing بسيط، لكن قليلون يفهمون كيف تعمل حقاً في الذاكرة، وكيف يمكن استخدامها لحل مشاكل حقيقية في الإنتاج مثل الـ Caching، الـ Retry Logic، وحتى الـ Circuit Breaking. في هذا المقال، سنذهب أبعد من الأمثلة التافهة مثل @timed أو @logged، ونستكشف كيف يمكن للـ Decorators أن تكون سلاحاً سرياً في ترسانة المطور المحترف.

كيف تعمل الـ Decorators حقاً؟ تشريح ما يحدث خلف الكواليس

عندما ترى سطراً مثل @cache قبل دالة، قد تظن أنه مجرد اختصار لكتابة دالة جديدة. لكن الحقيقة أكثر تعقيداً. الـ Decorator هو في الواقع دالة تأخذ دالة أخرى وتعيد دالة ثالثة. هذا يعني أن بايثون تقوم بثلاث خطوات خلف الكواليس: أولاً، تُنشئ الدالة الأصلية في الذاكرة. ثانياً، تمرر هذه الدالة كمعامل إلى الـ Decorator. ثالثاً، تستبدل الدالة الأصلية بالدالة الجديدة التي عاد بها الـ Decorator. هذه العملية تحدث أثناء تحميل الكود، قبل حتى تشغيل البرنامج، وهذا ما يجعل الـ Decorators فعالة جداً في تحسين الأداء.

لفهم هذا بعمق، دعونا ننظر إلى ما يحدث في الذاكرة. عندما تكتب @retry قبل دالة get_data، بايثون لا تقوم فقط باستبدال الدالة، بل تنشئ closure يحتفظ بمرجع للدالة الأصلية. هذا الـ Closure هو ما يسمح للـ Decorator بالوصول إلى الدالة الأصلية حتى بعد انتهاء تنفيذ الـ Decorator نفسه. المشكلة هنا أن الكثير من المطورين ينسون أن هذا الـ Closure يحجز مساحة في الذاكرة، وإذا لم يتم تصميمه بعناية، يمكن أن يؤدي إلى تسرب ذاكرة (memory leaks) كما حدث في مشروعي السابق. في بايثون، الـ Closures تحتفظ بمراجع لجميع المتغيرات في النطاق الخارجي، وهذا يعني أنه إذا كان لديك قائمة كبيرة داخل الـ Decorator، فإنها لن تُحرر من الذاكرة حتى يتم تدمير الـ Decorator نفسه.

python
import sys

def debug_memory(func):
 def wrapper(*args, **kwargs):
 print(f"Memory usage before {func.__name__}: {sys.getsizeof(func)} bytes")
 result = func(*args, **kwargs)
 print(f"Memory usage after {func.__name__}: {sys.getsizeof(wrapper)} bytes")
 return result
 return wrapper

@debug_memory
def process_large_data(data):
 # معالجة بيانات ضخمة
 return [x * 2 for x in data]

# تجربة مع بيانات كبيرة
large_data = list(range(10**6))
process_large_data(large_data)

الـ Decorators المتقدمة: من الـ Caching إلى الـ Circuit Breaking

في الإنتاج، الـ Decorators يمكن أن تكون أكثر من مجرد أداة لقياس الوقت أو تسجيل الأخطاء. في شركة أوبر مثلاً، يستخدمون الـ Decorators لتنفيذ الـ Rate Limiting على مستوى الدوال، مما يسمح لهم بالتحكم في عدد الطلبات التي يمكن لكل مستخدم إرسالها في الثانية دون الحاجة لتعديل كل دالة على حدة. في هذا القسم، سنستكشف بعض الاستخدامات المتقدمة التي يمكن أن تغير طريقة تفكيرك في الـ Decorators.

أولاً، دعونا نتحدث عن الـ Caching. معظم المطورين يعرفون عن @lru_cache من مكتبة functools، لكن قليلون يفهمون كيف يمكن تخصيصه لحالات محددة. مثلاً، إذا كنت تعمل مع بيانات تتغير بشكل دوري، يمكنك إنشاء decorator مخصص يقوم بتحديث الـ Cache كل فترة زمنية محددة. هذا مفيد جداً في تطبيقات مثل لوحات التحكم التي تعرض بيانات في الوقت الفعلي. المشكلة مع @lru_cache هي أنها تحتفظ بالبيانات في الذاكرة إلى الأبد، مما يمكن أن يؤدي إلى استهلاك كبير للذاكرة إذا كانت البيانات كبيرة أو كثيرة.

python
import time
from functools import wraps

def timed_cache(seconds):
 def decorator(func):
 cache = {}
 last_updated = 0
 
 @wraps(func)
 def wrapper(*args):
 nonlocal last_updated
 current_time = time.time()
 
 if current_time - last_updated > seconds:
 cache.clear()
 last_updated = current_time
 print("Cache cleared and updated")
 
 if args not in cache:
 cache[args] = func(*args)
 return cache[args]
 return wrapper
 return decorator

@timed_cache(sec10)
def get_expensive_data(param):
 # محاكاة عملية مكلفة
 time.sleep(2)
 return f"Result for {param}"

# التجربة
print(get_expensive_data("test")) # سيستغرق 2 ثانية
print(get_expensive_data("test")) # سيستخدم الكاش
# بعد 10 ثوانٍ، سيتم تحديث الكاش

الـ Circuit Breaker: كيف تحمي نظامك من الفشل المتتالي

في الأنظمة الموزعة، الفشل هو القاعدة وليس الاستثناء. إذا كان لديك خدمة تعتمد على خدمة خارجية، فإن فشل تلك الخدمة يمكن أن يؤدي إلى فشل متتالي في نظامك بأكمله. هنا يأتي دور نمط الـ Circuit Breaker، والذي يمكن تنفيذه بسهولة باستخدام الـ Decorators. الفكرة بسيطة: إذا فشل الطلب أكثر من عدد معين من المرات، يتم فتح الـ Circuit، ويتم إعادة الخطأ فوراً دون محاولة الاتصال بالخدمة الخارجية. بعد فترة زمنية محددة، يتم إغلاق الـ Circuit تلقائياً للسماح بمحاولة جديدة.

في شركة نتفليكس، يستخدمون هذا النمط على نطاق واسع لحماية خدماتهم من الفشل. المثير للاهتمام هنا هو أن الـ Decorator لا يقوم فقط بحماية النظام، بل يمكنه أيضاً تسجيل البيانات عن حالات الفشل، مما يساعد في تحليل المشاكل لاحقاً. المشكلة التي واجهتها شخصياً مع هذا النمط هي أنه يمكن أن يؤدي إلى حالة تسمى "الارتداد" (thrashing)، حيث يتم فتح وإغلاق الـ Circuit بسرعة كبيرة، مما يؤدي إلى عدم استقرار النظام. الحل هو استخدام استراتيجية ذكية لفتح وإغلاق الـ Circuit، مثل زيادة الوقت بين المحاولات تدريجياً.

python
import time
from functools import wraps

def circuit_breaker(max_failures, reset_timeout):
 def decorator(func):
 failures = 0
 last_failure_time = 0
 is_open = False
 
 @wraps(func)
 def wrapper(*args, **kwargs):
 nonlocal failures, last_failure_time, is_open
 
 if is_open:
 if time.time() - last_failure_time > reset_timeout:
 is_open = False
 failures = 0
 else:
 raise Exception("Circuit is open - service unavailable")
 
 try:
 result = func(*args, **kwargs)
 failures = 0 # إعادة تعيين العداد عند النجاح
 return result
 except Exception as e:
 failures += 1
 last_failure_time = time.time()
 if failures >= max_failures:
 is_open = True
 raise e
 return wrapper
 return decorator

# مثال على استخدام Circuit Breaker
@circuit_breaker(max_failures=3, reset_timeout=10)
def unreliable_service(param):
 import random
 if random.random() < 0.7: # 70% فرصة للفشل
 raise Exception("Service failed")
 return f"Success: {param}"

# التجربة
for i in range(10):
 try:
 print(unreliable_service(f"request-{i}"))
 except Exception as e:
 print(f"Error: {e}")
 time.sleep(1)

الـ Decorators مع الـ Async/Await: عالم جديد من الإمكانيات

مع انتشار الـ Async/Await في بايثون، أصبح من الضروري فهم كيفية استخدام الـ Decorators مع الدوال غير المتزامنة. المشكلة هنا أن معظم الأمثلة على الإنترنت تستخدم دوال متزامنة، مما يجعلها غير قابلة للاستخدام في التطبيقات الحديثة التي تعتمد على الـ Async I/O. الـ Decorator العادي لا يعمل مع الدوال غير المتزامنة لأنه لا ينتظر تنفيذها، مما يؤدي إلى سلوك غير متوقع.

الحل هو استخدام asyncio لإنشاء decorators غير متزامنة. لكن هنا تأتي المشكلة الحقيقية: إذا استخدمت await داخل الـ Decorator، فإنك تقوم بتحويل الدالة الأصلية إلى دالة غير متزامنة حتى لو لم تكن كذلك. هذا يمكن أن يؤدي إلى مشاكل في الأداء إذا لم تكن الدالة الأصلية تقوم بأي عمليات I/O. في تجربتي، أفضل حل هو إنشاء نوعين من الـ Decorators: واحد للدوال المتزامنة وآخر للدوال غير المتزامنة، مع توثيق واضح لأي نوع يجب استخدامه.

python
import asyncio
from functools import wraps

def async_retry(max_retries=3, delay=1):
 def decorator(func):
 @wraps(func)
 async def wrapper(*args, **kwargs):
 last_exception = None
 for attempt in range(max_retries):
 try:
 return await func(*args, **kwargs)
 except Exception as e:
 last_exception = e
 print(f"Attempt {attempt + 1} failed, retrying in {delay} seconds...")
 await asyncio.sleep(delay)
 raise last_exception
 return wrapper
 return decorator

# مثال على استخدام Async Decorator
@async_retry(max_retries=3, delay=1)
async def fetch_data(url):
 import aiohttp
 async with aiohttp.ClientSession() as session:
 async with session.get(url) as response:
 if response.status != 200:
 raise Exception(f"HTTP Error: {response.status}")
 return await response.text()

# التجربة
async def main():
 try:
 data = await fetch_data("https://api.example.com/data")
 print("Data fetched successfully")
 except Exception as e:
 print(f"Failed to fetch data: {e}")

asyncio.run(main())

الفخاخ والمشاكل الشائعة: ما لا يخبرك به أحد

الـ Decorators تبدو بسيطة في البداية، لكن هناك العديد من الفخاخ التي يمكن أن تقع فيها إذا لم تكن حذراً. أولاً، مشكلة الـ Introspection: عندما تستخدم decorator، فإن الدالة الأصلية تفقد اسمها ووثائقها (docstring) الأصلية. هذا يمكن أن يسبب مشاكل في الـ Debugging وأدوات مثل Sphinx التي تعتمد على هذه المعلومات. الحل هو استخدام @wraps من مكتبة functools، والذي يحافظ على معلومات الدالة الأصلية.

ثانياً، مشكلة الـ Mutable Default Arguments. إذا استخدمت قائمة أو قاموس كمتغير افتراضي داخل الـ Decorator، فإن هذا المتغير سيتم مشاركته بين جميع استدعاءات الدالة، مما يؤدي إلى سلوك غير متوقع. هذا مشابه لمشكلة الـ Mutable Defaults في الدوال العادية، لكنه أكثر خطورة في الـ Decorators لأنه يمكن أن يؤثر على جميع الدوال التي تستخدم نفس الـ Decorator. الحل هو استخدام None كقيمة افتراضية، ثم إنشاء القائمة أو القاموس داخل الدالة إذا كانت القيمة None.

python
# مثال على مشكلة Mutable Defaults في Decorators
from functools import wraps

def bad_decorator(func):
 cache = {} # هذا القاموس سيتم مشاركته بين جميع الدوال التي تستخدم هذا الـ Decorator
 
 @wraps(func)
 def wrapper(*args):
 if args not in cache:
 cache[args] = func(*args)
 return cache[args]
 return wrapper

def good_decorator(func):
 @wraps(func)
 def wrapper(*args):
 if not hasattr(wrapper, 'cache'):
 wrapper.cache = {} # هذا القاموس سيكون فريداً لكل دالة
 if args not in wrapper.cache:
 wrapper.cache[args] = func(*args)
 return wrapper.cache[args]
 return wrapper

@bad_decorator
def add_bad(a, b):
 return a + b

@good_decorator
def add_good(a, b):
 return a + b

# التجربة
print(add_bad(1, 2)) # يعمل بشكل صحيح
print(add_bad(3, 4)) # يعمل بشكل صحيح
print(add_good(1, 2)) # يعمل بشكل صحيح
print(add_good(3, 4)) # يعمل بشكل صحيح
# لكن إذا استخدمت نفس الـ Decorator مع دالة أخرى، ستشارك الكاش في الحالة السيئة

الـ Decorators مع الـ Class Methods: فخ الـ self

عندما تستخدم الـ Decorators مع دوال الكلاس، يجب أن تكون حذراً جداً مع الـ self. المشكلة هنا أن الـ Decorator العادي لا يفهم أن الدالة الأولى التي يستقبلها هي في الواقع method، مما يعني أن الـ self سيتم تمريره كمعامل عادي. هذا يمكن أن يؤدي إلى أخطاء غريبة إذا لم يتم التعامل معه بشكل صحيح. الحل هو استخدام decorators مصممة خصيصاً للدوال الكلاس، أو استخدام @classmethod أو @staticmethod حسب الحاجة.

في أحد المشاريع، واجهنا مشكلة غريبة حيث كانت الـ Decorators تعمل بشكل صحيح مع الدوال العادية، لكنها تفشل مع دوال الكلاس. بعد ساعات من الـ Debugging، اكتشفنا أن الـ Decorator كان يحاول الوصول إلى self كمعامل عادي، مما يؤدي إلى خطأ في عدد المعاملات. الحل كان بسيطاً: استخدام @wraps مع التأكد من أن الـ wrapper يقبل self كمعامل أول. هذه المشكلة شائعة جداً لدرجة أن العديد من المكتبات مثل Flask وDjango تتعامل معها تلقائياً في الـ Decorators الخاصة بها.

python
from functools import wraps

def class_method_decorator(func):
 @wraps(func)
 def wrapper(self, *args, **kwargs): # لاحظ self هنا
 print(f"Calling {func.__name__} with {args}")
 return func(self, *args, **kwargs)
 return wrapper

class MyClass:
 @class_method_decorator
 def my_method(self, x):
 return x * 2

# التجربة
obj = MyClass()
print(obj.my_method(5)) # سيعمل بشكل صحيح

خلاصة المهندس: نصائح ذهبية لاستخدام الـ Decorators باحترافية

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

ثانياً، دائماً استخدم @wraps من مكتبة functools. هذا ليس مجرد نصيحة تجميلية، بل هو أمر ضروري للحفاظ على معلومات الدالة الأصلية وجعل الكود قابلاً للصيانة. ثالثاً، كن حذراً مع الـ Closures والمتغيرات المشتركة. تذكر أن الـ Decorator يتم تنفيذه مرة واحدة عند تحميل الكود، وليس مع كل استدعاء للدالة. هذا يعني أن أي متغير داخل الـ Decorator سيتم مشاركته بين جميع استدعاءات الدالة، مما يمكن أن يؤدي إلى سلوك غير متوقع.

رابعاً، اختبر الـ Decorators بشكل منفصل عن الدوال التي تستخدمها. الكثير من المطورين يفترضون أن الـ Decorator يعمل بشكل صحيح لأنهم اختبروا الدالة التي تستخدمه. لكن الـ Decorator نفسه يمكن أن يحتوي على أخطاء، ويجب اختباره بشكل مستقل. خامساً، توثيق الـ Decorators بشكل جيد. إذا كتبت decorator مخصص، تأكد من كتابة docstring يشرح ما يفعله، وما هي المعاملات التي يقبلها، وما هي الآثار الجانبية المحتملة.

أخيراً، لا تخف من استخدام الـ Decorators مع الـ Async/Await. الكثير من المطورين يتجنبون هذا لأنهم لا يفهمون كيفية عمله، لكن الـ Async Decorators يمكن أن تكون أداة قوية جداً في تحسين أداء التطبيقات غير المتزامنة. فقط تذكر أن الـ Async Decorators تختلف عن الـ Decorators العادية، ويجب استخدامها بحذر.

  • •استخدم الـ Decorators لتحسين الأداء، وليس لمجرد تجميل الكود.
  • •دائماً استخدم @wraps للحفاظ على معلومات الدالة الأصلية.
  • •كن حذراً مع المتغيرات المشتركة داخل الـ Decorators لتجنب تسرب الذاكرة.
  • •اختبر الـ Decorators بشكل مستقل عن الدوال التي تستخدمها.
  • •وثق الـ Decorators المخصصة بشكل جيد لتسهيل صيانة الكود.
  • •لا تخف من استخدام الـ Async Decorators في التطبيقات غير المتزامنة.
  • •تجنب استخدام الـ Decorators للدوال البسيطة التي لا تحتاج إلى تعديل سلوكها.

الـ Decorators في بايثون هي أداة قوية، لكنها تتطلب فهماً عميقاً لكيفية عملها. إذا استخدمت بشكل صحيح، يمكنها تحسين أداء الكود، وتقليل التكرار، وجعل الكود أكثر قابلية للصيانة. لكن إذا استخدمت بشكل خاطئ، يمكنها أن تؤدي إلى أخطاء غريبة، وتسرب ذاكرة، وسلوك غير متوقع. المفتاح هو فهم ما يحدث خلف الكواليس، واختبار الكود بشكل جيد، وتوثيقه بشكل واضح. بهذه الطريقة، يمكنك الاستفادة من قوة الـ Decorators دون الوقوع في الفخاخ الشائعة.

Python Decorators Advanced Python Software Engineering Performance Optimization

التعليقات

العودة للمقالات
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر