Skip to content

অবজেক্ট ও ক্লাস

এই অধ্যায়ে আমরা অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং সম্পর্কে জানবো। এর জন্য আমাদেরকে ফেরত যেতে হবে সেই টার্টলের কাছে। আমরা ইতিমধ্যে এই বইতে টার্টল মডিউলের বিভিন্ন ফাংশন কল করে বিভিন্ন রকমের কাজ করেছি। উদাহরণ হিসেবে আমি এখন টার্টল দিয়ে বৃত্ত তৈরি করার একটি প্রোগ্রাম লিখবো।

import turtle

turtle.circle(50)

turtle.done()

প্রোগ্রামটি রান করলে একটি বৃত্ত তৈরি হবে। তো এই ধরণের প্রোগ্রামিংকে বলা হয় স্ট্রাকচার্ড প্রোগ্রামিং (আমরা এই বইতে এবং তার আগের বইতে বেশিরভাগ সময়ই স্ট্রাকচার্ড প্রোগ্রামিং করেছি)। এখন আমরা এই প্রোগ্রামটিই করবো, কিন্তু সেজন্য turtle মডিউলের মধ্যে Turtle নামে একটি ক্লাস (class) আছে, সেটি ব্যবহার করে। আমরা Turtle ক্লাস ব্যবহার করে টার্টলের অবজেক্ট বা ইনস্ট্যান্স (instance) তৈরি করবো। এখন এই ক্লাস, অবজেক্ট, ইনস্ট্যান্স – এগুলো কী জিনিস?

প্রথমে বলে নিই, অবজেক্ট আর ইনস্ট্যান্স একই জিনিস। একেক সময় একেক নাম ব্যবহার করা হয়। আর যখনই অবজেক্ট (বা ইনস্ট্যান্স)-এর কথা বলা হয়, তার সঙ্গে আরেকটি জিনিস যুক্ত করে দিতে হয়, যে এই অবজেক্টটি কোন ক্লাসের অবজেক্ট? ক্লাস হচ্ছে মূল নকশা আর সেই ক্লাসের এক বা একাধিক অবজেক্ট তৈরি করা যায়, যারা ওই ক্লাসের সব বৈশিষ্ট্য ধারণ করবে। যেমন, একটি গাড়ির ডিজাইন হচ্ছে ক্লাস আর সেই ডিজাইন অনুসরণ করে যত গাড়ি তৈরি করা হয়, সেই গাড়িগুলো হচ্ছে ওই ক্লাসের অবজেক্ট। তেমনি turtle মডিউলের ভেতরে Turtle ক্লাসে বলা আছে যে, একটি টার্টলের কী কী বৈশিষ্ট্য থাকবে এবং সেটি কী কী কাজ করতে পারবে ও কিভাবে করতে পারবে। এই বৈশিষ্ট্যগুলোকে বলা হয় ডেটা অ্যাট্রিবিউট (data attribute) আর যেসব কাজ করতে পারবে, সেগুলোকে বলা হয় মেথড (method)। আসলে মেথডগুলো হচ্ছে ক্লাসের ভেতরে তৈরি করা ফাংশন, তবে ক্লাস ও অবজেক্টের বেলায় তাদেরকে আমরা মেথড বলি। একারণেই এই বইতে এবং আগের বইতে কোথাও কোথাও ফাংশন, কোথাও কোথাও মেথড শব্দ ব্যবহার করা হয়েছে।

অবজেক্ট তৈরি ও ব্যবহার

টার্টল ক্লাসের অবজেক্ট তৈরি করবো কীভাবে? পাইথন ইন্টারপ্রেটারে সেটি আমি দেখাচ্ছি।

>>> import turtle
>>> tom = turtle.Turtle()

