در مقالههای گذشته با مفهوم دکوراتورها و کاربردهای آنها آشنا شدیم. دکوراتورها یکی از ابزارهای قدرتمندی هستند که پایتون در اختیار توسعهدهندگان قرار میدهد و این امکان را فراهم میکنند تا بدون دست بردن در ساختار اصلی توابع یا کلاسها، ویژگیهای جدیدی به آنها اضافه کنیم. دکوراتورها در پایتون کاربردهای متنوعی دارند؛ از مدیریت دسترسی و بهبود عملکرد گرفته تا مدیریت منابع و تنظیمات مرتبط با نوع دادهها. در این مقاله، به بررسی ۲۰ دکوراتور مهم و کاربردی پایتون میپردازیم و هرکدام را همراه با مثال توضیح میدهیم تا بتوانید در پروژههای خود از آنها بهرهمند شوید.
در این مقاله، یاد خواهید گرفت:
- دکوراتورهای پایتون چه هستند و چرا استفاده از آنها مفید است.
- چگونه میتوان دکوراتورها را برای افزودن قابلیتهای جدید به توابع و کلاسها به کار گرفت.
- معرفی و توضیح ۲۰ دکوراتور مهم و کاربردی، به همراه مثال برای هر یک.
- چگونه از دکوراتورها برای مدیریت منابع، بهینهسازی عملکرد و کنترل دسترسی در کدنویسی استفاده کنید.
پس اگر مقاله آموزش دکوراتورها را نخواندهاید، اول پیشنهاد میکنیم سری به مقاله بزنید.
مقاله پیشین: دکوراتور در پایتون
staticmethod@
این دکوراتور برای تعریف متدی است که به هیچ ویژگی یا نمونهای از کلاس وابسته نیست و میتواند بدون نمونهسازی از کلاس فراخوانی شود. از این متدها معمولاً برای عملیات عمومی استفاده میشود.
class MathOperations: @staticmethod def add(a, b): return a + b # استفاده از متد استاتیک بدون نیاز به ایجاد نمونه print(MathOperations.add(5, 10)) # خروجی: 15
classmethod@
این دکوراتور برای تعریف متدی به کار میرود که به خود کلاس به عنوان پارامتر cls
دسترسی دارد، نه به یک نمونه خاص. از این دکوراتور برای دسترسی و کار با ویژگیها یا متدهای کلاس (نه نمونهها) استفاده میشود.
class Person: species = "Homo sapiens" @classmethod def print_species(cls): print(f"Species: {cls.species}") # استفاده از متد کلاس بدون نیاز به ایجاد نمونه Person.print_species() # خروجی: Species: Homo sapiens
property@
با استفاده از این دکوراتور، یک متد میتواند مانند یک ویژگی قابل دسترسی باشد. این دکوراتور اغلب برای کنترل دسترسی به متغیرهای خصوصی به کار میرود و امکان تعریف متدهای getter و setter برای یک ویژگی فراهم میکند.
class Circle: def __init__(self, radius): self._radius = radius @property def radius(self): return self._radius @radius.setter def radius(self, value): if value > 0: self._radius = value else: raise ValueError("Radius must be positive") circle = Circle(5) print(circle.radius) # خروجی: 5 circle.radius = 10 print(circle.radius) # خروجی: 10
abstractmethod@
این دکوراتور که در کتابخانهی abc
قرار دارد، برای تعریف متدهای انتزاعی به کار میرود و از کلاسها میخواهد که این متدها را در زیرکلاسها پیادهسازی کنند. کلاسهای حاوی متدهای انتزاعی نمیتوانند مستقیم نمونهسازی شوند و باید ابتدا کامل شوند.
from abc import ABC, abstractmethod class Animal(ABC): @abstractmethod def sound(self): pass class Dog(Animal): def sound(self): return "Woof" # استفاده از کلاس انتزاعی dog = Dog() print(dog.sound()) # خروجی: Woof
lru_cache@
این دکوراتور که در functools
موجود است، نتایج توابع را برای بهبود عملکرد کش میکند و با ذخیره نتایج محاسبات قبلی باعث سرعت بخشیدن به عملیات میشود. این دکوراتور مخصوصاً برای توابعی که نیاز به محاسبات مکرر دارند مناسب است.
from functools import lru_cache @lru_cache(maxsize=3) def fibonacci(n): if n < 2: return n return fibonacci(n-1) + fibonacci(n-2) print(fibonacci(10)) # خروجی: 55
dataclass@
این دکوراتور به کلاسها ویژگیهای خودکار مانند نمایش، مقایسه و مقداردهی اولیه میدهد. این دکوراتور که در dataclasses
قرار دارد، برای کلاسهایی که فقط شامل دادهها هستند مناسب است و کدنویسی را بسیار سادهتر میکند.
from dataclasses import dataclass @dataclass class Point: x: int y: int point = Point(5, 10) print(point) # خروجی: Point(x=5, y=10)
singledispatch@
این دکوراتور که در functools
قرار دارد، به شما امکان میدهد تا یک تابع را برای نوعهای مختلف دادهها به طور جداگانه تعریف کنید. در واقع این دکوراتور امکان چندشکلی را فراهم میکند و بر اساس نوع ورودی، رفتار متفاوتی از تابع ارائه میدهد.
from functools import singledispatch @singledispatch def process_data(data): raise NotImplementedError("Unsupported type") @process_data.register(str) def _(data): return data.upper() @process_data.register(int) def _(data): return data ** 2 print(process_data("hello")) # خروجی: HELLO print(process_data(5)) # خروجی: 25
wraps@
این دکوراتور که در functools
موجود است، در دکوراتورهای سفارشی برای حفظ اطلاعات تابع اصلی به کار میرود، به طوری که نام و مستندات تابع اصلی پس از اعمال دکوراتور تغییر نکند و همچنان قابل دسترسی باشد.
from functools import wraps def decorator(func): @wraps(func) def wrapper(*args, **kwargs): print("Before function call") result = func(*args, **kwargs) print("After function call") return result return wrapper @decorator def greet(): print("Hello, World!") greet() # خروجی: # Before function call # Hello, World! # After function call
contextmanager@
این دکوراتور در contextlib
برای ایجاد مدیریتکنندههای زمینه به کار میرود و با استفاده از ساختار with
، امکان باز و بسته کردن خودکار منابعی مثل فایلها را فراهم میکند. این دکوراتور به سادگی مدیریت منابع را آسانتر میکند.
from contextlib import contextmanager @contextmanager def open_file(name): f = open(name, 'w') try: yield f finally: f.close() with open_file('example.txt') as f: f.write('Hello, World!')
برای توضیحات بیشتر: آموزش Context Manager در پایتون
asyncio.coroutine@
این دکوراتور که در کتابخانهی asyncio
قرار دارد، برای تعریف توابعی به کار میرود که به صورت غیرهمزمان اجرا میشوند و امکان استفاده از await
برای کنترل جریان برنامه را فراهم میکند.
توجه
استفاده از @asyncio.coroutine
به عنوان دکوراتور در نسخههای جدیدتر پایتون منسوخ شده و به جای آن از async def
استفاده میشود. نمونه کد زیر بر اساس روش جدید نوشته شده است.
import asyncio async def say_hello(): await asyncio.sleep(1) print("Hello, Async World!") # اجرای تابع همزمانی asyncio.run(say_hello())
total_ordering@
این دکوراتور در functools
امکانات مقایسهای کامل را برای کلاسهایی که حداقل یک متد مقایسهای دارند فراهم میکند و از کدنویسی تکراری جلوگیری میکند. با این دکوراتور فقط نیاز به تعریف یک متد مقایسهای داریم و بقیه مقایسهها خودکار انجام میشود.
from functools import total_ordering @total_ordering class Number: def __init__(self, value): self.value = value def __eq__(self, other): return self.value == other.value def __lt__(self, other): return self.value < other.value a = Number(5) b = Number(10) print(a < b) # خروجی: True
cache@
این دکوراتور، مشابه @lru_cache
، نتایج تابع را برای بهبود عملکرد ذخیره میکند و برای توابع بدون پارامترهای متغیر و سنگین مناسب است. این دکوراتور به افزایش سرعت اجرای توابع تکراری کمک میکند.
from functools import cache @cache def factorial(n): if n == 0: return 1 return n * factorial(n - 1) print(factorial(5)) # خروجی: 120
همچنین توضیحات بیشتری رو میتوانید در این مقاله پیدا کنید: کشینگ در پایتون با دکوراتر
final@
این دکوراتور از بازنویسی و ارثبری یک متد یا کلاس جلوگیری میکند. زمانی که میخواهید متد یا کلاسی کاملاً ثابت باشد و در زیرکلاسها تغییر نکند، این دکوراتور بسیار کاربردی است.
from typing import final class Animal: @final def sound(self): return "Some sound" class Dog(Animal): pass # این کلاس نمیتواند `sound` را بازنویسی کند
runtime_checkable@
این دکوراتور که به همراه Protocol
در کتابخانهی typing
استفاده میشود، امکان بررسی پیروی از پروتکلها در زمان اجرا را فراهم میکند و به سادگی از نوعسنجی پویا پشتیبانی میکند.
from typing import Protocol, runtime_checkable @runtime_checkable class Runnable(Protocol): def run(self) -> None: ... class MyRunnable: def run(self): print("Running!") r = MyRunnable() print(isinstance(r, Runnable)) # خروجی: True
beartype@
این دکوراتور برای بررسی نوع آرگومانهای ورودی و خروجی توابع استفاده میشود و به صورت دقیق خطاهای نوع را در زمان اجرا تشخیص میدهد. این دکوراتور در کتابخانهی beartype
موجود است و امنیت کد را افزایش میدهد.
from beartype import beartype @beartype def add(a: int, b: int) -> int: return a + b print(add(2, 3)) # خروجی: 5
overload@
این دکوراتور در typing
امکان تعریف چندین امضای مختلف برای یک تابع واحد را فراهم میکند. این ویژگی امکان تعریف توابعی با چندین نوع پارامتر و بازدهی را فراهم میکند که موجب خوانایی بیشتر و چندشکلی در کد میشود.
from typing import overload @overload def process(value: int) -> int: ... @overload def process(value: str) -> str: ... def process(value): if isinstance(value, int): return value * 2 elif isinstance(value, str): return value.upper() print(process(3)) # خروجی: 6 print(process("hello")) # خروجی: HELLO
typechecked@
این دکوراتور که در typeguard
موجود است، نوع آرگومانهای ورودی و خروجی توابع را در زمان اجرا بررسی میکند تا از بروز خطاهای احتمالی ناشی از نوع نادرست جلوگیری کند و ایمنی کد را افزایش دهد.
from typeguard import typechecked @typechecked def greet(name: str): print(f"Hello, {name}") greet("Alice") # خروجی: Hello, Alice
retry@
این دکوراتور که در tenacity
وجود دارد، در صورت بروز خطا، تابع را بر اساس سیاست تعیینشده مجدد اجرا میکند. این دکوراتور برای سناریوهایی مثل اتصال به شبکه یا درخواستهای ناپایدار مناسب است.
from tenacity import retry, stop_after_attempt @retry(stop=stop_after_attempt(3)) def flaky_function(): print("Trying...") raise Exception("Failed") try: flaky_function() except: print("All retries failed.")
register@
این دکوراتور که در atexit
استفاده میشود، توابعی را ثبت میکند که قرار است در زمان پایان برنامه اجرا شوند و برای بستن منابع یا انجام کارهای پایانی به کار میرود.
import atexit def goodbye(): print("Goodbye!") atexit.register(goodbye) print("Hello, World!")
measure_time@
این دکوراتور زمان اجرای تابع را اندازهگیری میکند و برای تحلیل عملکرد کد مناسب است. این دکوراتور با گزارش مدت زمان اجرای تابع، به شناسایی نقاط کندی در برنامه و بهینهسازی آن کمک میکند.
import time from functools import wraps def measure_time(func): @wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__} took {end - start:.4f} seconds") return result return wrapper @measure_time def slow_function(): time.sleep(1) slow_function() # خروجی: slow_function took 1.0000 seconds
حرف آخر
دکوراتورها در پایتون ابزاری ضروری و انعطافپذیر برای توسعهدهندگان هستند. هر دکوراتوری که در این مقاله معرفی شد، میتواند کدنویسی شما را سادهتر، کارآمدتر و امنتر کند. از دکوراتورهای پایهای مانند @staticmethod
و @property
گرفته تا دکوراتورهای پیشرفتهتری چون @singledispatch
و @asyncio.coroutine
، هر کدام با هدف خاصی طراحی شدهاند و بهکارگیری آنها میتواند کد شما را حرفهایتر کند. اگر از این دکوراتورها در پروژههای خود استفاده کردهاید یا تجربههای خاصی دارید، خوشحال میشویم که نظرات و تجربیات خود را در بخش دیدگاهها با ما به اشتراک بگذارید. کدام دکوراتور برای شما مفیدتر بوده است؟