Decorator در پایتون چیست؟ آموزش Decorators به زبان ساده
در این آموزش با Decorator در پایتون آشنا میشویم و یاد میگیریم چگونه بدون تغییر مستقیم کد یک تابع، رفتار آن را گسترش دهیم.
Decorator در پایتون چیست؟
Decorator یا دکوراتور یکی از قابلیتهای قدرتمند پایتون است که به ما اجازه میدهد رفتار یک تابع یا متد را بدون تغییر مستقیم کد اصلی آن تغییر دهیم یا امکانات جدیدی به آن اضافه کنیم.
دکوراتورها در پروژههای واقعی کاربردهای زیادی دارند؛ برای مثال میتوان از آنها برای ثبت زمان اجرای تابع، بررسی دسترسی کاربر، ثبت Log و موارد مشابه استفاده کرد.
تابع در پایتون یک شیء است
برای درک Decorator ابتدا باید بدانیم که در پایتون تابعها نیز مانند بسیاری از اشیای دیگر میتوانند داخل متغیر قرار بگیرند یا به عنوان آرگومان به تابع دیگری داده شوند.
def say_hello():
print("Hello Python")
message = say_hello
message()
خروجی:
Hello Python
در این مثال، تابع say_hello را داخل متغیر
message قرار دادهایم.
ارسال تابع به عنوان آرگومان
میتوانیم یک تابع را به عنوان آرگومان به تابع دیگری ارسال کنیم.
def say_hello():
print("Hello Python")
def execute(function):
function()
execute(say_hello)
این مفهوم یکی از پایههای اصلی ساخت Decorator است.
تابع تو در تو یا Nested Function
تابعی که داخل تابع دیگری تعریف شود، Nested Function نام دارد.
def outer():
def inner():
print("Hello")
inner()
outer()
Decoratorها معمولاً از تابعهای تو در تو برای ایجاد تابع جدید استفاده میکنند.
ساخت اولین Decorator
یک Decorator ساده میتواند به شکل زیر نوشته شود:
def decorator(function):
def wrapper():
print("قبل از اجرای تابع")
function()
print("بعد از اجرای تابع")
return wrapper
حالا میتوانیم از این Decorator استفاده کنیم:
def say_hello():
print("Hello Python")
say_hello = decorator(say_hello)
say_hello()
خروجی:
قبل از اجرای تابع
Hello Python
بعد از اجرای تابع
استفاده از علامت @
پایتون برای استفاده سادهتر از Decoratorها یک syntax مخصوص در اختیار ما قرار میدهد.
به جای:
say_hello = decorator(say_hello)
میتوانیم بنویسیم:
@decorator
def say_hello():
print("Hello Python")
علامت @ باعث میشود Decorator روی تابع اعمال شود.
Decorator با *args و **kwargs
اگر تابعی که قرار است Decorate شود آرگومان داشته باشد، باید Decorator بتواند این آرگومانها را دریافت کند.
def decorator(function):
def wrapper(*args, **kwargs):
print("شروع اجرای تابع")
result = function(*args, **kwargs)
print("پایان اجرای تابع")
return result
return wrapper
حالا میتوانیم تابعی با آرگومانهای مختلف را Decorate کنیم.
@decorator
def add(a, b):
return a + b
result = add(10, 20)
print(result)
خروجی:
شروع اجرای تابع
پایان اجرای تابع
30
functools.wraps چیست؟
هنگام استفاده از Decorator، اطلاعاتی مانند نام و توضیحات
تابع اصلی ممکن است با اطلاعات تابع wrapper
جایگزین شود.
برای جلوگیری از این مشکل میتوانیم از
functools.wraps استفاده کنیم.
from functools import wraps
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
return function(*args, **kwargs)
return wrapper
استفاده از wraps باعث میشود اطلاعات مهم تابع
اصلی بهتر حفظ شوند.
مثال کاربردی: اندازهگیری زمان اجرای تابع
یکی از کاربردهای جالب Decorator، اندازهگیری زمان اجرای یک تابع است.
import time
from functools import wraps
def timer(function):
@wraps(function)
def wrapper(*args, **kwargs):
start = time.time()
result = function(*args, **kwargs)
end = time.time()
print("زمان اجرا:", end - start)
return result
return wrapper
@timer
def calculate():
total = 0
for i in range(1000000):
total += i
return total
calculate()
در این مثال Decorator قبل و بعد از اجرای تابع زمان را اندازهگیری میکند.
Decorator با آرگومان
گاهی میخواهیم خود Decorator نیز هنگام استفاده آرگومان دریافت کند. در این حالت معمولاً به یک تابع اضافی نیاز داریم.
def repeat(times):
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
for _ in range(times):
function(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def hello():
print("Hello Python")
hello()
خروجی:
Hello Python
Hello Python
Hello Python
استفاده از چند Decorator
میتوان بیش از یک Decorator را روی یک تابع قرار داد.
@decorator_one
@decorator_two
def hello():
print("Hello")
Decoratorها به ترتیب مشخصی روی تابع اعمال میشوند؛ بنابراین هنگام استفاده از چند Decorator باید ترتیب آنها را در نظر بگیریم.
کاربردهای Decorator در پروژههای واقعی
- ثبت Log و اطلاعات اجرای توابع
- اندازهگیری زمان اجرای کد
- بررسی دسترسی کاربران
- کش کردن نتایج
- اعتبارسنجی ورودیها
- اضافه کردن قابلیتهای مشترک به چند تابع
Decoratorها در بسیاری از کتابخانهها و فریمورکهای پایتون نیز استفاده میشوند.
اشتباهات رایج هنگام استفاده از Decorator
- فراموش کردن
returnنتیجه تابع اصلی - عدم استفاده از
*argsو**kwargsدر موارد لازم - نادیده گرفتن
functools.wraps - پیچیده کردن بیش از حد Decoratorها
بهتر است Decoratorها را زمانی استفاده کنیم که واقعاً باعث سادهتر و قابل نگهداریتر شدن کد شوند.
تمرینهای Decorator در پایتون
تمرین ۱
یک Decorator بنویسید که قبل از اجرای تابع عبارت
شروع و بعد از آن عبارت پایان را چاپ کند.
تمرین ۲
یک Decorator بسازید که تعداد دفعات اجرای یک تابع را نمایش دهد.
تمرین ۳
یک Decorator بنویسید که زمان اجرای تابع را اندازهگیری کند.
تمرین ۴
یک Decorator بسازید که یک تابع را سه بار اجرا کند.
سوالات متداول
Decorator در پایتون چیست؟
Decorator ابزاری برای اضافه کردن یا تغییر رفتار یک تابع یا متد بدون تغییر مستقیم کد اصلی آن است.
علامت @ در پایتون چه کاربردی دارد؟
علامت @ برای اعمال سادهتر Decorator روی یک تابع
یا متد استفاده میشود.
چرا از functools.wraps استفاده میکنیم؟
برای حفظ اطلاعاتی مانند نام و metadata تابع اصلی هنگام استفاده از Decorator.
آیا Decorator فقط برای تابعهاست؟
Decoratorها میتوانند برای توابع و متدها استفاده شوند و در موارد مختلف برنامهنویسی کاربرد دارند.