প্রথমে আমরা turtle মডিউল ইমপোর্ট করছি। তারপর tom নামে Turtle ক্লাসের একটি অবজেক্ট তৈরি করছি। এরজন্য মডিউলের নাম (turtle), তারপরে একটি ডট (.), তারপরে ক্লাসের নাম (Turtle) এবং তারপরে প্রথম বন্ধনী ব্যবহার করেছি। পাইথনে ক্লাসের নামের প্রথম অক্ষর সাধারণত বড় হাতের (capital letter) হয়। এখন আমি tom নামের এই টার্টলকে দিয়ে বিভিন্ন কাজ করাতে পারবো। যেমন, আমি যদি 100 পিক্সেল ব্যাসার্ধের একটি বৃত্ত আঁকতে চাই, তাহলে লিখবো :

>>> tom.circle(100)

এখন আমি যদি দেখতে চাই যে, tom কী টাইপের (বা কোন ক্লাসের) অবজেক্ট, তাহলে আমাকে type() ফাংশন ব্যবহার করতে হবে।

>>> type(tom)
<class 'turtle.Turtle'>

আমরা দেখতে পাচ্ছি, tom হচ্ছে turtle মডিউলের অন্তর্গত Turtle ক্লাসের অবজেক্ট।

আমরা চাইলো আরো টার্টল অবজেক্ট তৈরি করতে পারি। আমি এখন আরো দুটি টার্টল ক্লাসের অবজেক্ট তৈরি করবো :

>>> nonte = turtle.Turtle()
>>> fonte = turtle.Turtle()

এখন আমি তাদের আকৃতি পরিবর্তন করে দেবো। তারপরে nonte-কে 30 ডিগ্রী বাম দিকে ঘুরে 100 পিক্সেল সামনে যেতে বলবো। আর Fonte-কে বলব 50 পিক্সেল পেছনে চলে আসতে।

>>> nonte.shape("circle")
>>> fonte.shape("square")
>>> nonte.left(30)
>>> nonte.forward(100)
>>> fonte.backward(50)

প্রতিটি লাইনের পরপর কিন্তু স্ক্রিন হালনাগাদ (আপডেট) হবে। সেটি সঙ্গে সঙ্গে দেখতে হবে। তাহলে বুঝতে পারা যাবে যে, কোন লাইন কী কাজ করছে। এখন আমি আরেকটি টার্টল অবজেক্ট তৈরি করবো এবং টার্টলগুলোকে আরো কিছু কাজ করতে বলবো।

>>> monte = turlte.Turtle()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'turlte' is not defined
>>> monte = turtle.Turtle()
>>> monte.setpos(-100, -100)
>>> 
>>> monte.foward(30)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Turtle' object has no attribute 'foward'
>>> monte.forward(30)
>>> monte.clear()
>>> monte.clear()
>>> fonte.clear()
>>> fonte.shape("triangle")
>>> monte.shape("square")
>>>

ওপরের স্টেটমেন্টগুলো চালালে শেষ পর্যন্ত স্ক্রিন নিচের মতো দেখাবে :

এখন ওপরে আমরা কিন্তু দুটি এরর দেখতে পাচ্ছি, একটি হচ্ছে NameError, আরেকটি হচ্ছে AttributeError। প্রথম এররটি বলছে, NameError: name ‘turlte’ is not defined, অর্থাৎ পাইথন ইন্টারপ্রেটার turlte নামে কোনো কিছু খুঁজে পায় নি (কারণ, আমি বানান ভুল করেছি)। আর দ্বিতীয় এররটি বলছে, AttributeError: ‘Turtle’ object has no attribute ‘foward’, অর্থাৎ Turtle ক্লাসে foward নামে কোনো কিছু নেই (কারণ, এখানেও আমি বানান ভুল করেছি)। তার মানে, আমরা কেবল সেসব জিনিসই ব্যবহার করতে পারবো, যেগুলো ক্লাসের মধ্যে বলে দেওয়া আছে।

