মডিউল এবং প্যাকেজ

মডিউল এবং প্যাকেজ #

মডিউল #

0 1 1 2 3 5 8 13 21 34 55 89 144 233 – এই ধারার সংখ্যাগুলোর মাঝে একটা চমৎকার ব্যাপার আছে। ব্যাপারটা হলো এই ধারার প্রতিটি সংখ্যা এর আগের দুটি সংখ্যার যোগফল। ইতালিয়ান গণিতবিদ লিওনার্দো ফিবোনাচি এটি প্রকাশ করেন। তার নামানুসারে এই ধারাকে বলা হয় ফিবোনাচি ধারা। সাধারণত ০ ও ১ অথবা ১ ও ১ দিয়ে ফিবোনাচি ধারা শুরু হয়।

যাহোক, আমরা এখন কোনো একটা নির্দিষ্ট ডিরেক্টরিতে বা ফোল্ডারে fibo.py নামে একটা ফাইল তৈরি করব। এবার সেখানে ফিবোনাচি ধারা বের করার কোড লিখব।

def fib(n):
    series = []
    a, b = 0, 1
    while b < n:
        series.append(b)
        a, b = b, a+b
    return series

if __name__ == "__main__":
    temp = fib(100)
    print(temp)

আউটপুট

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

এবার আমরা আমাদের পুরনো সেই test.py তে ফিরে যাই। সেখানে আমরা একটা ফিবোনাচি সিরিজের লিস্টকে for লুপ দিয়ে ইটারেট করে প্রিন্ট করব।

import fibo

series = fibo.fib(100)
for item in series:
    print(item)

আউটপুট

1
1
2
3
5
8
13
21
34
55
89

ম্যাজিকটা খেয়াল করলাম সবাই? fibo.py তে লেখা fib() ফাংশনটাকেই আবার ব্যবহার করেছি আমরা। এই যে ফাংশনটাকে আবার না লিখে বা কপি না করেই ব্যবহার করলাম এটাই হল মডিউলের সবচেয়ে বড় সুবিধা। মডিউল হল লজিকালি কোড অর্গানাইজ করার উপায়। পাইথনিক ভাষায়, মডিউল হল একটা ফাইল যেখানে পাইথন ডেফিনিশন ও স্টেটমেন্ট থাকে। .py অংশ বাদ দিলে এই ফাইলই হচ্ছে মডিউলের নাম। import স্টেটমেন্টের মাধ্যমে আমরা একটা মডিউলকে সোর্স ফাইলে আমদানি করতে পারি, সোজা কথায় যোগ করতে পারি। আর মডিউলের নামের পর ডট . চিহ্ন দিয়ে আমরা এর বিভিন্ন অ্যাট্রিবিউটকে ব্যবহার বা অ্যাক্সেস করতে পারি।

আমরা চাইলে সরাসরি কোন মডিউল থেকে কোন নির্দিষ্ট অ্যাট্রিবিউটকে আমদানি করতে পারি। এজন্য আমাদের from ... import ... স্টেটমেন্ট ব্যবহার করতে হবে।

from fibo import fib

series = fib(100)
for item in series:
    print(item)

আউটপুট

1
1
2
3
5
8
13
21
34
55
89

একটা মডিউলের সবগুলো অ্যাট্রিবিউটকে একবারে আমদানি করার জন্য from ... import * স্টেটমেন্ট ব্যবহার করতে পারি আমরা।

from fibo import *

series = fib(100)
for item in series:
    print(item)

আউটপুট

1
1
2
3
5
8
13
21
34
55
89

এবার আসি if __name__ == "__main__" এর কাহিনিতে। যখন আমরা সরাসরি একটা স্ক্রিপ্ট রান করি কেবল ও কেবলমাত্র তখনই এর __name__, ‍"__main__" এর সমান হয়। আর এই ব্লকের কোড রান হবার সুযোগ পায়। পাইথনে প্রতিটি স্ক্রিপ্টই এক হিসাবে মডিউল। যখন একটা স্ক্রিপ্টে লেখা ক্লাস বা ফাংশনকে আমরা অন্য কোথাও আমদানি করে কাজ করতে যাব তখন ব্যাকগ্রাউন্ডে পুরো স্ক্রিপ্টটাই রান করবে ও আউটপুট তৈরি করবে। কিন্তু তা তো আমাদের দরকার নাই। এইজন্যই if __name__ == "__main__" ব্যবহার করা হয়। মডিউল আমদানি করার ক্ষেত্রে ঐ মডিউলের if __name__ == "__main__" এর অধীনে থাকা কোড রান হবে না। কারণ তখন __name__ আর "__main__" সমান হয় না। এজন্য পারফেক্ট ভাবে পাইথন স্ক্রিপ্ট লেখার উপায় হল:

def my_function():
    # do something

def your_function():
    # do something

def main():
    # call all functions here
    my_function()
    # play with them
    your_function()

if __name__ == "__main__":
    # now call main function
    main()

মডিউল সার্চ পাথ #

যখন আমরা fibo মডিউলটাকে আমদানি করেছি তখন পাইথন ইন্টারপ্রিটার fibo নামের একটা বিল্ট-ইন ফাংশনের খোঁজ করে প্রথমে। না পেলে, বিভিন্ন ডিরেক্টরিতে fibo.py নামের একটা ফাইলের খোঁজ করে। এই ডিরেক্টরিগুলোর লিস্ট sys.path ভ্যারিয়েবলে থাকে। এই ডিরেক্টরি সার্চিংয়ের ব্যাপারটাও নিয়ম মেনে হয়। রানিং স্ক্রিপ্টটা যে ডিরেক্টরিতে আছে ইন্টারপ্রিটার প্রথমে সেখানে খোঁজ করে। তারপর শেল ভ্যারিয়েবল PYTHONPATH এর ডিরেক্টরি লিস্টে সার্চ করে। আর সবার শেষে ইন্সটলেশন-ডিপেন্ডেন্ট ডিফল্ট পাথে সার্চ করে। তারপরও খুঁজে না পেলে ImportError এক্সেপশান থ্রো করে।

from omuk import tomuk

আউটপুট

Traceback (most recent call last):
  File "/home/ugcoder/Desktop/test.py", line 1, in <module>
    from omuk import tomuk
ImportError: No module named 'omuk'

স্টান্ডার্ড লাইব্রেরি #

ডিফল্টভাবেই পাইথনের সাথে অনেকগুলো মডিউল থাকে। এদেরকে স্টান্ডার্ড লাইব্রেরি বলে। কেউ কেউ অবশ্য স্টান্ডার্ড মডিউলও বলে। যেমন: time একটা স্টান্ডার্ড মডিউল। বিল্ট-ইন ফাংশন dir() দিয়ে আমরা এই মডিউলের সবগুলো ভ্যালিড অ্যাট্রিবিউট দেখতে পারি।

>>> import time
>>> dir(time)
['CLOCK_MONOTONIC', 'CLOCK_MONOTONIC_RAW', 'CLOCK_PROCESS_CPUTIME_ID', 'CLOCK_REALTIME', 'CLOCK_THREAD_CPUTIME_ID', '_STRUCT_TM_ITEMS', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'altzone', 'asctime', 'clock', 'clock_getres', 'clock_gettime', 'clock_settime', 'ctime', 'daylight', 'get_clock_info', 'gmtime', 'localtime', 'mktime', 'monotonic', 'perf_counter', 'process_time', 'sleep', 'strftime', 'strptime', 'struct_time', 'time', 'timezone', 'tzname', 'tzset']

এই ফাংশনের ভিতর কোন আর্গুমেন্ট পাস না করলে তা বর্তমান রানটাইমে আমাদের ডিফাইন করা সব ভ্যারিয়েবল, মডিউল, ফাংশনের লিস্ট রিটার্ন করবে।

>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'requests', 'sys', 'time']

প্যাকেজ #

প্যাকেজ হল মডিউল স্ট্রাকচার করার একটা উপায়। মডিউল, সাব-প্যাকেজ, সাব-সাব-প্যাকেজ ইত্যাদি নিয়ে একটা প্যাকেজ হয়। পাইথনের অফিসিয়াল ডক থেকে আমরা একটা উদাহরণ দেখব।

sound/                          Top-level package
      __init__.py               Initialize the sound package
      formats/                  Subpackage for file format conversions
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Subpackage for sound effects
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Subpackage for filters
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

এখানে আমরা sound প্যাকেজের বিভিন্ন সাব-প্যাকেজ ও মডিউল দেখতে পাচ্ছি। পাইথন যাতে ভিতরের ডিরেক্টরিগুলোকে সাব-প্যাকেজ মনে করে সেজন্য __init__.py ফাইল থাকা দরকার। __init__.py ফাইলে ইনিশিয়ালাইজেশন কোড থাকতে পারে। আবার খালি থাকলেও কোন সমস্যা নাই।

উপরের প্যাকেজ থেকে আমরা এখন echo মডিউলটা আমদানি করব। আর echo সাব-প্যাকেজের ফাংশন echofilter() ব্যবহার করব।

import sound.effects.echo

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

আরেকভাবে আমদানি করা যায়:

from sound.effects import echo

echo.echofilter(input, output, delay=0.7, atten=4)

উপায় কিন্তু আরো একটা আছে:

from sound.effects.echo import echofilter

echofilter(input, output, delay=0.7, atten=4)

