Skip to content

পাইথনের ডেটা স্ট্রাকচার – লিস্ট ও টাপল

আমরা বইতে এতক্ষণ ছোট ছোট অনেক প্রোগ্রাম লিখেছি, যেগুলো প্রোগ্রামিংয়ের খুবই মৌলিক বিষয়। তবে একথাও ঠিক যে এসব বিষয় সহজ হলেও আয়ত্বে আনা খুব সহজ কোনো ব্যাপার নয়। প্রোগ্রামিং শেখার শুরুর দিকে কয়েকমাস প্রতিদিন একনাগাড়ে পাঁচ-ছয় ঘণ্টা চর্চা করা জরুরী। আর প্রোগ্রামিং যেহেতু একটা দক্ষতা, তাই বই পড়ার সঙ্গে সঙ্গে প্র্যাকটিস করাটা জরুরী।

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

লিস্ট (list)

লিস্ট যেহেতু আমরা পূর্বেই ব্যবহার করেছি, তাই লিস্ট নিয়ে মৌলিক কোনো আলোচনায় যাবো না। বরং লিস্টের বিভিন্ন মেথড ও তার ব্যবহার দেখবো। আর এই বইতে আমি কখনো কখনো ফাংশন, কখনো কখনো মেথড শব্দটি ব্যবহার করছি। আমরা এই বইয়ের পরবর্তি খণ্ডে ফাংশন ও মেথডের পার্থক্য বিস্তারিত জানবো। এখন এগুলো নিয়ে মাথা না ঘামালেও চলবে।

লিস্টের শেষে নতুন উপাদান যোগ করার জন্য আমরা append() মেথড ব্যবহার করবো।

>>> saarc = ["Bangladesh", "India", "Sri Lanka", "Pakistan", "Nepal", "Bhutan"]
>>> print(saarc)
['Bangladesh', 'India', 'Sri Lanka', 'Pakistan', 'Nepal', 'Bhutan']
>>> saarc.append("Afganistan")
>>> print(saarc)
['Bangladesh', 'India', 'Sri Lanka', 'Pakistan', 'Nepal', 'Bhutan', 'Afganistan']

ওপরের উদাহরণে আমরা দেখলাম যে সার্কভুক্ত দেশগুলোর তালিকায় কিভাবে আমরা একটি নতুন দেশকে যুক্ত করতে পারি।

আমরা যখন স্ট্রিংয়ে বিভিন্ন অপারেশন চালিয়েছি কিংবা বিভিন্ন মেথড ব্যবহার করেছি, তখন কিন্তু মূল স্ট্রিংয়ে কোনো পরিবর্তন হয় নি, বরং নতুন একটি স্ট্রিং তৈরি হয়েছে। কিন্তু লিস্টের বেলায় মূল লিস্ট পরিবর্তিত হয়ে যায়। কারণ লিস্ট হচ্ছে মিউটেবল (mutable)। অর্থাৎ, লিস্ট মিউটেশন বা পরিবর্তন করা যায়।

আমরা যদি চাই, কোনো লিস্টের উপাদানগুলো একটি নির্দিষ্ট ক্রমে সাজাবো, তখন আমরা sort() মেথড ব্যবহার করতে পারি। যেমন :

>>> saarc.sort()
>>> print(saarc)
['Afganistan', 'Bangladesh', 'Bhutan', 'India', 'Nepal', 'Pakistan', 'Sri Lanka']
>>> li = [1, 3, 7, 2, 4, 6, 1]
>>> li.sort()
>>> print(li)
[1, 1, 2, 3, 4, 6, 7]
>>>

লিস্টের উপাদানগুলো উল্টো ক্রমে নিয়ে আসতে চাইল reverse() মেথড ব্যবহার করা যায়।

>>> li = [1, 2, 3, 4]
>>> li.reverse()
>>> print(li)
[4, 3, 2, 1]
>>> li = ["mango", "banana", "orange"]
>>> li.reverse()
>>> print(li)
['orange', 'banana', 'mango']
>>>