এতক্ষণ আমরা শিখলাম, কিভাবে একটি ক্লাসের অবজেক্ট তৈরি করতে হয়, অবজেক্টগুলোকে কিভাবে ব্যবহার করতে হয়। আবার একটু ভুল করলে কী হয়, তাও দেখলাম। অবজেক্ট তৈরির একটি বড় সুবিধা হচ্ছে, ক্লাসের মধ্যে যেসব বৈশিষ্ট্যর কথা বলা আছে, আমরা যখন সেই ক্লাসের অবজেক্ট তৈরি করবো, তখন প্রতিটি অবজেক্টের জন্য সেই বৈশিষ্ট্যগুলো আলাদা হবে। তাই আমরা nonte, fonte, monte – এই তিনটি টার্টলকে তিন রকম আকৃতি দিতে পারি, তাদেরকে আলাদা রংও দিতে পারি। তাদেরকে বিভিন্ন কমান্ড দিয়ে আলাদা জায়গায় পাঠাতে পারি। একজনের বৈশিষ্ট্য বা আচরণ অন্যজনের ওপর কোনো প্রভাব ফেলে না। nonte-কে 100 পিক্সেল সামনে যেতে বললে কেবল সে-ই সেই কাজটি করবে, বাকী সবাই নিজেদের মতো থাকবে।

একটি অবজেক্টের কী কী অ্যাট্রিবিউট (ডেটা অ্যাট্রিবিউট ও মেথড) আছে, সেটা জানতে চাইলে dir() ফাংশন ব্যবহার করতে হবে। এসব অ্যাট্রিবিউটের নাম দেখে অবশ্য খুব বেশি কিছু বোঝা যাবে না। বরং ডকুমেন্টেশন পড়লেই বিস্তারিত জানা যাবে। আমরা আগের বইতে লিস্ট তৈরি করা শিখেছিলাম। যখন কোনো লিস্ট তৈরি করা হয়, সেই লিস্ট আসলে list ক্লাসের একটি অবজেক্ট।

>>> li = [1, 10, 2, 20]
>>> type(li)
<class 'list'>

ওপরে দেখতে পাচ্ছি যে, li হচ্ছে list ক্লাসের অবজেক্ট। এখন এই li-এর কী কী অ্যাট্রিবিউট আছে, তাও আমরা জানতে পারি, এভাবে :

>>> dir(li)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
'__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
'__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__',
'__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__',
'__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__',
'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop',
'remove', 'reverse', 'sort']

যেসব অ্যাট্রিবিউটগুলোর নাম আন্ডারস্কোর চিহ্ন (_) দিয়ে শুরু হয়েছে, সেগুলো আপাতত আমাদের বোঝার দরকার নেই। সেগুলো বাদ দিলে আমরা দেখতে পাচ্ছি, লিস্টের এই অ্যাট্রিবিউটগুলো : ‘append’, ‘clear’, ‘copy’, ‘count’, ‘extend’, ‘index’, ‘insert’, ‘pop’, ‘remove’, ‘reverse’, ‘sort’। এগুলোর বেশ কিছু ব্যবহার আমরা আগের বইতে দেখেছি। এখন চাইলে নিজে নিজে পরীক্ষানিরীক্ষা করে দেখা যেতে পারে যে, কোন অ্যাট্রিবিউট কী কাজ করে। আর পাইথনের অফিশিয়াল ডকুমেন্টেশনেও লিস্টের বিভিন্ন মেথডগুলোর কাজের বর্ণনা লেখা আছে। যেহেতু li হচ্ছে list ক্লাসের একটি অবজেক্ট, তাই এর বিভিন্ন মেথড ব্যবহার করার সময় আমরা প্রথমে লিখবো অবজেক্টের নাম, তারপরে ডট, তারপরে মেথডের নাম। যেমন : li.sort()।

>>> li.sort()
>>> li
[1, 2, 10, 20]

নতুন ক্লাস তৈরি করা

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

ডেটা অ্যাট্রিবিউট:

  • নাম (name)
  • উৎপাদনকারী প্রতিষ্ঠান (manufacturer)
  • রং (color)
  • তৈরির সাল (year)
  • ইঞ্জিনের ক্ষমতা (সিসি) (cc)

মেথড :

  • ইঞ্জিন চালু করা (start)
  • ব্রেক করা (brake)
  • চালানো (drive)
  • ডানে-বাঁয়ে ঘোরা (turn)
  • গিয়ার পরিবর্তন (change gear)

