ما هو مصمم بايثون؟
A مصمم بايثونهو نمط تصميم يتيح لك إضافة سلوك جديد إلى وظيفة موجودة – دون لمس رمز الوظيفة. في بايثون، الوظائف هي كائنات من الدرجة الأولى: يمكن تمريرها كوسائط، وتعيينها للمتغيرات، وإعادتها من وظائف أخرى. يستغل مصممو الديكور هذه الخاصية.
📋 Table of Contents
- ما هو مصمم بايثون؟
- دعونا نبني
- هناك خطأ خفي في الديكور أعلاه. بعد الزخرفة
- ماذا لو كنت تريد تكوين الديكور الخاص بك؟ على سبيل المثال،
- يمكنك تطبيق العديد من الديكورات على وظيفة واحدة. يطبقون
- أي كائن قابل للاستدعاء يمكن أن يكون ديكورًا. يمنحك استخدام الفصل الدراسي الحالة بين المكالمات:
- تأتي Python مع العديد من أدوات الديكور المضمنة التي ستستخدمها باستمرار:
- تعمل أدوات الديكور على تشغيل معظم أطر عمل بايثون. فيما يلي أنماط درجة الإنتاج:
- النسيان
- ما هو مصمم بايثون؟
بناء الجملة هو مجرد سكر نحوي. عندما تكتب:@📋 نسخ
@my_decoratorndef greet(name):n return f"Hello, {name}!"
📋 نسخ
greet = my_decorator(greet)
يشير الآن إلى النسخة المحسنة.greetمصمم الديكور الأول الخاص بك: خطوة بخطوة
دعونا نبني
بسيطة ديكور قطع الأشجارالذي يطبع رسالة قبل وبعد استدعاء أي وظيفة.📋 نسخ
def log_calls(func):n """Decorator that logs function entry and exit."""n def wrapper(*args, **kwargs):n print(f"→ Calling {func.__name__} with args={args}, kwargs={kwargs}")n result = func(*args, **kwargs)n print(f"← {func.__name__} returned {result!r}")n return resultn return wrappernn@log_callsndef add(a, b):n return a + bnnadd(3, 4)n# Output:n# → Calling add with args=(3, 4), kwargs={}n# ← add returned 7
يستقبل
log_calls(الوظيفة الأصلية)funcإنه يحدد داخلي- يقبل
wrapper– لذلك فهو يعمل مع أي توقيع دالة*args, **kwargsالمجمع يستدعي الأصلي - ، يلتقط النتيجة ويعيدها
funcتقوم بإرجاع الغلاف — وليس نتيجة تسميته log_callsإصلاح functools.wraps
هناك خطأ خفي في الديكور أعلاه. بعد الزخرفة
يعودadd.__name__، وليس'wrapper'. هذا يكسر'add'، وآثار المكدس. اصلاحها معhelp(), inspect📋 نسخfunctools.wraps:
import functoolsnndef log_calls(func):n @functools.wraps(func) # ← copies __name__, __doc__, __module__, etc.n def wrapper(*args, **kwargs):n print(f"→ Calling {func.__name__}")n result = func(*args, **kwargs)n print(f"← {func.__name__} returned {result!r}")n return resultn return wrappernn@log_callsndef add(a, b):n """Add two numbers."""n return a + bnnprint(add.__name__) # 'add' ✅nprint(add.__doc__) # 'Add two numbers.' ✅
@functools.wraps(func)ديكورات بالحججماذا لو كنت تريد تكوين الديكور الخاص بك؟ على سبيل المثال،
. أنت بحاجة إلى طبقة إضافية من التداخل —@retry(times=3)مصنع ديكور📋 نسخ:
import functools, timenndef retry(times=3, delay=1.0, exceptions=(Exception,)):n """Retry a function up to `times` times on specified exceptions."""n def decorator(func):n @functools.wraps(func)n def wrapper(*args, **kwargs):n last_err = Nonen for attempt in range(1, times + 1):n try:n return func(*args, **kwargs)n except exceptions as e:n last_err = en print(f" Attempt {attempt}/{times} failed: {e}")n if attempt < times:n time.sleep(delay)n raise last_errn return wrappern return decoratornn@retry(times=3, delay=0.5, exceptions=(ConnectionError,))ndef fetch_data(url):n # simulate a flaky network calln raise ConnectionError("timeout")nntry:n fetch_data("https://api.example.com/data")nexcept ConnectionError:n print("All retries exhausted.")
. نسميها:retry() → decorator() → wrapper()يعودretry(times=3)، والذي يأخذdecoratorويعودfuncتكديس ديكورات متعددةwrapper.
يمكنك تطبيق العديد من الديكورات على وظيفة واحدة. يطبقون
من أسفل إلى أعلى(الأعمق أولاً):📋 نسخ
import functools, timenndef timer(func):n @functools.wraps(func)n def wrapper(*args, **kwargs):n start = time.perf_counter()n result = func(*args, **kwargs)n elapsed = time.perf_counter() - startn print(f"{func.__name__} took {elapsed:.4f}s")n return resultn return wrappernndef log_calls(func):n @functools.wraps(func)n def wrapper(*args, **kwargs):n print(f"Calling {func.__name__}")n return func(*args, **kwargs)n return wrappernn@timer # applied second (outer)n@log_calls # applied first (inner)ndef process(n):n return sum(range(n))nnprocess(1_000_000)n# Output:n# Calling processn# process took 0.0312s
. الترتيب مهم – الديكورات الخارجية تغلف الديكورات الداخلية.process = timer(log_calls(process))مصممو الديكور على أساس الفصل
أي كائن قابل للاستدعاء يمكن أن يكون ديكورًا. يمنحك استخدام الفصل الدراسي الحالة بين المكالمات:
📋 نسخ
import functoolsnnclass CallCounter:n """Counts how many times a function is called."""n def __init__(self, func):n functools.update_wrapper(self, func)n self.func = funcn self.count = 0nn def __call__(self, *args, **kwargs):n self.count += 1n print(f"{self.func.__name__} called {self.count} time(s)")n return self.func(*args, **kwargs)nn@CallCounterndef say_hello(name):n print(f"Hello, {name}!")nnsay_hello("Alice") # called 1 time(s)nsay_hello("Bob") # called 2 time(s)nprint(say_hello.count) # 2
تأتي Python مع العديد من أدوات الديكور المضمنة التي ستستخدمها باستمرار:
📋 نسخ
class Temperature:n _kelvin_offset = 273.15nn def __init__(self, celsius):n self._celsius = celsiusnn @propertyn def fahrenheit(self):n """Computed attribute — no () needed when accessing."""n return self._celsius * 9/5 + 32nn @fahrenheit.settern def fahrenheit(self, value):n self._celsius = (value - 32) * 5/9nn @classmethodn def from_kelvin(cls, kelvin):n """Factory method — creates instance from Kelvin."""n return cls(kelvin - cls._kelvin_offset)nn @staticmethodn def is_valid_celsius(value):n """Utility — no access to instance or class."""n return value >= -273.15nnt = Temperature(100)nprint(t.fahrenheit) # 212.0 (property)nt.fahrenheit = 32 # setternt2 = Temperature.from_kelvin(373.15) # classmethodnprint(Temperature.is_valid_celsius(-300)) # False (staticmethod)
| الارج الاول | الوصول | حالة الاستخدام | مثيل + فئة |
|---|---|---|---|
@property |
self |
السمات المحسوبة، الحروف/المحددات | فئة فقط |
@classmethod |
cls |
أساليب المصنع، البنائين البديلين | لا شيء |
@staticmethod |
ولا | وظائف الأداة المساعدة ذات مساحة اسمية للفئة | حالات الاستخدام في العالم الحقيقي |
تعمل أدوات الديكور على تشغيل معظم أطر عمل بايثون. فيما يلي أنماط درجة الإنتاج:
1. التخزين المؤقت باستخدام ذاكرة التخزين المؤقت LRU
📋 نسخ
from functools import lru_cachenn@lru_cache(maxsize=128)ndef fibonacci(n):n if n < 2:n return nn return fibonacci(n - 1) + fibonacci(n - 2)nnprint(fibonacci(50)) # Instant — cached resultsnprint(fibonacci.cache_info()) # CacheInfo(hits=48, misses=51, ...)
📋 نسخ
import functoolsnndef require_auth(func):n """Decorator for protected routes."""n @functools.wraps(func)n def wrapper(request, *args, **kwargs):n token = request.headers.get("Authorization", "")n if not token.startswith("Bearer "):n return {"error": "Unauthorized"}, 401n return func(request, *args, **kwargs)n return wrappernn@require_authndef get_user_profile(request):n return {"user": "Alice", "email": "alice@example.com"}
📋 نسخ
import functools, timenfrom collections import defaultdict, dequenndef rate_limit(calls=10, period=60):n """Allow at most `calls` per `period` seconds per caller."""n history = defaultdict(deque)n def decorator(func):n @functools.wraps(func)n def wrapper(*args, **kwargs):n now = time.time()n key = args[0] if args else "default" # use first arg as caller IDn q = history[key]n while q and now - q[0] > period:n q.popleft()n if len(q) >= calls:n raise RuntimeError(f"Rate limit exceeded: {calls} calls per {period}s")n q.append(now)n return func(*args, **kwargs)n return wrappern return decoratornn@rate_limit(calls=5, period=60)ndef send_email(user_id, message):n print(f"Email sent to {user_id}: {message}")
النسيان
- — فواصل
@functools.wrapsوالتتبع وأدوات الاختبارhelp()استدعاء الدالة في جسم الديكور - بدلا من —
return wrapper()لا يستخدمreturn wrapper - — يكسر أي دالة بمعلمات
*args, **kwargsالحالة الافتراضية القابلة للتغيير المشتركة عبر المكالمات - — استخدم متغير إغلاق، وليس متغيرًا افتراضيًا قابلاً للتغيير على مستوى الوحدة النمطيةمفقود
- في المجمع
return result— يبتلع بصمت القيمة المرجعة للدالة📋 نسخ
# ❌ Wrong — returns Nonendef broken(func):n def wrapper(*args, **kwargs):n func(*args, **kwargs) # no return!n return wrappernn# ✅ Correctndef fixed(func):n @functools.wraps(func)n def wrapper(*args, **kwargs):n return func(*args, **kwargs) # always returnn return wrapper
الآن بعد أن فهمت مصممي الديكور، استكشف كيفية تشغيل أطر العمل مثل
هيكلة مشاريع بايثونوالأنماط المتقدمة. تحقق من دليلنا علىتعبيرات بايثون المختصرةلمزيد من الحيل رمز بايثونيك.الأسئلة المتداولة
ما هو مصمم بايثون؟
مصمم الديكور في Python هو دالة تأخذ وظيفة أخرى كمدخلات وترجع نسخة محسنة – دون تعديل النسخة الأصلية.
بناء الجملة هو اختصار لـ@متى يجب أن أستخدم أدوات الديكور؟func = decorator(func).
استخدم أدوات الديكور للمخاوف الشاملة: التسجيل، والتخزين المؤقت، والمصادقة، وتحديد المعدل، والتوقيت، والتحقق من صحة الإدخال – أي سلوك تريد تطبيقه على وظائف متعددة دون تكرار التعليمات البرمجية.
ماذا يفعل functools.wraps؟
يقوم بنسخ الوظيفة الأصلية
It copies the original function’s__name__, __doc__والسمات الأخرى للمجمّع، مع الحفاظ على الهوية لتصحيح الأخطاء، والمساعدة () وأدوات الاختبار.
هل يمكن لمصممي الديكور أن يكون لديهم حجج؟
نعم. أضف دالة مصنع خارجية تقبل الوسائط وتعيد الديكور الحقيقي. النمط:@retry(times=3) → retry()إرجاع الديكور.
ما الفرق بين @staticmethod و@classmethod؟
@staticmethodلا يحصل على أي وسيطة أولى ضمنية – إنها وظيفة عادية في مساحة اسم الفئة.@classmethodيتلقى الفئة (cls) كوسيطة أولى، وهي مفيدة لطرق المصنع.
كيف تعمل الديكورات المكدسة؟
من أسفل إلى أعلى: مصمم الديكور الأقرب إلىdefيتم تطبيقه أولا.@A @B def f()يساويf = A(B(f)).
n
🔗 Share this article
✍️ Leave a Comment