title: "পাইথন ডেকোরেটর" description: "পাইথন ডেকোরেটর কী এবং কীভাবে এটি ব্যবহার করে কোড পুনরায় ব্যবহারযোগ্য করা যায়" date: "2023-05-25" readTime: "১২ মিনিট" difficulty: "অ্যাডভান্সড" tags: ["পাইথন", "ডেকোরেটর", "ফাংশন", "মেটাপ্রোগ্রামিং", "এডভান্সড পাইথন"] featuredImage: "/placeholder.svg?height=400&width=800"
পাইথন ডেকোরেটর
ডেকোরেটর কী?
পাইথন ডেকোরেটর হল একটি শক্তিশালী ডিজাইন প্যাটার্ন যা আপনাকে অন্য ফাংশন বা মেথডের আচরণ পরিবর্তন বা বাড়ানো করতে দেয়, কোড পুনরাবৃত্তি না করেই। ডেকোরেটর মূলত একটি ফাংশন যা অন্য ফাংশন গ্রহণ করে এবং একটি নতুন ফাংশন প্রদান করে। এটি ফাংশনাল প্রোগ্রামিং এবং মেটাপ্রোগ্রামিং এর একটি উদাহরণ।
ডেকোরেটর কেন ব্যবহার করবেন?
ডেকোরেটর নিম্নলিখিত কারণে ব্যবহার করা হয়:
- কোড পুনরায় ব্যবহার: একই কোড বিভিন্ন ফাংশনে পুনরাবৃত্তি এড়ানো
- সেপারেশন অফ কনসার্নস: লজিক পৃথক করে মেইনটেনেন্স সহজ করা
- প্রি-প্রসেসিং/পোস্ট-প্রসেসিং: ফাংশন কল করার আগে বা পরে কাজ করা
- কোড অ্যাস্পেক্ট যোগ করা: যেমন লগিং, মেমো-ইজেশন, অথেনটিকেশন
সাধারণ ডেকোরেটর তৈরি
একটি বেসিক ডেকোরেটর তৈরি দেখি:
def my_decorator(func):
def wrapper():
print("কিছু করার আগে")
func()
print("কিছু করার পরে")
return wrapper
@my_decorator
def say_hello():
print("হ্যালো!")
# কল করা
say_hello()
আউটপুট:
কিছু করার আগে
হ্যালো!
কিছু করার পরে
অ্যারগুমেন্ট সহ ডেকোরেটর
ফাংশনে অ্যারগুমেন্ট থাকলে:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("কিছু করার আগে")
result = func(*args, **kwargs)
print("কিছু করার পরে")
return result
return wrapper
@my_decorator
def add(a, b):
return a + b
# কল করা
result = add(3, 5)
print(f"ফলাফল: {result}")
আউটপুট:
কিছু করার আগে
কিছু করার পরে
ফলাফল: 8
ডেকোরেটরে প্যারামিটার পাস করা
ডেকোরেটরে প্যারামিটার পাঠাতে আমাদের একটি অতিরিক্ত ফাংশন লেভেল প্রয়োজন:
def repeat(n=1):
def decorator(func):
def wrapper(*args, **kwargs):
result = None
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(n=3)
def say_hello(name):
print(f"হ্যালো, {name}!")
say_hello("রহিম")
আউটপুট:
হ্যালো, রহিম!
হ্যালো, রহিম!
হ্যালো, রহিম!
বিল্ট-ইন ডেকোরেটর
পাইথনে কিছু বিল্ট-ইন ডেকোরেটর আছে:
@staticmethod
ক্লাসে স্ট্যাটিক মেথড ডিফাইন করে, যা ক্লাস ইন্সট্যান্স ছাড়াই ব্যবহার করা যায়:
class MathUtils:
@staticmethod
def add(a, b):
return a + b
# ক্লাস ইন্সট্যান্স না করেই কল করা যায়
result = MathUtils.add(5, 3)
print(result) # 8
@classmethod
ক্লাস মেথড ডিফাইন করে, যা প্রথম প্যারামিটার হিসেবে ক্লাস নিজেই নেয়:
class Person:
count = 0
def __init__(self, name):
self.name = name
Person.count += 1
@classmethod
def number_of_people(cls):
return cls.count
# লোক তৈরি
person1 = Person("আলী")
person2 = Person("করিম")
# ক্লাস মেথড কল
print(Person.number_of_people()) # 2
@property
একটি মেথডকে অ্যাট্রিবিউটের মতো ব্যবহার করতে দেয়:
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:
raise ValueError("ব্যাসার্ধ ধনাত্মক হতে হবে")
self._radius = value
@property
def area(self):
return 3.14159 * self._radius ** 2
# ব্যবহার
circle = Circle(5)
print(circle.radius) # 5
print(circle.area) # ~78.54
# সেটারের ব্যবহার
circle.radius = 10
print(circle.area) # ~314.16
# ভুল ইনপুট দিলে এরর দেখাবে
# circle.radius = -1 # ValueError: ব্যাসার্ধ ধনাত্মক হতে হবে
বাস্তব জীবনে প্রয়োগ
1. টাইমিং ডেকোরেটর
ফাংশন কতটা সময় নেয় তা মাপতে:
import time
def timing_decorator(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:.6f} সেকেন্ড সময় লেগেছে")
return result
return wrapper
@timing_decorator
def slow_function():
import time
time.sleep(1)
return "কাজ শেষ!"
print(slow_function())
2. লগিং ডেকোরেটর
ফাংশন কল এবং রিটার্ন লগ করতে:
def log_decorator(func):
def wrapper(*args, **kwargs):
print(f"কল করছি: {func.__name__}, আর্গুমেন্ট: {args}, কিওয়ার্ড আর্গুমেন্ট: {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} থেকে রিটার্ন: {result}")
return result
return wrapper
@log_decorator
def calculate_square(x):
return x * x
calculate_square(5)
3. ক্যাশিং/মেমোইজেশন ডেকোরেটর
পারফরম্যান্স উন্নত করতে ফাংশন কল ক্যাশ করা:
def memoize(func):
cache = {}
def wrapper(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@memoize
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# আগে এটি খুব ধীর হত, এখন দ্রুত
print(fibonacci(35))
4. রেট লিমিটিং ডেকোরেটর
API কলে রেট লিমিট সেট করতে:
import time
from functools import wraps
def rate_limit(max_calls, period):
calls = []
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
now = time.time()
# পুরানো কলগুলি সরানো
while calls and calls[0] < now - period:
calls.pop(0)
if len(calls) >= max_calls:
raise Exception(f"রেট লিমিট অতিক্রান্ত! সর্বোচ্চ {max_calls} কল প্রতি {period} সেকেন্ডে")
calls.append(now)
return func(*args, **kwargs)
return wrapper
return decorator
# প্রতি 5 সেকেন্ডে 2টি কল সীমাবদ্ধ করা
@rate_limit(2, 5)
def api_call():
return "API কল সফল"
# ব্যবহার
print(api_call()) # API কল সফল
print(api_call()) # API কল সফল
# print(api_call()) # এরর: রেট লিমিট অতিক্রান্ত!
ডেকোরেটর সম্পর্কে সাবধানতা
ডেকোরেটর ব্যবহার করার সময় কিছু সাবধানতা অবলম্বন করুন:
- প্যারামিটার হারিয়ে যাওয়া:
functools.wraps
ব্যবহার করে মূল ফাংশনের নাম, ডকস্ট্রিং ইত্যাদি বজায় রাখুন - ডিবাগ করা কঠিন: জটিল ডেকোরেটর ডিবাগ করা কঠিন হতে পারে
- পারফরম্যান্স ওভারহেড: অতিরিক্ত ফাংশন কলে পারফরম্যান্স কমতে পারে
- নেস্টেড ডেকোরেটর: একাধিক ডেকোরেটর ব্যবহার করলে, তাদের ক্রম গুরুত্বপূর্ণ
উপসংহার
ডেকোরেটর পাইথনের একটি শক্তিশালী এবং লচ্ছন্দ ফিচার। সঠিকভাবে ব্যবহার করলে, এটি আপনার কোডকে আরও পরিষ্কার, আলাদা করা, এবং রক্ষণাবেক্ষণযোগ্য করতে পারে। তবে, এটি অতিরিক্ত জটিলতা যোগ করতে পারে, তাই সাবধানতার সাথে ব্যবহার করুন।
ডেকোরেটর আপনার কোডকে আরও শক্তিশালী এবং বিশ্লেষণযোগ্য করতে পারে, বিশেষ করে বড় প্রোজেক্টগুলিতে যেখানে আপনি অপারেশন বা লজিকের পুনরাবৃত্তি এড়াতে চান।