এভাবে প্রথমে আমাদের ক্লাসের ডিজাইন করতে হবে। তারপর ক্লাস ডায়াগ্রাম (class diagram) তৈরি করতে হবে, তবে এই বইতে সেটি আমি দেখাবো না, তাই আপাতত এটি না শিখলেও চলবে। তারপরে আমরা পাইথনে ক্লাসটি তৈরি করবো। এখন প্রশ্ন হচ্ছে, আমরা কি করে বুঝবো যে আমাদেরকে একটি ক্লাস তৈরি করতে হবে? আমরা যখন সফটওয়্যার তৈরি করবো, এটি যেই সমস্যার সমাধানের জন্য তৈরি, সেটি নিয়ে চিন্তা করে বিশেষ্য পদগুলো (Noun) খুঁজে বের করতে হবে। দেখা যাবে এগুলোর বেশিরভাগের জন্যই আমাদের ক্লাস তৈরি করার দরকার হবে। যেমন, কার রেসিং গেমের ক্ষেত্রে, Car, Player, Track ইত্যাদি। আবার আমরা যদি স্কুল ম্যানেজমেন্ট করার জন্য একটি সফটওয়্যার তৈরি করি, তাহলে সেখানে Student, Teacher, Employee, ClassRoom, Subject, Result ইত্যাদি ক্লাস থাকতে পারে। সবগুলোই কিন্তু বিশেষ্য পদ, অর্থাৎ Noun।

পাইথনে ক্লাস তৈরি করতে হলে প্রথমে class লিখে একটি স্পেস দিয়ে ক্লাসের নাম লিখতে হয়, তারপরে সেই ক্লাসের ভেতরে বিভিন্ন স্টেটমেন্ট লেখা হয়। এখানেও ইনডেন্টেশন করতে হয়। ক্লাস ডিক্লেয়ার করার পরের লাইন থেকে যতটুকু কোড ক্লাসের ভেতরে থাকবে, তারা এক ট্যাব (tab) ডান দিক থেকে শুরু হবে।

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>

ওপরে ClassName হচ্ছে ক্লাসের নাম। আমরা যেই নামের ক্লাস তৈরি করতে চাই, সেই নামটি সেখানে ব্যবহার করবো। আর স্টেটমেন্ট-এর জায়গায় বিভিন্ন ভ্যারিয়েবলে মান রাখা কিংবা মেথড তৈরি করা যাবে।

class Car:
    name = "Premio"
    color = "white"

    def start():
        print("Starting the engine")

ওপরে আমরা Car নামে একটি ক্লাস তৈরি করলাম। তারপরে সেখানে name ও color নামে দুটি ডেটা অ্যাট্রিবিউট রাখলাম, আর start নামে একটি মেথড তৈরি করলাম। এখন আমরা এই অ্যাট্রিবিউটগুলো ব্যবহার করতে পারবো। নিচের কোডে সেটি দেখানো হয়েছে :

class Car:
    name = "Premio"
    color = "white"

    def start():
        print("Starting the engine")


print("Name of the car:", Car.name)
print("Color:", Car.color)

Car.start()

প্রোগ্রামটি সেভ করে রান করলে নিচের মতো আউটপুট আসবে :

$ python car.py 
Name of the car: Premio
Color: white
Starting the engine

[নোট: কার রেসিং গেম তৈরি করার সময় কিন্তু start() মেথড কেবল একটি লাইন প্রিন্ট করবে না, বরং গাড়ি চালু করার একটা শব্দ হবে, অ্যানিমেশন হবে। কিন্তু সেগুলো আমার এই বইতে আলোচনার বিষয় নয়। আমরা বরং অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং শেখার চেষ্টা করছি।]

এখন কেউ প্রশ্ন করতে পারে যে, সব গাড়ির রং তো সাদা হবে না, আর সব গাড়ির নামও প্রিমিও (Premio) হবে না। তাহলে কী করা যায়? আমরা ক্লাসের ভেতরে ওই ডেটা অ্যাট্রিবিউটগুলোয় ফাঁকা স্ট্রিং অ্যাসাইন করতে পারি। তারপরে সেগুলো পরিবর্তন করা যাবে।

