Skip to content

মডিউল ও প্যাকেজ

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

আমরা যে বিভিন্ন প্রোগ্রামে print(), input(), type(), sum() ইত্যাদি ফাংশন ব্যবহার করেছি, সেগুলো কিন্তু আমরা নিজেরা তৈরি করি নি, ব্যবহার করেছি কেবল। এগুলোকে বিল্টইন (builtin) ফাংশন বলে। বিল্টইন শব্দের অর্থ হচ্ছে, যেগুলো কোনোকিছুর সঙ্গে দেওয়া থাকে। যেমন, টেলিভিশন অন-অফ করার জন্য বিল্টইন সুইচ দেওয়া থাকে। ওই ফাংশনগুলো তৈরি করা না থাকলে আমাদের নিজেদের সেগুলো তৈরি করা লাগত এবং প্রতিবার আমাদের বেশ খানিকটা সময় ব্যায় হতো। ফাংশনগুলো ইতিমধ্যে তৈরি করা আছে builtins নামক মডিউলে। পাইথন ইন্টারপ্রেটার চালু করলে এই মডিউল আপনাআপনি চলে আসে, নইলে আমাদেরকে আলাদাভাবে ইমপোর্ট (import) করতে হতো। কারো যদি জানতে ইচ্ছে করে যে, এই মডিউলে কী কী আছে, তাহলে সেটি জানার জন্য পাইথন ইন্টারপ্রেটার চালু করে নিচের দুই লাইন লিখতে হবে :

>>> import builtins
>>> dir(builtins)

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

স্ট্যান্ডার্ড লাইব্রেরি (Standard Library)

বিল্টইন ফাংশন ছাড়াও কিন্তু আমরা বিভিন্ন মডিউল ইমপোর্ট করে ব্যবহার করেছি। যেমন, math মডিউল ইমপোর্ট করেছি মৌলিক সংখ্যা বের করার প্রোগ্রাম লেখার সময়। এজন্য আমাদের লিখতে হয়েছে, import math। এই মডিউলটি হচ্ছে পাইথনের স্ট্যান্ডার্ড লাইব্রেরির একটি মডিউল। এখানে বিভিন্ন গাণিতিক ফাংশন তৈরি করে দেওয়া আছে, সেই সঙ্গে কিছু গাণিতিক ধ্রুবক (constant)-ও সংজ্ঞায়িত করে দেওয়া আছে। কিছু উদাহরণ দেখি :

>>> import math
>>> math.pi
3.141592653589793
>>> math.pow(2, 3)
8.0
>>> math.pow(3, 2)
9.0
>>> math.sqrt(25)
5.0
>>> math.floor(5.2)
5
>>> math.ceil(5.2)
6

