FA-TOOLS — Header Component

# آموزش decorators در پایتون — کاربرد و نحوه نوشتن

سلام رفیق برنامه‌نویس! اگه دنبال یه راه حل اساسی برای مرتب کردن کدها، اضافه کردن قابلیت‌های جدید به توابع بدون دستکاری مستقیمشون و خلاصه حرفه‌ای‌تر کد زدن هستی، درست اومدی. امروز می‌خوایم یکی از قدرتمندترین و در عین حال شیک‌ترین ویژگی‌های پایتون رو با هم بررسی کنیم: Decoratorها. این مقاله جامع و کاربردی، از صفر تا صد دکوراتورها رو بهت یاد می‌ده تا بتونی همین الان ازشون استفاده کنی و کدهای تمیزتر و قابل نگهداری‌تری بنویسی.

قبل از اینکه غرق دنیای جذاب دکوراتورها بشیم، یه سر به ابزارهای پایتونی `fa-tools.ir` بزن؛ مطمئنم ابزارهایی پیدا می‌کنی که کار برنامه‌نویسیت رو راحت‌تر و سریع‌تر می‌کنه. برای اطلاعات بیشتر هم می‌تونی با شماره [09202232789](tel:09202232789) تماس بگیری.

🗺️ نقشه راه Decoratorها در یک نگاه

آموزش decorators در پایتون — کاربرد و نحوه نوشتن — تصویر 1

اینفوگرافیک متنی زیر خلاصه‌ای از تمام چیزهایی است که قراره یاد بگیریم:

┌──────────────────────────────────────────────┐
│        🌟  آموزش Decorators در پایتون 🌟      │
├──────────────────────────────────────────────┤
│  🎯 هدف: بهینه‌سازی، خوانایی، قابلیت نگهداری │
├──────────────────────────────────────────────┤
│  ❓ چرا؟       │  ✨ چیست؟      │  🛠️ چطور؟     │
│ ────────────────────────────────────────────── │
│  تکرار کد      │  تابع‌ای که    │  1. پیش‌نیازها  │
│  افزودن قابلیت │  تابع دیگری    │     - First-Class Functions │
│  پویا          │  را ورودی گرفته │     - Closures              │
│                │  و تابعی جدید  │  2. ساخت:     │
│                │  را برمی‌گرداند.│     - بدون آرگومان (@my_decorator) │
│                │                │     - با آرگومان (@my_decorator(arg)) │
├──────────────────────────────────────────────┤
│        🚀 کاربردهای کلیدی (نمونه‌ها)         │
│ ────────────────────────────────────────────── │
│  •  Log کردن (ثبت رویدادها)                   │
│  •  اندازه‌گیری زمان اجرا (Performance)       │
│  •  احراز هویت و مجوزها (Auth/Permissions)   │
│  •  کش کردن نتایج (Caching)                   │
│  •  مدیریت خطا (Error Handling/Retry)        │
└──────────────────────────────────────────────┘
    

## چرا به Decoratorها نیاز داریم؟

تصور کن یه عالمه تابع توی برنامه‌ات داری و می‌خوای به همه‌شون یه قابلیت مشترک اضافه کنی. مثلاً:

* **Log کردن:** دوست داری قبل و بعد از اجرای هر تابع، یه پیامی توی کنسول یا فایل لاگ ثبت بشه تا بدونی کی چی صدا زده شده.
* **اندازه‌گیری زمان:** می‌خوای بفهمی هر تابع چقدر طول می‌کشه تا اجرا بشه و عملکرد برنامه‌ات رو بررسی کنی.
* **احراز هویت:** بعضی توابع فقط باید توسط کاربرهای خاصی اجرا بشن (مثلاً ادمین‌ها).
* **کش کردن:** اگه یه تابع همیشه با ورودی‌های یکسان، خروجی یکسانی داره، چرا هر بار از نو محاسبش کنی؟ می‌تونی نتیجه رو ذخیره و دوباره استفاده کنی.

