এক্সেপশন হ্যান্ডেলিং

আগের চ্যাপ্টারে আমরা দেখেছি, এক্সেপশন তৈরি হলে প্রোগ্রাম অনাকাঙ্ক্ষিত ভাবে বন্ধ হয়ে যায়। খুশির খবর হচ্ছে এরকম তৈরি হওয়া এক্সেপশন গুলোকে সঠিকভাবে হ্যান্ডেল করতে পারলে প্রোগ্রাম যেমন বন্ধ না হয়ে এগিয়ে চলবে তেমনি প্রোগ্রামের কোথায় কোন সমস্যা আছে সেগুলোকেও সহজে চিহ্নিত করা যাবে। এ জন্য পাইথনে আছে try, except স্টেটমেন্টের ব্যবহার।

try ব্লকের মধ্যে এমন কোড গুলো লেখা হয় যেখানে এক্সেপশন তৈরি হতে পারে (ইউজার ইনপুট বা সেরকম অন্যান্য কারনে)। আর except ব্লকের মধ্যে লেখা হয় এমন কোড যেগুলো এক্সিকিউট হবে যদি আসলেই ওই try ব্লকের মধ্যে কোন এক্সেপশন তৈরি হয়। অর্থাৎ try এর মধ্যে এক্সেপশন তৈরি হলে এই ব্লকের কোড এক্সিকিউশন বন্ধ হবে কিন্তু except ব্লকের কোড স্বাভাবিক ভাবে এক্সিকিউট হবে। একটি উদাহরণ দেখি -

try:
    a = 1000
    b = int(input("Enter a divisor to divide 1000: "))
    print(a/b)
except ZeroDivisionError:
    print("You entered 0 which is not permitted!")

যদি ইনপুট হয় নিচের মত,

Enter a divisor to divide 1000: 5

তাহলে আউটপুট,

200.0

অথবা যদি ইনপুট হয় এরকম,

Enter a divisor to divide 1000: 0

তবে আউটপুট,

You entered 0 which is not permitted!

উপরের প্রোগ্রামে দুটো নাম্বার নিয়ে ভাগের কাজ করা হয়েছে। একটি নাম্বারের মান 1000 এবং আরেকটি নিচ্ছি ইউজারের কাছ থেকে। যদি ইউজার ভালোয় ভালোয় সঠিক সংখ্যা ইনপুট দেয় (যেমন 5) তাহলে প্রোগ্রামটি সঠিক ভাবে কাজ করে ভাগফল প্রিন্ট করছে। কিন্তু ইউজারের মনোভাব তো আমরা জানি না। ইউজার চাইলে শূন্য ইনপুট দিতে পারে। আর তখন প্রোগ্রাম ভাগ করতে না পেরে অনাকাঙ্ক্ষিত ভাবে বন্ধ হয়ে যাবে।

আর তাই সেটুকু আন্দাজ করেই আমরা ভাগ করার কোড টুকু একটি ট্রাই ব্লকের মধ্যে লিখেছি এবং সেই ব্লকের মধ্যে যদি শূন্য দিয়ে ভাগ করার কারনে কোন এক্সেপশন তৈরি হয় তাহলে সেটা হ্যান্ডেল করার জন্য এক্সেপ্ট ব্লক ব্যবহার করেছি এবং নির্দিষ্ট করে ZeroDivisionError এক্সেপশন হ্যান্ডেল করেছি। এখন, ইউজার চাইলে শূন্য ইনপুট দিতে পারে, তাই বলে প্রোগ্রাম অনাকাঙ্ক্ষিত ভাবে শাটডাউন বা বন্ধ হবে না। বরং ইউজারকে যথাযথ ম্যাসেজ দেখিয়ে স্বাভাবিক কাজ চালিয়ে যেতে পারছে।

