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