بدون دکوراتورها، احتمالاً مجبور می‌شدی کدهای تکراری رو بارها و بارها توی هر تابع کپی-پیست کنی. این کار هم کد رو شلوغ می‌کنه، هم نگهداریش رو کابوس‌وار. هر تغییری توی اون قابلیت مشترک، یعنی باید بری توی ده‌ها (یا صدها!) تابع و کد رو دستی ویرایش کنی. اینجا دقیقاً همون جاییه که دکوراتورها میان و مثل یک ناجی عمل می‌کنن.

## Decorator چیست؟ یک تعریف ساده و کاربردی

به زبان ساده، یه Decorator توی پایتون، **یه تابع دیگه است که یه تابع رو به عنوان ورودی می‌گیره، یه کار اضافه‌ای روش انجام می‌ده و یه تابع جدید (معمولاً با قابلیت‌های افزوده) رو برمی‌گردونه.** این “کار اضافه” می‌تونه هر چیزی باشه: لاگ کردن، اندازه‌گیری زمان، چک کردن مجوزها و… .

تصور کن یه جعبه هدیه ساده داری. Decorator مثل کاغذ کادو، ربان، و کارت تبریکیه که جعبه اصلی رو تغییر نمی‌ده، اما بهش ظاهر و کاربرد جدیدی می‌بخشه. اون تابع اصلی همون جعبه هدیه‌ست که دست نخورده باقی می‌مونه، ولی دکوراتور بهش قابلیت‌های بیشتری اضافه می‌کنه.

نحوه استفاده ازشون هم خیلی شیکه: یه `@` قبل از تعریف تابع اصلیت می‌ذاری و اسم دکوراتور رو جلوی اون می‌نویسی. مثلاً:

“`python
# یک Decorator فرضی
def my_decorator(func):
def wrapper(*args, **kwargs):
print(“قبل از اجرای تابع…”)
result = func(*args, **kwargs)
print(“بعد از اجرای تابع…”)
return result
return wrapper

@my_decorator
def say_hello():
print(“سلام دنیا!”)

say_hello()
# خروجی:
# قبل از اجرای تابع…
# سلام دنیا!
# بعد از اجرای تابع…
“`

همونطور که می‌بینی، تابع `say_hello` خودش هیچ منطق لاگ کردن نداره، اما به خاطر `@my_decorator`، این قابلیت بهش اضافه شده. جادویی نیست؟

## پیش‌نیازها: آشنایی با مفاهیم کلیدی پایتون

برای اینکه دکوراتورها رو واقعاً درک کنیم و بتونیم خودمون بنویسیمشون، باید با دو مفهوم مهم پایتون آشنا بشیم: توابع First-Class و Closures. نگران نباش، اینها پیچیده نیستن و اتفاقاً خیلی کاربردی‌اند.

جدول: توابع First-Class در برابر Closures

مفهوم توضیح
First-Class Functions توابع می‌توانند مانند هر شیء دیگری (مثل اعداد یا رشته‌ها) منتقل شوند: به متغیر اختصاص یابند، به عنوان آرگومان به تابع دیگر پاس داده شوند یا از یک تابع برگردانده شوند.
Closures یک تابع داخلی (Nested Function) که به متغیرهای محلی تابع بیرونی خود، حتی پس از اتمام اجرای تابع بیرونی، دسترسی دارد. این متغیرها را “به خاطر می‌سپارد”.

### توابع First-Class در پایتون

در پایتون، توابع “First-Class” هستند. این یعنی:
* میشه اونها رو به یه متغیر اختصاص داد.
* میشه اونها رو به عنوان آرگومان به توابع دیگه فرستاد.
* میشه اونها رو از توابع دیگه برگردوند.
* میشه اونها رو توی ساختار داده‌ای مثل لیست یا دیکشنری ذخیره کرد.

یه مثال ساده:

“`python
def add(a, b):
return a + b

def subtract(a, b):
return a – b

def calculate(func, x, y):
return func(x, y)

# توابع رو به متغیرها اختصاص میدیم
operation1 = add
operation2 = subtract

print(operation1(5, 3)) # خروجی: 8
print(operation2(5, 3)) # خروجی: 2

# تابع رو به عنوان آرگومان به تابع دیگه میفرستیم
print(calculate(add, 10, 5)) # خروجی: 15
print(calculate(subtract, 10, 5)) # خروجی: 5

# تابع رو از یه تابع دیگه برمیگردونیم
def get_multiplier(factor):
def multiplier(number):
return number * factor
return multiplier

double = get_multiplier(2)
triple = get_multiplier(3)

print(double(5)) # خروجی: 10
print(triple(5)) # خروجی: 15
“`

همین مورد آخر (برگرداندن تابع از تابع) کلید درک دکوراتورهاست!

### درک Closures و Scope

Closure وقتی اتفاق می‌افته که یه تابع داخلی، به متغیرهای محلی تابع بیرونی خودش دسترسی داشته باشه، حتی بعد از اینکه تابع بیرونی کارش تموم شده و از پشته فراخوانی خارج شده. تابع داخلی اون متغیرها رو “به خاطر می‌سپاره”.

“`python
def outer_function(message):
# ‘message’ یک متغیر محلی برای outer_function است
def inner_function():
# inner_function به message دسترسی دارد
print(message)
return inner_function

my_closure = outer_function(“سلام از Closure!”)
my_closure() # خروجی: سلام از Closure!

# تابع outer_function تموم شده، اما inner_function هنوز ‘message’ رو یادشه.
“`

دقیقاً همین دو مفهوم (First-Class Functions و Closures) هستند که به دکوراتورها قدرت می‌دهند تا بتوانند توابع را بپوشانند (Wrap کنند) و رفتارشان را تغییر دهند.

## ساخت اولین Decorator شما: قدم به قدم

حالا که پیش‌نیازها رو می‌دونیم، وقتشه خودمون دکوراتور بسازیم.

### Decorator بدون آرگومان

یه Decorator ساده برای لاگ کردن اجرای توابع می‌سازیم. این دکوراتور هیچ آرگومانی نمی‌گیره (به جز خود تابع که قراره دکوریت بشه).

1. **تابع دکوراتور رو تعریف کن:** این تابع، تابع اصلیت رو به عنوان آرگومان می‌گیره.
2. **یک تابع داخلی (wrapper) تعریف کن:** این تابع داخلی همون چیزیه که به جای تابع اصلی صدا زده میشه. توی این تابع، می‌تونی کارهای قبل و بعد از اجرای تابع اصلی رو انجام بدی.
3. **تابع اصلی رو داخل wrapper صدا بزن:** نتیجه‌اش رو هم برگردون.
4. **`wrapper` رو از تابع دکوراتور برگردون:** این همون Closure معروفه.
5. **از `@` استفاده کن:** دکوراتور رو به تابع مورد نظرت اعمال کن.

“`python
def simple_logger(func):
def wrapper(*args, **kwargs):
print(f”[{func.__name__}] در حال اجرا…”) # Log قبل از اجرا
result = func(*args, **kwargs) # اجرای تابع اصلی
print(f”[{func.__name__}] اجرا شد.”) # Log بعد از اجرا
return result
return wrapper

@simple_logger
def greet(name):
print(f”سلام، {name}!”)
return f”خوش آمدی {name}!”

@simple_logger
def add_numbers(a, b):
print(f”محاسبه جمع {a} و {b}…”)
return a + b

# استفاده از توابع دکوریت شده
greet(“علی”)
# خروجی:
# [greet] در حال اجرا…
# سلام، علی!
# [greet] اجرا شد.

sum_result = add_numbers(10, 20)
print(f”نتیجه جمع: {sum_result}”)
# خروجی:
# [add_numbers] در حال اجرا…
# محاسبه جمع 10 و 20…
# [add_numbers] اجرا شد.
# نتیجه جمع: 30
“`