আমরা দেখেছি যে, append() ব্যবহার করে লিস্টের শেষে নতুন উপাদান যোগ করা যায়। কিন্তু আমরা যদি চাই যে লিস্টের শুরুতে কিংবা যেকোনো অবস্থানে আমরা একটি উপাদান যোগ করবো, তখন আমরা insert() মেথড ব্যবহার করতে পারি। এই মেথডে দুটো আর্গুমেন্ট পাঠাতে হবে, insert(index, item)। index হচ্ছে লিস্টের কোন ইনডেক্সে উপাদানটি থাকবে আর item হচ্ছে সেই উপাদান। নিচের উদাহরণ দেখলেই বিষয়টি পরিষ্কার হবে :

>>> fruits = ["mango", "banana", "orange"]
>>> fruits.insert(0, "apple")
>>> fruits
['apple', 'mango', 'banana', 'orange']
>>> fruits.insert(2, "coconut")
>>> fruits
['apple', 'mango', 'coconut', 'banana', 'orange']
>>>

আমরা যদি লিস্টের কোনো উপাদান বাদ দিতে চাই, তখন আমরা remove() মেথড ব্যবহার করতে পারি। যেই উপাদানটি আমরা বাদ দিতে চাই, সেটি remove() এর ভেতরে আর্গুমেন্ট আকারে পাঠাতে হবে। আর যদি ওই উপাদান লিস্টে না থাকে তাহলে এরর মেসেজ আসবে।

>>> fruits
['apple', 'mango', 'coconut', 'banana', 'orange']
>>> fruits.remove("coconut")
>>> fruits
['apple', 'mango', 'banana', 'orange']
>>> fruits.remove("pineapple")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
>>>

তাই আমরা চাইলে এখানে একটি শর্ত দিয়ে দিতে পারি, যেন এরর মেসেজ না আসে।

>>> fruits
['apple', 'mango', 'banana', 'orange']
>>> item = "pineapple"
>>> if item in fruits:
...     remove(item)
... else:
...     print(item, "not in list")
... 
pineapple not in list
>>>

এখন আমরা দেখবো pop() মেথডের ব্যবহার, যেটি কোনো লিস্টের শেষ উপাদানটি রিটার্ন করে, আবার সেই সঙ্গে লিস্ট থেকে মুছে ফেলে।

>>> fruits
['apple', 'mango', 'banana', 'orange']
>>> item = fruits.pop()
>>> item
'orange'
>>> fruits
['apple', 'mango', 'banana']

আবার আমরা যদি চাইতাম যে, লিস্টের একটি নির্দিষ্ট ইনডেক্সের উপাদান রিটার্ন করবে, তাহলে pop()-এর ভেতরে সেই ইনডেক্স দিয়ে দিলেই হবে।

>>> fruits
['apple', 'mango', 'banana']
>>> item = fruits.pop(1)
>>> item
'mango'
>>> fruits
['apple', 'banana']
>>>

একটি লিস্টের শেষে যদি আরেকটি লিস্ট যুক্ত করতে চাই, তাহলে আমরা extend() মেথড ব্যবহার করতে পারি।

>>> li = [1, 2, 3]
>>> li2 = [3, 4, 5, 6]
>>> li.extend(li2)
>>> li
[1, 2, 3, 3, 4, 5, 6]

কোনো উপাদান একটি লিস্টে কতবার আছে, সেটি আমরা দেখতে পারি count() মেথড ব্যবহার করে।

>>> li = [1, 2, 3, 3, 4, 5, 6]
>>> li.count(3)
2
>>> li.count(5)
1
>>> li.count(10)
0
>>>

আমরা যদি লিস্ট থেকে কোনো উপাদান মুছে ফেলতে চাই, কিংবা সম্পূর্ণ লিস্ট মুছে ফেলতে চাই, সেটি আমরা করতে পারি del() ফাংশন ব্যবহার করে।