class Car:
    name = ""
    color = ""

    def start():
        print("Starting the engine")


Car.name = "Axio"
Car.color = "Black"
print("Name of the car:", Car.name)
print("Color:", Car.color)

Car.start()

এখন প্রোগ্রামটি রান করে দেখব। আর বইটি কিন্তু কেবল পড়ে গেলে চলবে না, সবকিছু বইয়ের সঙ্গে সঙ্গে নিজেও কোড করে প্র্যাকটিস করতে হবে।

$ python car.py 
Name of the car: Axio
Color: Black
Starting the engine

আমরা যদি ওপরের কোডে print(dir(Car)) লিখি, তাহলে Car ক্লাসের তিনটি অ্যাট্রিবিউট দেখতে পাবো : ‘color’, ‘name’, ‘start’। সেই সঙ্গে অবশ্য আরো অ্যাট্রিবিউট দেখাবে, যেগুলো _ দিয়ে শুরু, আপাতত সেগুলো নিয়ে চিন্তা করা দরকার নেই।

এখন পর্যন্ত আমরা সরাসরি Car ক্লাসের বিভিন্ন অ্যাট্রিবিউট ব্যবহার করেছি। এখন আমরা Car ক্লাসের অবজেক্ট তৈরি করবো। এই অবজেক্ট তৈরির কাজটিকে পাইথনের ভাষায় বলে ইনস্ট্যানশিয়েট (instantiate) করা। এজন্য অবজেক্টকে ইনস্ট্যান্সও বলা হয়ে থাকে।

class Car:
    name = ""
    color = ""

    def start():
        print("Starting the engine")

# creating a Car object
my_car = Car()
my_car.name = "Allion"
print(my_car.name)
my_car.start()

এখানে আমি প্রথমে Car ক্লাসের একটি অবজেক্ট তৈরি করলাম : my_car = Car()। তারপর, my_car – এর নাম দিয়ে দিলাম : my_car.name = “Allion” এবং সেটি প্রিন্ট করে দেখলাম। শেষ লাইনে my_car-কে চালু করার চেষ্টা করলাম, তার start() মেথড কল করে। কিন্তু এখানেই বিপত্তি। প্রোগ্রামটি যদি আমরা চালাই, তাহলে নিচের মতো আউটপুট দেখবো :

$ python car.py 
Allion
Traceback (most recent call last):
  File "car.py", line 12, in <module>
    my_car.start()
TypeError: start() takes 0 positional arguments but 1 was given

নাম প্রিন্ট হলো ঠিকই, কিন্তু তারপরে এরর দিলো যে, start() takes 0 positional arguments but 1 was given। এর অর্থ হচ্ছে, start() মেথড যখন তৈরি করা হয়েছে, সেখানে সেটি আর্গুমেন্ট হিসেবে কোনো কিছু নেয় না (def start()), মানে 0টি আর্গুমেন্ট নেয়, কিন্তু আমরা 1টি আর্গুমেন্ট পাঠাচ্ছি (but 1 was given)! কিন্তু আমরা কোথায় একটি আর্গুমেন্ট পাঠালাম? আমরা তো লিখেছি my_car.start()। ব্র্যাকেটের ভেতরে কিছু লিখি নি। আমরা যখন কোনো অবজেক্টের মেথড কল করি (যেমন : my_car.start()), তখন সেই মেথডের ভেতরে অবজেক্টটি আপনাআপনি চলে যায়। কেন যায়, সেটি আমরা পরবর্তি সময়ে আলোচনা করবো। আমাদের এখন যেটি করতে হবে, সেটি হচ্ছে, def start()-এর বদলে লিখতে হবে def start(self)।

class Car:
    name = ""
    color = ""

    def start(self):
        print("Starting the engine")

my_car = Car()
my_car.name = "Allion"
print(my_car.name)
my_car.start()

এখন প্রোগ্রামটি রান করলে ঠিকঠাক চলবে। self-এর বদলে অন্যকিছু লিখলেও চলে, কিন্তু এই ক্ষেত্রে পাইথনের রীতি হচ্ছে self লেখা।