math মডিউলের কী কী ফাংশন আছে এবং সেগুলোর কাজ কী, তা পুরোপুরিভাবে জানতে হলে ডকুমেন্টেশন দেখতে হবে। পাইথনের ওয়েবসাইটে ডকুমেন্টেশন দেওয়া আছে (https://docs.python.org/3/library/math.html\) আবার চাইলে সেটি ডাউনলোডও করে ফেলা যায়। math ছাড়াও আমরা ইতিপূর্বে পাইথন দিয়ে প্রোগ্রামিং শেখা বইতে turtle, random, timeit ইত্যাদি মডিউল ব্যবহার করেছি। আরো কয়েকটি উদাহরণ দেখাই।

দিন-তারিখ নিয়ে কাজ-কর্ম করার জন্য আছে datetime মডিউল। এই মডিউলের মধ্যে আবার বিভিন্ন ক্লাস আছে। আমরা সেই ক্লাসের বিভিন্ন মেথড ব্যবহার করতে পারি। এখন আমরা যদি আজকের তারিখ জানতে চাই, তাহলে datetime মডিউলের date ক্লাসের today() মেথডকে কল করতে হবে।

>>> import datetime
>>> today = datetime.date.today()
>>> print(today)
2017-06-10

আবার আমরা যদি আজকের তারিখ ও বর্তমান সময় জানতে চাই, তখন datetime মডিউলের datetime ক্লাসের today() মেথড কল করতে হবে।

>>> import datetime
>>> today = datetime.datetime.today()
>>> print(today)
2017-06-10 15:51:28.951772

ওপরের দুটি উদাহরণে আমরা দেখলাম প্রথমে মডিউলের নাম, তারপরে একটি ডট, তারপরে ক্লাসের নাম, তারপরে একটি ডট ও সবশেষে ফাংশনের নাম লিখে আমরা ফাংশনটি কল করছি। আবার মডিউলটি যদি কোনো প্যাকেজের মধ্যে থাকত, তাহলে প্রথমে সেই প্যাকেজের নাম লিখে ডট দিতে হতো এবং তারপরে মডিউলের নাম, ক্লাসের নাম, ফাংশনের নাম ইত্যাদি লিখতে হতো। অর্থাৎ, package_name.module_name.function_name() আর যদি মডিউলে কোনো ক্লাসের মেথডকে কল করি, তাহলে package_name.module_name.class_name.function_name()।

আগের উদাহরণে আমরা চাইলে datetime মডিউল থেকে কেবল datetime ক্লাসটি ইমপোর্ট করতে পারতাম। তখন datetime.today() লিখলেই কাজ হয়ে যাবে।

>>> from datetime import datetime
>>> d = datetime.today()
>>> print(d)
2017-06-10 17:08:56.428959

আমরা যদি আমাদের প্রোগ্রাম থেকে কোনো ওয়েবসাইট, ওয়েব ব্রাউজারের মাধ্যমে দেখাতে চাই, তার জন্যও ফাংশন তৈরি করে দেওয়া আছে webbrowser মডিউলে।

>>> import webbrowser
>>> url = "http://subeen.com"
>>> webbrowser.open(url)
True

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

>>> import webbrowser as wb
>>> url = "http://subeen.com"
>>> wb.open(url)
True

পাইথনের স্ট্যান্ডার্ড লাইব্রেরিতে কী কী মডিউল ও প্যাকেজ আছে, তা জানা যাবে এখানে : https://docs.python.org/3/library/index.html। অবসর সময়ে এগুলো নিয়ে পড়াশোনা করা যেতে পারে, তবে তাড়াহুড়ো নেই। আর সব লাইব্রেরির ব্যবহারও শেখার বা মুখস্থ করার দরকার নেই। সময়ের সঙ্গে প্রয়োজনমতো আমরা এমনিতেই শিখে নেবো।

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

নতুন মডিউল তৈরি করা

পাইথন দিয়ে প্রোগ্রামিং শেখা বইয়ের শেষ উদাহরণে আমরা দেখেছিলাম, কিভাবে ফিবোনাচ্চি সংখ্যা বের করতে হয়। n-তম ফিবোনাচ্চি সংখ্যা বের করার জন্য আমরা এখন একটি ফাংশন লিখে ফেলি।

def find_fib(n):
    if n <= 2:
        return 1
    fib_x, fib_next = 1, 1

    i = 3
    while i <= n:
        i += 1
        fib_x, fib_next = fib_next, fib_x + fib_next

    return fib_next

for x in range(1, 11):
    print(find_fib(x))

আমরা find_fib() নামে একটি ফাংশন তৈরি করলাম, যেটির প্যারামিটার হচ্ছে n এবং ফাংশনটি n-তম ফিবোনাচ্চি সংখ্যা রিটার্ন করে। তারপর একটি লুপ চালিয়ে আমরা ওই ফাংশন কল করে প্রথম দশটি ফিবোনাচ্চি সংখ্যা প্রিন্ট করলাম। আউটপুট আসবে নিচের মতো – প্রথম দশটি ফিবোনাচ্চি সংখ্যা :

$ python fibo.py 
1
1
2
3
5
8
13
21
34
55

অনুশীলনী :

  • ওপরের প্রোগ্রামে return fib_next না লিখে return fib_x লিখলে কী সমস্যা হতো? এটি নিয়ে একটু চিন্তাভাবনা করতে হবে।
  • একটি ফাংশন লিখতে হবে, যার কাজ হচ্ছে প্যারামিটার হিসেবে n নেওয়া এবং প্রথম n-সংখ্যক ফিবোনাচ্চি সংখ্যার লিস্ট রিটার্ন করা। ফাংশনটির নাম যদি হয় list_fib(), তাহলে print(list_fib(10)) লিখলে আউটপুট আসবে :

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

list_fib() ফাংশনটিও আমরা fibo.py ফাইলে লিখবো। অধিকাংশ পাঠকেরই ফাংশনটি লিখতে ঘণ্টাখানেক চেষ্টা করতে হতে পারে, তাতে হতাশ হওয়ার কিছু নেই। প্রোগ্রামিং শেখার সময় যে যত বেশি চেষ্টা করবে, চিন্তাভাবনা করবে, কোড লিখবে, তার প্রোগ্রামিং শেখা তত ভালো হবে। এক-দুই ঘণ্টা চেষ্টা করেও list_fib() ফাংশনটি লেখতে না পারলে সামনে আগাতে হবে, আমি ফাংশনটি একটু পরেই লিখে দিয়েছি।

তাহলে আমার fibo.py ফাইলটি দাঁড়াচ্ছে এরকম :


def find_fib(n):
    if n <= 2:
        return 1

    fib_x, fib_next = 1, 1

    i = 3
    while i <= n:
        i += 1
        fib_x, fib_next = fib_next, fib_x + fib_next

    return fib_next


def list_fib(n):
    fib_list = [1, 1]
    if n <= 2:
        return fib_list[:n]

    fib_x, fib_next = 1, 1

    i = 3
    while i <= n:
        i += 1
        fib_x, fib_next = fib_next, fib_x + fib_next
        fib_list.append(fib_next)

    return fib_list


for x in range(1, 11):
    print(find_fib(x))

print(list_fib(1))
print(list_fib(2))
print(list_fib(10))

এবারে প্রথম কাজ হচ্ছে প্রোগ্রামটি নিজ নিজ কম্পিউটারে টাইপ করে রান করে দেখা। আউটপুট আসবে এরকম :

$ python fibo.py 
1
1
2
3
5
8
13
21
34
55
[1]
[1, 1]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

আমি ফাইলের শেষে প্রিন্টগুলো করে নিশ্চিত হওয়ার চেষ্টা করেছি যে, ফাংশনদুটি ঠিকঠাক কাজ করে। এখন আমরা fibo.py ফাইলটি যে ডিরেক্টরি বা ফোল্ডারে আছে, সেখানে আরেকটি ফাইল তৈরি করবো, program.py আর সেখানে নিচের মতো কোড লিখবো :

import fibo

print("Hello, I am inside program.py!")

n = fibo.find_fib(15)

print("15th fibonacci number is,", n)

এই প্রোগ্রামে আমি আসলে কী করেছি? প্রথমে fibo.py ফাইলটি ইমপোর্ট করেছি। ইমপোর্ট করার সময় ফাইলের এক্সটেনশন .py দেওয়ার প্রয়োজন নেই, তাই আমরা লিখেছি import fibo। এটি করার উদ্দেশ্য হচ্ছে fibo.py ফাইলের ভেতরে যেই ফাংশন দুটি তৈরি করেছি, সেগুলো যেন আমি ব্যবহার করতে পারি। fibo.py ফাইলে দুটি ফাংশন আছে – find_fib() ও list_fib()। ফাংশনদুটিকে কল করতে হলে আমাদের লিখতে হবে, যথাক্রমে fibo.find_fib() ও fibo.list_fib()। প্রোগ্রামের তৃতীয় লাইনে লিখেছি n = fibo.find_fib(15), অর্থাৎ আমি 15-তম ফিবোনাচ্চি সংখ্যা বের করে, সেই সংখ্যাটি n-এর মধ্যে রাখছি। তারপরের লাইনে সেটি প্রিন্ট করছি। এখন আমরা প্রোগ্রামটি রান করে দেখি, আউটপুট কী আসে?

$ python program.py 
1
1
2
3
5
8
13
21
34
55
[1]
[1, 1]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Hello, I am inside program.py!
15th fibonacci number is, 610

আমাদের প্রোগ্রামে দুটি লাইন প্রিন্ট করার কথা, সেগুলো ঠিকই প্রিন্ট হয়েছে। কিন্তু তার আগে আবার fibo.py প্রোগ্রামে যা যা প্রিন্ট করতে দিয়েছিলাম, সেগুলোও প্রিন্ট হয়েছে। কেন এমন হলো? একটু তদন্ত করে দেখা যাক। আমরা এখন আমাদের program.py ফাইলে, শেষ দুটি স্টেটমেন্ট বাদ দিয়ে দেবো। ফাইলটি হবে এরকম :

import fibo

print("Hello, I am inside program.py!")

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

$ python program.py 
1
1
2
3
5
8
13
21
34
55
[1]
[1, 1]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Hello, I am inside program.py!

তাহলে আমরা বুঝতে পারছি, যখনই আমরা import fibo লিখছি, পাইথন করছে কী, fibo.py ফাইলে রান করছে, তাই ওই ফাইলের শেষে আমি যেই প্রিন্ট স্টেটমেন্টগুলো লিখেছিলাম, সেগুলোর আউটপুটও আমরা দেখতে পাচ্ছি।

এখানে fibo.py ফাইলটিকে বলা হয় মডিউল (module)। একটি পাইথন মডিউলে সাধারণত বিভিন্ন ভেরিয়েবল ও ফাংশন ডেফিনেশন থাকে। মডিউলের ভেতরে ক্লাসও থাকতে পারে (ক্লাস সম্পর্কে আমরা এই বইতে পরে জানবো)। মডিউলটি ইমপোর্ট করে তার ভেতরের জিনিসগুলো ব্যবহার করা যায়। একটু আগেই আমরা তা দেখেছি। কিন্তু একটি বিষয় আমাদের ভাবিয়ে তুলছে। আমরা চাই যে, fibo.py ফাইলটি যদি সরাসরি রান করি, তাহলে যেন তার ভেতরে ফাংশনগুলো কল করে যে প্রিন্টগুলো করেছিলাম, সেগুলো চলবে, কিন্তু অন্য ফাইল থেকে fibo-কে ইমপোর্ট করলে ওই স্টেটমেন্টগুলো চলবে না। এজন্য আমাদেরকে একটি গ্লোবাল (global) ভ্যারিয়েবলের সাহায্য নিতে হবে, যার নাম হচ্ছে, __name__। আর এই গ্লোবাল ভ্যারিয়েবলটি কিন্তু আমরা নিজেরা ডিক্লেয়ার করবো না, প্রোগ্রাম রান করার সময় পাইথন নিজে থেকেই এর মান ঠিক করে দেয়। এখন আমরা trial.py নামে একটি ফাইল তৈরি করে এক লাইন কোড লিখবো। fibo.py ও program.py ফাইলদুটি যে ডিরেক্টরিতে আছে, trial.py ফাইলটিও সেই ডিরেক্টরিতে রাখতে হবে।

print("My name is", __name__)

এখন প্রোগ্রামটি রান করি :

$ python trial.py 
My name is __main__

মডিউলের নাম তো হওয়ার কথা ছিল trial, কিন্তু আউটপুটে দেখছি __main__। এর কারণ হচ্ছে আমি যখন trial.py ফাইলটি সরাসরি রান করছি, তখন পাইথন তাকে কোনো মডিউল হিসেবে বিবেচনা করছে না, তাই তার নাম দেখাচ্ছে __main__। এখন আমি program.py ফাইলে trial মডিউল ইমপোর্ট করবো।

import trial

print("Hello, I am inside program.py!")
print(trial.__name__)

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

$ python program.py 
My name is trial
Hello, I am inside program.py!
trial

প্রথম লাইনে যখন import trial লিখেছি, তখন trial.py ফাইলের প্রিন্ট স্টেটমেন্ট রান করে আউটপুট দিয়েছে My name is trial। এখন কিন্তু সে তার মডিউলের নাম ঠিকঠাক প্রিন্ট করলো। কারণ অন্য কেউ তাকে ইমপোর্ট করেছে। তাহলে আমরা দেখতে পাচ্ছি যে বাইরে থেকে ইমপোর্ট করলে সঠিক নাম দেখায়, আর ফাইলটি সরাসরি চালালে নাম দেখায় __main__। এই ব্যাপারটি আমরা কাজে লাগিয়ে এমন ব্যবস্থা করতে পারি যেন trial.py সরাসরি রান করলে Hello from trial কথাটি প্রিন্ট হবে, কিন্তু বাইরে থেকে ইমপোর্ট করলে এটি প্রিন্ট হবে না। trial.py ফাইলের কোড হবে এরকম :

if __name__ == "__main__":
    print("Hello from trial")

প্রোগ্রামটি রান করি :

$ python trial.py 
Hello from trial

এবারে program.py ফাইলটি রান করি :

$ python program.py 
Hello, I am inside program.py!
trial

এখন কিন্তু আর trial.py ফাইলের print স্টেটমেন্টটি রান করছে না, কারণ if কন্ডিশনের ভেতরে __name__ -এর মান হচ্ছে trial, তাই শর্তটি মিথ্যা হয়ে যাচ্ছে।

অনুশীলনীঃ fibo.py ফাইলটি এমনভাবে পরিবর্তন করতে হবে, যেন ফাইলটি মডিউল আকারে ইমপোর্ট করলে ফাইলের শেষের প্রিন্টগুলো না দেখায়, কিন্তু fibo.py ফাইলটি সরাসরি রান করলে যেন প্রিন্টগুলো দেখায়।

আমরা এতক্ষণ সবগুলো ফাইল একই ডিরেক্টরিতে রেখে কাজ করেছি। তার কারণ কী? কোনো প্রোগ্রামে যখন কোনো মডিউল ইমপোর্ট করা হয়, তখন পাইথন কিভাবে জানবে যে, মডিউলের কোড কোন জায়গায় আছে? যখন কোনো মডিউল ইমপোর্ট করা হয় তখন পাইথন প্রথমে খুঁজে দেখে ওই নামে কোনো বিল্ট-ইন মডিউল আছে কি না। ওই নামে কোনো বিল্ট-ইন মডিউল না থাকলে তার সার্চ পাথের সবগুলো ডিরেক্টরি ও তাদের সাব-ডিরেক্টরির মধ্যে একে একে খুঁজতে থাকে যে, সেই মডিউলটি পাওয়া যায় কি না। সার্চ পাথ (search path) হচ্ছে মূলত অনেকগুলো ডিরেক্টরির একটি তালিকা বা লিস্ট। সার্চ পাথের তালিকার প্রথম এলিমেন্ট বা উপাদান হচ্ছে ”, যেটা বোঝায় বর্তমান ডিরেক্টরি (কারেন্ট ওয়ার্কিং ডিরেক্টরি – current working directory বা cwd), যেখানে আমরা আমাদের প্রোগ্রামটি চালাচ্ছি। সার্চ পাথের সবগুলো ডিরেক্টরির লিস্ট দেখতে চাইলে আমাদেরকে নিচের কোড ব্যবহার করতে হবে।

>>> import sys
>>> sys.path
>>> # এখানে একটি ডিরেক্টরির লিস্ট আসবে

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

নোট: একটি মডিউল যদি বেশ বড়সড় হয়ে যায়, তখন তাকে আবার বিভিন্ন আলাদা ফাইলে ভেঙ্গে একটি ডিরেক্টরির মধ্যে রেখে একটি প্যাকেজ তৈরী করা যায়। একটি মডিউলে যেমন, একাধিক ভ্যারিয়েবল, ফাংশন, ক্লাস ইত্যাদি থাকতে পারে; তেমনি, একটি প্যাকেজের মধ্যে একাধিক ফাইল বা মডিউল, সাব-ডিরেক্টরি (যাদেরকে সাব-প্যাকেজও বলা হয়) থাকতে পারে। প্যাকেজের মূল ডিরেক্টরির মধ্যে __init__.py নামে একটি ফাইল তৈরী করে তার মধ্যে বলে দিতে হয়, এই প্যাকেজের মধ্যে কী কী সাব-প্যাকেজ ও মডিউল রয়েছে।

Published inUncategorized