একটি try ব্লকের সাপেক্ষে একাধিক except ব্লক থাকতে পারে। আবার একটি except এর জন্য একাধিক এক্সেপশন ডিফাইন করা যেতে পারে ব্র্যাকেট এবং কমা ব্যবহার করে। এতে করে ট্রাই ব্লকের মধ্যে বিভিন্ন রকম এক্সেপশনের জন্য বিভিন্ন এক্সেপ্ট ব্লক দিয়ে সঠিক ভাবে সমস্যাকে চিহ্নিত করা যায় এবং সে অনুযায়ী কাজ করা যায়। আরেকটি উদাহরণ দেখি -

try:
    variable = 10
    print(variable + "hello")
    print(variable / 2)
except ZeroDivisionError:
    print("Divided by zero")
except (ValueError, TypeError):
    print("Type or value error occurred")

আউটপুট,

Type or value error occurred

উপরের প্রোগ্রামে ট্রাই ব্লকে দুই রকম অঘটন ঘটতে পারে। variable কে 2 দিয়ে ভাগ না করে শূন্য দিয়ে ভাগ করা হতে পারতো এবং সেক্ষেত্রে ZeroDivisionError এক্সেপশন তৈরি হত। আবার ট্রাই ব্লকের দ্বিতীয় স্টেটমেন্ট যেখানে একটি ইন্টিজারের সাথে স্ট্রিং কে যোগ করে প্রিন্ট করার চেষ্টা করা হয়েছে, সেখানে। এই উদাহরণে এখানেই এক্সেপশন তৈরি হচ্ছে। আর তাই TypeError এক্সেপশন তৈরি হচ্ছে। কিন্তু আমরা সেটা সঠিকভাবে হ্যান্ডেল করেছি আর তাই প্রোগ্রাম হুট করে বন্ধ না হয়ে বরং সুন্দর ভাবে আমাদের নির্ধারিত একটি প্রিন্ট স্টেটমেন্ট print("Type or value error occurred") এক্সিকিউট করেছে।

চাইলে সুনির্দিষ্ট ভাবে কোন এক্সেপশন ডিফাইন না করেও except ব্লক ব্যবহার করা যাবে। সেক্ষেত্রে try ব্লকের মধ্যে ঘটে যাওয়া যেকোনো রকম এক্সেপশনের জন্য এই except ব্লক রান করবে। যেমন -

try:
    word = "spam"
    print(word / 0)
except:
    print("An error occurred")

আউটপুট,

An error occurred

বোঝাই যাচ্ছে try ব্লকের মধ্যে উল্টা পাল্টা টাইপের ডাটা নিয়ে ভাগ করার কোড লেখা হয়েছে। রান টাইমে এখানে অবশ্যই এক্সেপশন তৈরি হচ্ছে। আর তাই except ব্লক ব্যবহার করে হ্যান্ডেলও করা হয়েছে। আপাত দৃষ্টিতে বিষয়টি ভালো মনে হলেও এভাবে হ্যান্ডেল করা ব্লকের মাধ্যমে ট্রাই ব্লকে ঘটে যাওয়া অঘটনের সঠিক কারণ চিহ্নিত করা যাবে না।
অর্থাৎ, ট্রাই ব্লকে যেকোনো রকম সমস্যার জন্যই এই এক্সেপ্ট ব্লক এক্সিকিউট হবে এবং বার বার শুধু An error occurred ম্যাসেজটাই ইউজারকে দেখানো হবে। কিন্তু যদি সম্ভাবনাময় কয়েকটি নির্দিষ্ট টাইপের এক্সেপ্ট ব্লক লিখে সেগুলোর মধ্যে আলাদা আলাদা ম্যাসেজ প্রিন্ট করা হত। তাহলে ইউজারকে আরও সুনির্দিষ্ট ম্যাসেজ দেয়া যেত এবং প্রোগ্রামটিকে পরবর্তীতে আপডেট করতেও সুবিধা হত।

সংকলন - নুহিল মেহেদী