>>> li
[1, 2, 3, 3, 4, 5, 6]
>>> del(li[1])
>>> li
[1, 3, 3, 4, 5, 6]
>>> del(li[0])
>>> li
[3, 3, 4, 5, 6]
>>> del(li)
>>> li
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'li' is not defined

আমাদের কখনও কখনও এমন লিস্ট তৈরি করা লাগতে পারে যেখানে শুরুতে কোনো উপাদান থাকবে না, তাকে বলে ফাঁকা লিস্ট। li = [] এভাবে আমরা ফাঁকা লিস্ট তৈরি করতে পারি।

>>> li = []
>>> for x in range(10):
...     li.append(x)
... 
>>> print(li)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

লিস্টের মধ্যে যোগ ও গুণ অপারেশন করা যায়। এগুলো করলে কী হয় নিচের উদাহরণে আমরা দেখবো :

>>> li1 = [1, 2, 3]
>>> li2 = [4, 5, 6]
>>> li = li1 + li2
>>> print(li)
[1, 2, 3, 4, 5, 6]
>>> 
>>> li1 = [1, 2, 3]
>>> li2 = li1 * 3
>>> print(li2)
[1, 2, 3, 1, 2, 3, 1, 2, 3]
>>>

একটি লিস্টে যদি একাধিক স্ট্রিং থাকে, তাহলে যেগুলো যুক্ত করে আমরা একটি স্ট্রিং তৈরি করতে পারি। আবার কী দিয়ে যুক্ত করবো, সেটিও আমরা নির্ধারন করে দিতে পারি। যেমন –

>>> li = ["a", "b", "c"]
>>> print(li)
['a', 'b', 'c']
>>> str = "".join(li)
>>> print(str)
abc
>>> str = ",".join(li)
>>> print(str)
a,b,c
>>> str = " - ".join(li)
>>> print(str)
a - b - c

লিস্ট কমপ্রিহেনশনস (list comprehensions)

আমরা এখন একটি মজার জিনিস দেখবো, যাকে বলে লিস্ট কমপ্রিহেনশনস। ধরা যাক, আমার একটি লিস্ট আছে যেখানে কিছু সংখ্যা আছে। আমি চাই আরেকটি লিস্ট তৈরি করতে, যেখানে প্রতিটি উপাদান হবে আগের লিস্টের উপাদানের দ্বিগুণ। তাহলে আমরা সেটি করতে পারি এভাবে :

>>> li = [1, 2, 3, 4]
>>> new_li = []
>>> for x in li:
...     new_li.append(2 * x)
... 
>>> print(new_li)
[2, 4, 6, 8]

লিস্ট কমপ্রিহেনশনস ব্যবহার করে আমি কাজটি খুব সংক্ষেপে করতে পারি :

>>> new_li = [2 * x for x in li]
>>> new_li
[2, 4, 6, 8]

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

>>> li = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> even_numbers = []
>>> for x in li:
...     if x % 2 == 0:
...         even_numbers.append(x)
... 
>>> print(even_numbers)
[2, 4, 6, 8, 10]

আর লিস্ট কমপ্রিহেনশনস ব্যবহার করে কাজটি করা যায় এভাবে :

even_numbers = [x for x in range(1, 11) if x % 2 == 0]

অনুশীলনী : একটি লিস্ট দেওয়া আছে, যেখানে একাধিক সংখ্যা আছে। আরেকটি লিস্ট তৈরি করতে হবে, যেখানে প্রতিটি উপাদান হচ্ছে আগের লিস্টের প্রতিটি উপাদানের বর্গ। অর্থাৎ, [1, 2, 3, 4] দেওয়া থাকলে [1, 4, 9, 16] তৈরি করতে হবে। লিস্ট কমপ্রিহেনশনস ব্যবহার করার চেষ্টা করতে হবে।

টাপল (Tuple)

