“ডেকরেটর! ওই যে বিভিন্ন অনুষ্ঠানের সাজ সজ্জা করে মানে ডেকরেট করে….. তো পাইথনে আবার তাদের কি কাজ!”
পোস্টের টাইটেল দেখে যদি আপনার মনে এরকম প্রশ্নের উদয় হয় তাহলেও দোষের কিছু নেই। পাইথনে ডেকরেটর কিছুটা এডভান্স আর কমপ্লেক্স টপিক। তবে চিন্তা নেই, এই পোস্টে আমরা একটু সহজ ভাবে, ধাপে ধাপে জানার চেষ্টা করবো পাইথনে ডেকরেটর জিনিষটা কী, কীভাবে কাজ করে আর কীভাবেই বা ব্যাবহার করে।
ঠিক ঠাক ভাবে বললে, ডেকরেটর হল এক ধরনের callable যা অন্য callable এর ফাংশনালিটিকে মডিফাই করে। আরেকটু সহজ করে বললে, ডেকরেটর হল এক ধরনের ফাংশন যা অন্য ফাংশনের ফাংশনালিটিকে মডিফাই করে। [ডেকরেটর ক্লাসও হয়, তবে এ পোস্টে সেটা আলোচনা করবো না।] তো ধাপে ধাপে শুরু করা যাক। আমি ধরে নিচ্ছি ভ্যারিয়েবলের স্কোপ রেজ্যুলেশন সম্পর্কে আমাদের মোটামুটি ভাল ধারনা আছে, তাই এ সম্পর্কে আর বাড়তি কিছু লিখলাম না।
ফাংশনের কারিকুরি
এই কোড ব্লক টা দেখি:
|
|
8 নম্বর লাইন থেকে কোডটা ইন্টারেস্টিং হওয়া শুরু করেছে। আমরা জানি পাইথনে সব কিছুই এক একটা অবজেক্ট। ফাংশনও। এই লাইনে আমরা hello
কে hi
তে এসাইন করেছি। লক্ষনীয়, এখানে hello
এর পাশে ()
(ব্র্যাকেট/প্যারেন্থেসিস) দেই নি। অর্থাৎ এখানে hello
ফাংশনটি এক্সিকিউট বা কল হয় নি। 10 নম্বর লাইনের আউটপুট দেখলে ব্যাপারটা আরো পরিষ্কার হবে। আর 13 নম্বর লাইনে hi
কে কল করা হয়েছে, আউটপুট পেয়েছি ঠিক hello
এর মত।
ফাংশনের ভেতর ফাংশন!
হ্যা, পাইথনে আমরা ফাংশনের ভেতর ফাংশন ডিফাইন করতে পারি। অন্যভাবে বললে আমরা নেস্টেড ফাংশন বানাতে পারি। এরকম:
|
|
আউটপুট:
Inside hello
Inside nested
Inside hello again
আরেকটি কোড ব্লক দেখি:
|
|
শুরুতেই লিখেছিলাম ভ্যারিয়েবলের স্কোপ রেজ্যুলেশন সম্পর্কে লিখবো না। কোডটা একটু ভাল মত লক্ষ্য করলেই আশা করি বুঝতে পারবেন।
ফাংশন থেকে ফাংশন রিটার্ন!
ফাংশন থেকে ইচ্ছা করলে আমরা ফাংশন রিটার্নও করতে পারি! এই কোডটা দেখলে ব্যাপারটা পরিষ্কার হবে:
|
|
9 নম্বর লাইনে hello()
কে কল করায় এটা nested
কে রিটার্ন করেছে, তা এসাইন হয়েছে hi
তে। পূর্বের কোড ব্লক গুলো ফলো করলে এটি সহজেই বোঝা যাবে।
আরেকটি কোড ব্লক দেখি:
|
|
কি, আউটপুট গেস করেছেন? এটা আর এক্সপ্লেইন করবো না। আউটপুট হবে 6।
ফাংশনের আর্গুমেন্ট/প্যারামিটার হিসেবে ফাংশন
সরাসরি একটা কোড স্নিপেট দেখে ফেলি:
|
|
এখানে hi
ফাংশনের প্যারামিটার হিসেবে hello
কে পাস করা হয়েছে। hi
এর ভেতর hello
কল হয়েছে। আউটপুট হবে এরকম:
Hi!
Hello World!
ডেকরেটর
এখন হচ্ছে মূল বিষয়, ডেকরেটর। আমরা আগেই জেনেছি, ডেকরেটর হচ্ছে এমন ফাংশন যা অন্য ফাংশনের ফাংশনালিটি মডিফাই করে। এখন তাহলে একটু সাজানো গুছানো উদাহরণ দেখে নেই:
|
|
এখানে mydecorator
ফাংশনটি প্যারামিটার হিসেবে আরেকটি ফাংশন এক্সপেক্ট করছে। এর মধ্যকার wrapper
ফাংশনটিতে প্যারামিটারে পাওয়া ফাংশন কল করার আগে এবং পরে কিছু কাজ হচ্ছে। আর mydecorator
থেকে wrapper
কে রিটার্ন করা হচ্ছে। ১৫ নম্বর লাইনে mydecorator
কে hello
প্যারামিটার দিয়ে কল করা হয়েছে। রিটার্ন ভ্যালু এসাইন করা হয়েছে আবার hello
তে। অর্থাৎ mydecorator
এর মাধ্যমে hello
মডিফাই হয়েছে। [প্রয়োজনে আবার খেয়াল করুন।] সব শেষ লাইনে hello()
কল হয়েছে। আউটপুট হবে এরকম:
Before calling func()
Hello World!
After calling func()
নাম এবং কাজ দেখে বোঝাই যাচ্ছে mydecorator
হচ্ছে আমাদের কাঙ্খিত সেই ডেকরেটর।
তবে ডেকরেটর ব্যাবহারের সুন্দর একটি সিনট্যাক্স আছে, @। উপরের কোড কে আমরা সুন্দর করে এভাবে লিখতে পারি:
|
|
অর্থাৎ, hello = mydecorator(hello)
এই লাইনের পরিবর্তে আমরা hello
ফাংশনটি ডিফাইনের ঠিক আগে @mydecorator
লিখেছি। পূর্বের মত একই কাজ হবে।
বাস্তব উদাহরণ
এবার একটি বাস্তব উদাহরণ দেখা যাক। মনে করি আমাদের একটি ফাংশন আছে, আমরা চাই যখন এটি কল হবে ঠিক ওই সময় যেন লগ হিসবে একটা ফাইলে থাকে। এর সমাধান দেখে নেয়া যাক:
|
|
এই প্রোগ্রাম রান করলে কারেন্ট ওয়ার্কিং ডিরেক্টরিতে log.txt
নামের একটা ফাইল তৈরি হবে, সেটি খুললে hello()
এক্সিকিউট হওয়ার সময় গুলো পাওয়া যাবে।
প্যারামিটার/আর্গুমেন্ট সহ ডেকরেটর
যদি log
ডেকরেটর টায় প্যারামিটার হিসেবে ফাইলের নাম দিয়ে দেয়া যেত, log.txt
এর পরিবর্তে আমাদের প্রয়োজন মত নাম, তাহলে সুবিধা হতো না? হ্যা, ডেকরেটরে প্যারামিটার/আর্গুমেন্ট পাস করা সম্ভব। এজন্য আমাদের ডেকরেটরকে আরেকটা ফাংশনের মধ্যে নেস্টেড আকারে রাখা লাগবে। এরকম:
|
|
এখন আমাদের লগ history.txt
খুললে পাওয়া যাবে। আর যদি @log
এ কোন প্যারামিটার পাস না করা হয়, তাহলে ডিফল্ট ভাবে log.txt
তে লগ থাকবে।
এইত! এই ছিল পাইথনের ডেকরেটর্স কনসেপ্ট। যদিও যেভাবে উপস্থাপন করতে চেয়েছিলাম সেভাবে পারি নি, তারপরও আশা করছি অপেক্ষাকৃত নতুনেরা উপকৃত হবে। সামনে কোন এক সময় ক্লাস ডেকরেটর নিয়ে লিখবো ইনশাআল্লাহ।