একটা ব্যাপার জানা থাকা উচিত আমাদের। from package import item ব্যবহার করার ক্ষেত্রে item সাব-মডিউলও হতে পারে আবার সাব-প্যাকেজও হতে পারে অথবা প্যাকেজে ডিফাইন করা কোন ফাংশন, ক্লাস বা ভ্যারিয়েবল। আর import item.subitem.subsubitem ব্যবহার করার ক্ষেত্রে শেষেরটা বাদে প্রত্যেকটা আইটেমই প্যাকেজ হতে হবে। আর শেষের আইটেমটা মডিউল বা প্যাকেজ যেকোন কিছু হতে পারবে। কিন্তু আগের আইটেমে ডিফাইন করা ক্লাস, ফাংশন বা ভ্যারিয়েবল হতে পারবে না।

আচ্ছা, যদি আমরা from sound.effects import * এভাবে ইমপোর্ট করতে চাই?

যখন প্যাকেজের ভিতরের কোন ফাইলে সেই প্যাকেজের কোন মডিউল বা ফাংশনকে আমদানি করার দরকার পড়বে তখন নরমালি ইমপোর্ট করতে পারি। আবার রিলেটিভ ইমপোর্টও ব্যবহার করতে পারি। এই প্রক্রিয়ায় vocoder.py ফাইলে sound.effects প্যাকেজের echo মডিউলকে নিচের মত করে আমদানি করতে পারি।

from . import echo

বিষয়টা জটিল হয়ে গেল মনে হচ্ছে। আরেকটা নতুন প্যাকেজ স্ট্রাকচার বিবেচনা করি।

my_package/
my_package/__init__.py
my_package/main.py
my_package/name.py
my_package/school.py
my_package/college.py

এখানে main.py ফাইলে যদি আমরা school.py কে আমদানি করতে চাই অথবা school.py এর কোন ফাংশনকে আমদানি করতে চাই, তবে এভাবে করতে পারি:

from . import school
from .school import function1, function2

পাইথন প্যাকেজ ইনডেক্স (PyPI) #

PyPI (https://pypi.python.org/pypi/) হল পাইথনের প্যাকেজ রিপোজিটরি (সংক্ষেপে রেপো বলে)। বর্তমানে এখানে প্রায় ৯১২৫৯ টি প্যাকেজ রয়েছে। এই সবগুলো প্যাকেজই কিন্তু থার্ড-পার্টি প্যাকেজ। মানে পাইথনের মূল ডেভেলপাররা নয়, বরং আমাদের মত তৃতীয় পক্ষ এগুলো ডেভেলপ করে। এগুলো পাইথনের সাথে ডিফল্টভাবে আসে না। আলাদাভাবে ইন্সটল করে ব্যবহার করতে হয়। এই থার্ড-পার্টি প্যাকেজগুলোই পাইথনের মূল শক্তি। এরাই পাইথনকে করে তুলেছে অনন্য, করে তুলেছে আনবিটেবল।

কথা হল, কিভাবে এই প্যাকেজগুলি আমরা ইন্সটল করতে পারি? এজন্য আমাদের দরকার পাইথন প্যাকেজ ইন্সটলার pip। উবুন্টুতে পাইথন-৩ এর জন্য pip ইন্সটল করতে টার্মিনালে নিচের কমান্ড চালাতে পারি আমরা:

$ sudo apt-get install python3-pip

এবার ধরলাম, রেপো থেকে requests প্যাকেজটা ইন্সটল করব আমরা। এজন্য উবুন্টু টার্মিনালে নিচের কমান্ড চালাব আমরা:

$ sudo pip3 install requests

শুধু উবুন্টুর মত লিনাক্স ডিস্ট্রো নয়, উইন্ডোজ, ম্যাকওএস সব প্লাটফর্মই pip সাপোর্ট করে। আমাদেরকে শুধু আমাদের প্লাটফর্মের জন্য pip ও প্যাকেজ ইন্সটল করতে হবে। যাহোক, প্যাকেজ ইন্সটল হয়ে গেলে আমরা একে নরমাল প্যাকেজের মত করেই ব্যবহার করতে পারি।

>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.status_code
200

আর দিন শেষে দিনের সেরা ম্যাজিক। পাইথন শেলে কিছু লিখব আবার।

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

আমদানি-রফতানি করার আগে এই নিয়মগুলো একটু মাথায় রাখব আমরা। অবশ্য কনভেনশন চাপ্টারে আরো কিছু নিয়ম-কানুন শিখব।

আশা করি বুঝেছি সবাই। এ পর্যন্তই থাক তবে। আমরা এই চাপ্টারে লব্ধ জ্ঞান পরবর্তী চাপ্টারগুলোতে ব্যাপক পরিসরে প্রয়োগ করব। ততক্ষণ অবধি, হ্যাপি ইমপোর্টিং!

মন্তব্য করুন