টাপল হচ্ছে পাইথনের একটি ডেটা স্ট্রাকচার। এটি অনেকটা লিস্টের মতোই। তবে লিস্টে আমরা যেমন তৃতীয় বন্ধনী বা স্কয়ার ব্র্যাকেট ব্যবহার করতাম, টাপলের ক্ষেত্রে প্রথম বন্ধনী (একে ইংরেজীতে বলে প্যারেন্থেসিস) ব্যবহার করতে হবে।

>>> x = (1, 2, 3)
>>> type(x)
<class 'tuple'>

এখন একটি মজার বিষয় দেখি।

>>> x = 1, 2, 3
>>> type(x)
<class 'tuple'>
>>>

তারমানে প্রথম বন্ধনী না দিলেও পাইথন x-কে একটি টাপল হিসেবেই বিবেচনা করছে। এখন এভাবে একটি উপাদনের টাপল তৈরি করা সম্ভব? নিচের কোড সেই প্রশ্নের উত্তর দেবে :

>>> x = 1
>>> type(x)
<class 'int'>
>>> x = 1, 
>>> type(x)
<class 'tuple'>

আমাদের যদি কখনো খালি টাপল তৈরি করা লাগে, তখন আমরা এভাবে করতে পারি :

>>> t = ()
>>> type(x)
<class 'tuple'>

টাপলের প্রতিটি উপাদান আমরা ইনডেক্স ব্যবহার করে পেতে পারি, আর এই ইনডেক্সও 0 থেকে শুরু হয়।

>>> tpl = (1, 2, 3)
>>> tpl[0]
1
>>> tpl[1]
2
>>> tpl[2]
3
>>> tpl[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: tuple index out of range

এখন, টাপল কিন্তু মিউটেবল (mutable) নয়। অর্থাৎ, এই যে tpl নামে একটি টাপল তৈরি করলাম যার তিনটি উপাদান যথাক্রমে 1, 2 ও 3, এখন কিন্তু আমি আর পরিবর্তন করতে পারবো না, যেটি লিস্টের বেলাতে সম্ভব।

>>> li = [1, 2, 3]
>>> li
[1, 2, 3]
>>> li[0] = 5
>>> li
[5, 2, 3]
>>> tpl = (1, 2, 3)
>>> tpl
(1, 2, 3)
>>> tpl[0] = 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

আমরা অনেকগুলো ভ্যারিয়েবল একসঙ্গে প্যাক (pack) করে একটি টাপলে রাখতে পারি, আবার একটি টাপলকে আনপ্যাক (unpack) করে তার উপাদানগুলো বিভিন্ন ভ্যারিয়েবলে রাখা যায়।

>>> numbers = (10, 20, 30, 40)
>>> n1, n2, n3, n4 = numbers
>>> n1
10
>>> n2
20
>>> n3
30
>>> n4
40
>>> t = n3, n4
>>> t
(30, 40)
>>> type(t)
<class 'tuple'>

টাপলের ভেতরে আমরা বিভিন্ন জিনিস রাখতে পারি, এবং এগুলোর ওপর লুপও চালানো যায়।

>>> items = (1, 2, 5.5, ["a", "b", "c"], ("apple", "mango"))
>>> for item in items:
...     print(item, type(item))
... 
1 <class 'int'>
2 <class 'int'>
5.5 <class 'float'>
['a', 'b', 'c'] <class 'list'>
('apple', 'mango') <class 'tuple'>
>>> items[3]
['a', 'b', 'c']
>>> items[3][0]
'a'
>>> items[4][1]
'mango'
>>> items[4]
('apple', 'mango')

এখন অনেকের মনেই নিশ্চয়ই প্রশ্ন জাগছে, লিস্টের ভেতরেও কি লিস্ট রাখা যায়? হ্যাঁ, তবে আমি আর সেটি দেখালাম না, নিজে নিজে পরীক্ষা করে দেখতে হবে।

টাপলের উপাদানগুলো দিয়ে সহজেই আমরা লিস্ট তৈরি করতে পারি।

>>> tpl = (1, 2, 3)
>>> li = list(tpl)
>>> li
[1, 2, 3]
Published inUncategorized