আমরা যখন একটি Car ক্লাসের অবজেক্ট তৈরি করছি, চাইলে তখনই আমরা সেই অবজেক্টের বিভিন্ন বৈশিষ্ট্য বলে দিতে পারি। প্রথমে আমরা নিচের কোডটি টাইপ করে রান করবো, তারপর সেটি আলোচনা করবো।

class Car:
    name = ""
    color = ""

    def __init__(self, name, color):
        self.name = name
        self.color = color

    def start(self):
        print("Starting the engine")

my_car = Car("Corolla", "White")
print(my_car.name)
print(my_car.color)
my_car.start()

প্রোগ্রামটি রান করলে ঠিকঠাক আউটপুট আসবে। এখানে আমি একটি নতুন মেথড ব্যবহার করেছি, __init__()। যখন কোনো ক্লাসের অবজেক্ট তৈরি করা হয়, তখন এই মেথডটি আপনাআপনি (অটোমেটিক) কল হয়। তাই যখন my_car = Car(“Corolla”, “White”) স্টেটমেন্টটি চলে, তখন আসলে Car ক্লাসের __init__ মেথড কল হয়। সেই মেথডের প্রথম প্যারামিটার হচ্ছে self। এটি সবসময়ই দিতে হবে। তারপরে যেহেতু আমরা গাড়ির নাম ও রং সেট করে দিতে চাই, তাই name ও color নামে দুটি প্যারামিটার রাখছি। তারপরে ফাংশনের ভেতরে self.name-এ name অ্যাসাইন করছি, আর self.color-এ color অ্যাসাইন করছি। আমি যখন লিখছি self.name, সেটি বোঝাচ্ছে যে, যেই অবজেক্টটি আমি তৈরি করছি, তার name অ্যাট্রিবিউট। এখানে Car ক্লাসের ভেতরে যে name ডিক্লেয়ার করেছি (name = “”), self.name, আর init ফাংশনের name প্যারামিটার – তিনটি কিন্তু আলাদা। এখানে এসে ব্যাপারটা একটু এলোমেলো লাগতে পারে। তাই আমরা ওপরের কোডটি একটু পরিবর্তন করে লিখি।

class Car:
    def __init__(self, n, c):
        self.name = n
        self.color = c

    def start(self):
        print("Starting the engine")

my_car = Car("Corolla", "White")
print(my_car.name)
print(my_car.color)
my_car.start()

প্রোগ্রামটি রান করলে আউটপুট আসবে এরকম –

$ python car.py 
Corolla
White
Starting the engine

এখানে যখন আমি my_car = Car(“Corolla”, “White”) লিখছি, তখন __init__(self, n, c)-এর self-এ যাচ্ছে my_car অবজেক্টের রেফারেন্স, n-এ যাচ্ছে “Corolla”, আর c-তে যাচ্ছে “White”। তারপর যখন self.name = n স্টেটমেন্ট এক্সিকিউট হচ্ছে, তখন my_car অবজেক্টের name নামে একটি অ্যাট্রিবিউট তৈরি হচ্ছে আর সেখানে n অ্যাসাইন হচ্ছে। একইভাবে color অ্যাট্রিবিউট তৈরি হয়ে সেখানে c-তে যা পাঠিয়েছিলাম, তা অ্যাসাইন হচ্ছে।

Car ক্লাসে যে দুটি ডেটা অ্যাট্রিবিউট তৈরি করেছিলাম (name ও color), সেগুলো কিন্তু আমি বাদ দিয়ে দিয়েছি। কারণ আমি অবজেক্ট তৈরি করার সময় init মেথডের ভেতরে self.name ও self.color যখন লিখছি, তখন সেই অবজেক্টের জন্য name ও color নামে অ্যাট্রিবিউট তৈরি হয়ে যাচ্ছে। একে বলে ইনস্ট্যান্স অ্যাট্রিবিউট, যা কেবল ওই ক্লাসের ইনস্ট্যান্সের (বা অবজেক্টের) থাকে। তবে এখন কিন্তু আর ক্লাসের নাম লিখে ডট দিয়ে name (ও color) একসেস করা যাবে না। আমরা যদি লিখি print(Car.name), তাহলে আমরা এরর পাবো : AttributeError: type object ‘Car’ has no attribute ‘name’। অর্থাৎ Car ক্লাসের name নামে কোনো অ্যাট্রিবিউট নেই। আমরা যখন এরকম লিখেছিলাম –