می‌تونی اسنیپت‌های پایتون بیشتری رو توی [اینجا](https://fa-tools.ir/snippets/python/) پیدا کنی.

نکته مهم در مورد `*args` و `**kwargs`:

استفاده از *args و **kwargs در تابع wrapper خیلی مهمه، چون باعث میشه دکوراتور ما با هر تابعی، با هر تعداد و نوع آرگومان، کار کنه. بدون اینها، دکوراتور فقط روی توابعی کار می‌کنه که آرگومان خاصی نگیرند.

### Decorator با آرگومان

گاهی اوقات ممکنه بخوایم به خود دکوراتور هم آرگومان پاس بدیم. مثلاً توی لاگر، بگیم که پیام لاگ چی باشه. برای این کار، یک لایه تابع دیگه اضافه می‌کنیم.

1. **تابع خارجی‌ترین (decorator factory) رو تعریف کن:** این تابع، آرگومان‌های دکوراتور رو می‌گیره.
2. **تابع دکوراتور اصلی رو برگردون:** این همون تابع دکوراتوریه که قبلاً ساختیم و تابع اصلی رو می‌گیره.
3. **بقیه مراحل مثل Decorator بدون آرگومان:** تابع wrapper رو تعریف کن، تابع اصلی رو داخلش اجرا کن و wrapper رو برگردون.

“`python
def parameterized_logger(log_message):
def decorator(func):
def wrapper(*args, **kwargs):
print(f”پیام قبل از اجرا: {log_message}”) # استفاده از آرگومان دکوراتور
print(f”[{func.__name__}] در حال اجرا…”)
result = func(*args, **kwargs)
print(f”[{func.__name__}] اجرا شد.”)
print(f”پیام بعد از اجرا: {log_message}”)
return result
return wrapper
return decorator

@parameterized_logger(“این یه پیام سفارشی برای تابع سلامته!”)
def say_hello_custom(name):
print(f”سلام {name} عزیز!”)
return f”خوش آمدی {name}!”

@parameterized_logger(“عملیات ریاضی انجام شد.”)
def calculate_power(base, exp):
return base ** exp

say_hello_custom(“سارا”)
power_result = calculate_power(2, 3)
print(f”نتیجه توان: {power_result}”)
“`

همینطور که می‌بینی، با این ساختار، می‌تونیم دکوراتورهای منعطف‌تری بنویسیم.
برای آشنایی بیشتر با ابزارهای برنامه‌نویسی و وب، یه سر به صفحه اصلی [فا تولز](https://fa-tools.ir/) بزن.

## کاربردهای رایج Decoratorها در دنیای واقعی

حالا که می‌دونیم چطور دکوراتور بسازیم، بیاید چندتا از کاربردهای اصلی و حیاتی‌شون رو بررسی کنیم:

### Log کردن فعالیت‌ها (Logging)

همونطور که دیدیم، لاگ کردن یکی از رایج‌ترین کاربردهاست. می‌تونی اطلاعات مفیدی مثل ورودی‌ها، خروجی‌ها و زمان اجرای تابع رو ثبت کنی.

“`python
import logging
logging.basicConfig(level=logging.INFO, format=’%(asctime)s – %(levelname)s – %(message)s’)

def log_function_call(func):
def wrapper(*args, **kwargs):
args_repr = [repr(a) for a in args]
kwargs_repr = [f”{k}={v!r}” for k, v in kwargs.items()]
signature = “, “.join(args_repr + kwargs_repr)
logging.info(f”فراخوانی {func.__name__} با آرگومان‌ها: ({signature})”)
result = func(*args, **kwargs)
logging.info(f”{func.__name__} به اتمام رسید. نتیجه: {result!r}”)
return result
return wrapper

@log_function_call
def complex_calculation(x, y):
return (x * y) ** 2

@log_function_call
def process_data(data):
return [item.upper() for item in data]

complex_calculation(3, 4)
process_data([“apple”, “banana”])
“`

### اندازه‌گیری زمان اجرا (Timing)

برای پروفایل کردن و بهینه‌سازی عملکرد برنامه‌ات، دونستن زمان اجرای هر تابع حیاتیه.

“`python
import time

def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f”تابع ‘{func.__name__}’ در {end_time – start_time:.4f} ثانیه اجرا شد.”)
return result
return wrapper

@timer
def expensive_operation(n):
sum_val = 0
for _ in range(n):
sum_val += sum(range(1000))
return sum_val

@timer
def another_task():
time.sleep(0.5)
print(“یه کار دیگه انجام شد.”)

expensive_operation(100)
another_task()
“`

### احراز هویت و مجوزها (Authentication/Authorization)

توی فریمورک‌های وبی مثل Flask یا Django، دکوراتورها برای چک کردن لاگین بودن کاربر یا داشتن مجوزهای خاص، نقش کلیدی دارن.

“`python
# فرض کنید یک سیستم مدیریت کاربر ساده داریم
current_user = {“name”: “admin”, “role”: “admin”}
# current_user = {“name”: “guest”, “role”: “user”}

def requires_role(required_role):
def decorator(func):
def wrapper(*args, **kwargs):
if current_user and current_user[“role”] == required_role:
print(f”کاربر {current_user[‘name’]} دسترسی دارد.”)
return func(*args, **kwargs)
else:
print(f”خطا: کاربر {current_user[‘name’]} (نقش: {current_user.get(‘role’, ‘N/A’)}) دسترسی به ‘{func.__name__}’ ندارد. نقش مورد نیاز: {required_role}”)
return None
return wrapper
return decorator

@requires_role(“admin”)
def delete_critical_data():
print(“داده‌های حیاتی حذف شدند.”)

@requires_role(“user”)
def view_profile(user_id):
print(f”پروفایل کاربر {user_id} مشاهده شد.”)

delete_critical_data()
view_profile(123)
“`

برای اسنیپت‌های عمومی‌تر و کدهای آماده می‌تونی به [این بخش](https://fa-tools.ir/snippets/) مراجعه کنی.

### کش کردن نتایج (Caching)

اگر تابعی دارید که با ورودی‌های یکسان، همیشه خروجی یکسان می‌ده و محاسباتش هم سنگینه، کش کردن می‌تونه عملکرد رو به طرز چشمگیری بالا ببره. پایتون یه دکوراتور توکار برای این کار داره (`@functools.lru_cache`)، اما می‌تونیم خودمون هم یه نسخه ساده بسازیم.

“`python
cache = {}

def memoize(func):
def wrapper(*args, **kwargs):
# ساخت یک کلید هش از آرگومان‌ها
key = (func.__name__, args, frozenset(kwargs.items()))
if key in cache:
print(f”کش شده برای ‘{func.__name__}’ بازیابی شد.”)
return cache[key]

print(f”محاسبه ‘{func.__name__}’…”)
result = func(*args, **kwargs)
cache[key] = result
return result
return wrapper

@memoize
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)

@memoize
def expensive_lookup(item_id):
time.sleep(1) # شبیه‌سازی یک عملیات سنگین
return f"اطلاعات آیتم {item_id}"

print(fibonacci(10)) # محاسبه می‌شود
print(fibonacci(10)) # از کش بازیابی می‌شود

print(expensive_lookup(5)) # محاسبه می‌شود
print(expensive_lookup(5)) # از کش بازیابی می‌شود
“`

### مدیریت خطا (Error Handling)

می‌تونی دکوراتورهایی بنویسی که یک تابع رو در صورت خطا چند بار امتحان کنن، یا خطاهای خاصی رو بگیرن و یه پیام دوستانه برگردونن.

“`python
import time

def retry(attempts=3, delay=1):
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(attempts):
try:
return func(*args, **kwargs)
except Exception as e:
print(f”تلاش {i+1} از {attempts} برای ‘{func.__name__}’ ناموفق بود: {e}”)
if i < attempts – 1:
time.sleep(delay)
print(f"همه {attempts} تلاش برای '{func.__name__}' ناموفق بود.")
return None # یا هر مقدار پیش‌فرض دیگر
return wrapper
return decorator

counter = 0

@retry(attempts=3, delay=2)
def unstable_network_call():
global counter
counter += 1
if counter < 3:
raise ConnectionError("قطع شدن ارتباط!")
print("ارتباط برقرار شد و داده‌ها دریافت شدند.")
return "Data received"

unstable_network_call()
“`

## Decoratorهای توکار پایتون (Built-in Decorators)

پایتون چندتا دکوراتور خیلی کاربردی داره که احتمالاً باهاشون آشنایی:

* **`@property`**: برای تبدیل متدها به ویژگی‌ها (Attributes) بدون نیاز به فراخوانی آنها به عنوان تابع. این بهت اجازه می‌ده که برای دسترسی به یک Attribute، یه سری منطق پشتش بذاری (مثل Validation یا Lazy Loading).
* **`@classmethod`**: برای تعریف متدهایی که به جای یک instance از کلاس، خود کلاس رو به عنوان اولین آرگومان می‌گیرن.
* **`@staticmethod`**: برای تعریف متدهایی که نه به instance کلاس و نه به خود کلاس دسترسی دارن. مثل یه تابع معمولی توی یه کلاس.
* **`@functools.wraps`**: **این یکی فوق‌العاده مهمه!** وقتی یه تابع رو با دکوراتور می‌پوشونی، متادیتای تابع اصلی (مثل `__name__`، `__doc__`) از بین می‌ره و جای خودش رو به متادیتای تابع `wrapper` می‌ده. `@functools.wraps` این متادیتا رو از تابع اصلی به تابع `wrapper` کپی می‌کنه و مشکل رو حل می‌کنه. همیشه توی Decoratorهات ازش استفاده کن.

“`python
import functools

def my_decorator_with_wraps(func):
@functools.wraps(func) # استفاده از wraps
def wrapper(*args, **kwargs):
“””این wrapper برای my_decorator_with_wraps است.”””
print(“قبل از اجرا با wraps”)
return func(*args, **kwargs)
return wrapper

@my_decorator_with_wraps
def say_hello_wrapped(name):
“””تابعی برای گفتن سلام.”””
return f”سلام، {name}!”

print(say_hello_wrapped.__name__)
print(say_hello_wrapped.__doc__)

# خروجی (با wraps):
# say_hello_wrapped
# تابعی برای گفتن سلام.

def my_decorator_without_wraps(func):
def wrapper(*args, **kwargs):
“””این wrapper برای my_decorator_without_wraps است.”””
print(“قبل از اجرا بدون wraps”)
return func(*args, **kwargs)
return wrapper

@my_decorator_without_wraps
def say_hello_unwrapped(name):
“””تابعی برای گفتن سلام.”””
return f”سلام، {name}!”

print(say_hello_unwrapped.__name__)
print(say_hello_unwrapped.__doc__)

# خروجی (بدون wraps):
# wrapper
# این wrapper برای my_decorator_without_wraps است.
“`

همینطور که می‌بینی، `wraps` باعث میشه تابع دکوریت شده، رفتاری شبیه به تابع اصلی داشته باشه و ابزارهای دیباگینگ و مستندسازی هم بهتر کار کنن.

## عیب‌یابی سریع (Troubleshooting Decorators)

گاهی اوقات ممکنه در کار با دکوراتورها به مشکلاتی بربخوری. نگران نباش، این مشکلات رایج هستند و راه‌حل‌های مشخصی دارند.

### 1. از دست دادن متادیتا (Metadata Loss)
* **مشکل:** وقتی یه تابع رو دکوریت می‌کنی، `__name__`، `__doc__` و سایر اَتریبیوت‌هاش عوض می‌شن و به متادیتا مربوط به تابع wrapper تبدیل می‌شن. این می‌تونه برای دیباگ کردن، مستندسازی و ابزارهایی که به این اطلاعات نیاز دارن، مشکل‌ساز بشه.
* **راه حل:** **همیشه از `@functools.wraps(func)` استفاده کن.** این دکوراتور، متادیتا رو از تابع اصلی به تابع wrapper کپی می‌کنه.

“`python
import functools

def preserve_metadata_decorator(func):
@functools.wraps(func) # حتما اینو بذار!
def wrapper(*args, **kwargs):
“””این مستندات مربوط به wrapper است.”””
return func(*args, **kwargs)
return wrapper

@preserve_metadata_decorator
def my_func():
“””این مستندات اصلی my_func است.”””
pass

print(f”نام تابع: {my_func.__name__}”)
print(f”داکسترینگ: {my_func.__doc__}”)
# خروجی صحیح: my_func، این مستندات اصلی my_func است.
“`

### 2. ترتیب اجرای دکوراتورها (Decorator Order)
* **مشکل:** وقتی چند دکوراتور رو روی یک تابع اعمال می‌کنی، ترتیب اجراشون می‌تونه مهم باشه.
* **راه حل:** دکوراتورها از **پایین به بالا** یا از **داخل به خارج** اجرا می‌شن. یعنی دکوراتوری که نزدیک‌تر به تعریف تابع هست، اول اجرا می‌شه.

“`python
def make_bold(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return f”{func(*args, **kwargs)}
return wrapper

def make_italic(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return f”{func(*args, **kwargs)}
return wrapper

@make_bold
@make_italic
def get_text(text):
return text

print(get_text(“سلام دنیا”))
# خروجی: سلام دنیا (اول italic اعمال شد بعد bold)

@make_italic
@make_bold
def get_text_reversed(text):
return text

print(get_text_reversed(“سلام دنیا”))
# خروجی: سلام دنیا (اول bold اعمال شد بعد italic)
“`

توی مثال بالا، `@make_italic` به تابع `get_text` نزدیک‌تره، پس اول محتوا رو ایتالیک می‌کنه، بعد `make_bold` روی خروجی `make_italic` اعمال میشه و اون رو Bold می‌کنه.

### 3. سربار عملکردی (Performance Overhead)
* **مشکل:** هر دکوراتور یک لایه اضافی از فراخوانی توابع رو اضافه می‌کنه. اگه تعداد دکوراتورها زیاد باشه یا دکوراتور خودش عملیات سنگینی انجام بده، ممکنه عملکرد برنامه کند بشه.
* **راه حل:**
* **بهینه بنویس:** دکوراتورها رو تا حد ممکن ساده و سبک نگه دار.
* **فقط در صورت نیاز استفاده کن:** برای هر تابع کوچیکی که نیازی به قابلیت‌های جانبی نداره، دکوراتور ننویس.
* **پروفایلینگ:** از ماژول `timeit` یا ابزارهای پروفایلینگ پایتون برای سنجش دقیق عملکرد استفاده کن و گلوگاه‌ها رو پیدا کن.

### 4. دیباگ کردن توابع دکوریت شده
* **مشکل:** وقتی خطایی توی یک تابع دکوریت شده پیش میاد، traceback ممکنه به جای تابع اصلی، به تابع wrapper اشاره کنه و تشخیص ریشه مشکل رو سخت‌تر کنه.
* **راه حل:**
* **استفاده از `@functools.wraps`:** این کار باعث میشه traceback ها خواناتر بشن و اسم تابع اصلی رو نشون بدن.
* **پرینت‌های موقت:** می‌تونی موقتاً پرینت‌هایی رو داخل تابع wrapper بذاری تا ببینی جریان اجرا چطوریه.
* **ابزارهای دیباگینگ:** IDEهای مدرن مثل VS Code یا PyCharm معمولاً ابزارهای دیباگینگ پیشرفته‌ای دارن که می‌تونن از لایه‌های دکوراتور عبور کنن.

## کلام آخر

Decoratorها یکی از قابلیت‌های قدرتمند پایتون هستند که کدنویسی رو تمیزتر، قابل نگهداری‌تر و ماژولارتر می‌کنن. با درک مفاهیم First-Class Functions و Closures، و تمرین ساخت دکوراتورهای ساده و پیچیده‌تر، به یک برنامه‌نویس پایتون مسلط‌تر تبدیل می‌شی. یادت باشه، کلید یادگیری تمرین و نوشتن کد با دست خودته!

امیدوارم این راهنمای جامع و دست‌اول، دید خوبی نسبت به دکوراتورها بهت داده باشه و بتونی ازشون توی پروژه‌هات استفاده کنی. اگه سوالی داری یا به مشکلی خوردی، همیشه می‌تونی با ما تماس بگیری یا از منابع آنلاین مثل اسنیپت‌های پایتون fa-tools.ir استفاده کنی. موفق باشی!

## سوالات متداول (FAQ)

Decorator چیست؟

Decorator در پایتون یک تابع است که تابع دیگری را به عنوان ورودی می‌گیرد، به آن قابلیت‌های جدیدی اضافه می‌کند و تابع جدیدی را برمی‌گرداند. این کار بدون تغییر مستقیم کد تابع اصلی انجام می‌شود و به افزایش خوانایی و قابلیت نگهداری کد کمک می‌کند.

چرا باید از Decoratorها استفاده کنیم؟

Decoratorها به شما کمک می‌کنند تا کدهای تکراری (مثل لاگ کردن، اندازه‌گیری زمان، احراز هویت) را حذف کرده و منطق مشترک را به صورت ماژولار و قابل استفاده مجدد پیاده‌سازی کنید. این کار باعث می‌شود کدهای شما تمیزتر، منعطف‌تر و کمتر مستعد خطا باشند.

تفاوت Decorator با آرگومان و بدون آرگومان چیست؟

Decorator بدون آرگومان، فقط تابع مورد نظر را به عنوان ورودی می‌گیرد و مستقیماً آن را “می‌پوشاند”. اما Decorator با آرگومان، یک لایه اضافی دارد: ابتدا آرگومان‌های خود دکوراتور را می‌گیرد، سپس یک تابع دکوراتور (که ورودی‌اش تابع اصلی است) را برمی‌گرداند. این ساختار به شما اجازه می‌دهد تا دکوراتور را با پارامترهای مختلفی پیکربندی کنید.

نقش functools.wraps در Decoratorها چیست؟

وقتی یک تابع با Decorator پوشانده می‌شود، متادیتا (مثل نام، مستندات، و ماژول) تابع اصلی از بین می‌رود و با متادیتا تابع wrapper جایگزین می‌شود. @functools.wraps(func) این متادیتا را از تابع اصلی به تابع wrapper کپی می‌کند و باعث می‌شود تابع دکوریت شده رفتار “طبیعی‌تری” داشته باشد، که برای دیباگ کردن و ابزارهای دیگر بسیار مفید است.

آیا Decoratorها تاثیری بر عملکرد برنامه دارند؟

بله، هر Decorator یک لایه اضافی از فراخوانی تابع را اضافه می‌کند که می‌تواند سربار کوچکی در عملکرد ایجاد کند. برای اکثر کاربردها، این سربار ناچیز است و مزایای استفاده از Decoratorها بر آن غلبه می‌کند. اما در کدهای بسیار حساس به عملکرد، باید مراقب باشید و در صورت لزوم، عملکرد را با ابزارهای پروفایلینگ بررسی کنید.

Table of Contents

آخرین نوشته‌ها