class Car:
    name = ""
    color = ""

তখন, এই name ও color–কে বলা হয় ক্লাস অ্যাট্রিবিউট। বেশিরভাগ সময়ই আমাদের এরকম অ্যাট্রিবিউট তৈরি করার দরকার পরবে না।

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

class Car:
    def __init__(self, n, c):
        self.name = n
        self.color = c

    def start(self):
        print("name: ", self.name)
        print("color: ", self.color)
        print("Starting the engine")

my_car = Car("Corolla", "White")
my_car.start()

ওপরের প্রোগ্রামে আমরা start() মেথডের ভেতরে অবজেক্টের নাম ও রং প্রিন্ট করেছি। এখন একটি মজার জিনিস দেখাই। self-এ যে অবজেক্ট পাঠানো হয়, তার প্রমাণ পাওয়া যাবে নিচের প্রোগ্রাম চালালে –

class Car:
    def __init__(self, n, c):
        self.name = n
        self.color = c

    def start(self):
        print("name: ", self.name)
        print("color: ", self.color)
        print("Starting the engine")

my_car = Car("Corolla", "White")

Car.start(my_car)

আমরা my_car অবজেক্ট তৈরি করলাম। তারপর, Car.start() মেথড কল করলাম কিন্তু আর্গুমেন্ট হিসেবে my_car অবজেক্ট ব্যবহার করলাম। এই প্রোগ্রামের আউটপুট কিন্তু হুবুহু আগের প্রোগ্রামের মতোই হবে। তবে বলে রাখা ভালো যে, সাধারণত এভাবে আমরা start() মেথড কল করবো না।

এখন আমরা চাইলে একাধিক কার অবজেক্ট তৈরি করতে পারি।

class Car:
    def __init__(self, n, c):
        self.name = n
        self.color = c

    def start(self):
        print("name: ", self.name)
        print("color: ", self.color)
        print("Starting the engine")

my_car1 = Car("Corolla", "White")
my_car1.start()
my_car2 = Car("Premio", "Black")
my_car2.start()
my_car3 = Car("Allion", "Blue")
my_car3.start()

প্রোগ্রামটি রান করলে আউটপুট আসবে এরকম :

$ python car.py 
name:  Corolla
color:  White
Starting the engine
name:  Premio
color:  Black
Starting the engine
name:  Allion
color:  Blue
Starting the engine

প্রতিটি অবজেক্টের অ্যাট্রিবিউটগুলো কিন্তু আলাদা। যেমন, my_car1-এর color আর my_car2-এর color আলাদা। এটি হচ্ছে অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিংয়ের একটি বড় সুবিধা। আমরা যত খুশি অবজেক্ট তৈরি করতে পারি। প্রতিটি অবজেক্ট তার নিজস্ব অ্যাট্রিবিউট ধারণ করবে।

আরেকটি কথা বলা প্রয়োজন। আমরা যে __init__ মেথড ব্যবহার করছি, যা অবজেক্ট তৈরি হওয়ার সময় আপনাআপনি কল হয়, একে বলে কনস্ট্রাকটর (constructor)।

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

class Car:
    def __init__(self, n, c):
        self.name = n
        self.color = c

    def start(self):
        print("Starting the engine")

car = Car("Corolla", "White")
car.year = 2017
print(car.name, car.color, car.year)

অনুশীলনীঃ আমরা শুরুতে Car ক্লাসের যে ডিজাইন করেছিলাম, আমি কিন্তু সেটি পুরোপুরি ইমপ্লিমেন্ট করি নি। এখন সেই ডিজাইন দেখে ক্লাসটি পুরোপুরি ইমপ্লিমেন্ট করতে হবে।

Published inUncategorized

Be First to Comment

Leave a Reply