フーノページ



Python、モジュール|パッケージ|ライブラリ

Pythonのモジュール/パッケージ/ライブラリとは?
〜やまかがし01。






モジュール :
名前空間を持ったオブジェクト(関数やデータの集まり)。
.pyファイル他、C拡張などが該当する。
            
パッケージ :
いくつかのモジュールをフォルダ構造(ディレクトリ化)としたもの。
まとめてインポートすることができる。
            
ライブラリ :
モジュールやパッケージを集めたものに対する、概念的・便宜的な呼び方。Pythonの言語仕様上の正式な部品ではない。
個別のモジュールを「ライブラリ」と呼ぶこともある。




Pythonの開発パターン。

こんにちは、「ふ」です。
今回はPythonにおける、モジュール/パッケージ/ライブラリとは何か?についてお話ししていきます。
この3つのワードは、Pythonを使っての開発手順を理解する上で外せないものです。

ただ「ふ」が調査したところ、文献によってその定義内容が少しずつ違っている部分もあるようです。
あれこれ検証したのち、どーにかこーにか「この解釈で間違いない」ところに落とし込むことができました。モジュール/パッケージ/ライブラリの概念について、しっくりときていない方。ぜひ参考にしてみてください。





モジュール。

モジュール :
名前空間を持ったオブジェクト(関数やデータの集まり)。
.pyファイル他、C拡張などが該当する。

モジュールとは何なのか?
厳密なところで言うと「単一の名前空間を持ったオブジェクト」ということになります。

 名前空間?
 オブジェクト

〜1つずつ探っていきましょう。

importの仕組み。

Pythonでは、他のファイルからその内容をインポートする(取り込む)ことができます。
今、1つのフォルダmy_project内にblue.pyとorange.py、2つのPythonファイルを置きました。

blue.py
aaa = 100

blue.pyの中で変数aaaを作り、値を「100」としています。

orange.py
import blue

次にorange.pyで「import blue」とすると、ドキュメント内にblue.pyの内容を取り入れることが出来ます。

orange.py
import blue
print(blue.aaa)
「「3>>> 100」」

orange.py内でprint関数を使い、blueのaaaを出力させます。
結果は「100」となります。ちゃんとblue.pyの内容が読み込まれている、ということです。

orange.py
import blue
print(blue.aaa)
「「3>>> 100」」

aaa = 50
print(aaa)
「「3>>> 50」」

さらに。
orange.pyの中で変数aaaを作り、値を「50」としました。
インポートしたblueの中にも変数aaaがあったので変数名が重複→エラーになるかと思いきや、ちゃんとプログラムは成立し、print関数でaaaを呼ぶと、ちゃんと「50」が出力されます。

独立した名前空間。

いま、importしたblue.pyにおける変数aaaと、orange.pyの中で純粋に作った変数aaaは別のものとして認識されていて、両方が成立しています。
これはorange.pyの中に「aaa」という同名の識別子があるにも関わらず、 blue.pyは、orange.pyに対して独立した「名前を認識・識別するルール」を持っていることになります。



この「独立した名前を認識・識別するルール」を、名前空間と呼びます。
blue.pyは、独自の名前空間を持っている、と言えます。
またorange.pyの内部で新たに作成した変数aaaが別の値とともに成立していることから、orange.pyも独自の名前空間を持っていることになります。

モジュールの定義を思い出してみます。

    名前空間を持つオブジェクト。

ここでいう「オブジェクト」とは、コンピュータ上での「情報と処理を一体化したデータのかたまり」のことです。「(あの難解でよくわからん)オブジェクト指向」のそれではありません。


1つのPythonファイルには情報にあたる変数や、処理にあたる関数がまとめられています。
なので、.pyファイルはPythonにおけるモジュールの1つです。「1つ」と表現したのはモジュールには他の形もあるからです。

Pythonをインストールした際、自分のファイルにimportできるプログラムファイルがたくさん付随してきます(後述する標準ライブラリと呼ばれるもの)。

それらは.pyファイルであったりC言語ベースで書かれたもの(⬆︎の.so拡張子ファイル。Winだと.pydとなる)もあります。
C言語ベースのファイルも名前空間を持っているため、変数や関数名で衝突することはありません。そしてその実体は「データと関数をまとめたもの」です。

この「C言語ベースのファイル」も、モジュールとしての条件を満たしています。


さらに。
Pythonプログラムの実行エンジンとなるインタプリタ内にも、名前空間で保持された「データと関数の集まり」が 記述されています。それらは「ビルトイン」とよばれるもので、モジュールの1つに分類されます。

「モジュール」に含まれるもの。

・Pythonファイル
・C拡張(C言語で記述されたものを.pyファイルからインポートできるようにコンパイルしたもの)
・Python実行エンジン内に記述されている、名前空間で囲まれたデータと処理の集まり

「モジュールとは?」の問いに対しては、⬆︎の3つが当てはまることになります。

公式PythonはC言語で記述されているのだそうです。処理速度の観点から、モジュールの中にはPython言語ではなく直接C言語で記述し、Pythonファイルからインポートできる形にコンパイルした「C拡張」が含まれます。先ほどの.so/.pyd拡張子のファイルがそれに当たります。





パッケージ。

パッケージ :
いくつかのモジュールをフォルダ構造(ディレクトリ化)としたもの。
まとめてインポートすることができる。

Pythonのパッケージとは、複数のモジュールをフォルダにまとめたもののことを指します。パッケージ自体は別に特別なものではなく、我々が普段ファイルをまとめている「フォルダ」に過ぎません。
パッケージにまとめたモジュールは、外部.pyファイルからまとめてimportすることができるのが特徴です。

ここでは実際に、簡単なパッケージを実装して確認してみましょう。

プロジェクトフォルダ「my_project2」直下に、

単体のファイルsoto.py と、
my_packageというフォルダをおきました。このフォルダをパッケージ として使ってみます。

my_packageの中には

__init__.py
hello.py
bye.py

の3つのPythonファイルを作っています。
このうちhello.pyとbye.pyが具体的なデータや処理が書かれたファイルで、__init__pyはmy_packageをPythonの「パッケージとして認識させる」ためのファイルです。パッケージ構築には、この__init.py__ファイルを含めることが必要です。

hello.py
def say_hello():
    print("hello~")

hello.pyには関数say_helloが入っています。実行すると「hello~」が出力されます。

bye.py
def say_bye():
    print("byebye")

一方でbye.pyには関数say_bye。こちらは実行すると「byebye」と出力されます。

__init__.py
import my_package.hello
import my_package.bye

__init.py__ではhello.pyとbye.py、2つのモジュールをインポートしておきました。
今回、__init__.pyの仕組みについては深く掘り下げません。あくまでパッケージの挙動だけを焦点に話を進めることとしますね。

そして。
外側にあるファイルであるsoto.pyの中で、my_packageをインポートします。

soto.py
import my_package
my_package.hello.say_hello()
「「3>>> hello~」」
my_package.bye.say_bye()
「「3>>> byebye」」

パッケージをインポートしたのち、hello.pyの関数say_helloを呼び出すとちゃんと実行されます。さらにbye.pyの関数say_byeを実行してもしっかり成立しました。
パッケージに含まれる全てのモジュールがインポートされていることがわかります。


Pythonにおいて、1つの.pyファイルだけではまともな成果物を作り上げることはできません。自作もしくは公開されているさまざまなモジュールをimportして(取り入れて)、開発を進める仕様になっているのです。

他人が命名したモジュールを利用することになる以上、変数や関数だけでなく、それらをまとめたモジュール名のレベルで名前の衝突も起こり得ます。


そこで複数のモジュールを集めてディレクトリ化し、名前空間で囲んでしまう(名前空間を階層化させる)という形式が採用されています。それがパッケージというものです。


みなさんも普段、画像や文書など自分のファイルをディレクトリで分類・整理していることかと思います。そのほうが断然管理がしやすいからですよね?
将来Pythonで大規模な開発をするとき、importによるモジュール同士の依存関係がどんどん複雑になってくることも考えられます。そのときにモジュールを階層化して、管理をしやすくする。パッケージにはそういった管理面のメリットも持ち合わせています。
さらにパッケージに関連のあるモジュールをまとめておくことで、部分的に別のプロジェクトに転用しやすくなる、という「再利用性の向上」にもつながります。

パッケージを使うことのメリット。


・関連モジュールをまとめてインポート可能。
・名前空間で囲まれるので、モジュール名の衝突を回避。
・モジュールをディレクトリ構造にすることで、整理や再利用がしやすくなる。



ライブラリ。

ライブラリ:
モジュールやパッケージを集めたものに対する、概念的・便宜的な呼び方。Pythonの言語仕様上の正式な部品ではない。
個別のモジュールを「ライブラリ」と呼ぶこともある。

では最後、ライブラリとは何か?についてです。


アプリ開発や機械学習、メールやExcelの効率化など、Pythonでできることはさまざまなものがあります。そのため開発プロジェクトごとに、必要なモジュール|パッケージを探してきて取り入れる必要があるのですが、数ある外部ソースの中からそれらを選定するのは非常に骨が折れることでしょう。



が、うれしいことにPythonでは目的別にモジュールやパッケージをまとめたセットが用意されていて、セットごとまとめてインストールすることができます。

この目的別にモジュールやパッケージを集めたものが、「ライブラリ」とよばれるものです。開発者は作りたいものに合ったライブラリをインストールし、利用することができます。

ライブラリには標準ライブラリと外部ライブラリの2つに大別されます。
ここでは主なものをいくつか紹介しておきます。

標準ライブラリ。

Pythonをインストールした際にも、多くのモジュールやパッケージが一緒にインストールされてきます。この元から含まれているモジュール|パッケージのセットは「標準ライブラリ」と呼ばれています。

標準ライブラリの中には多数のモジュール/パッケージがバインドされていますが、開発内容を問わずよく使うものを⬇︎にあげてみました。

OS・ファイル操作 → os、pathlib
CLI・設定 → argparse、configparser
データ保存・交換 → json、csv

テキスト処理 → re
時間管理 → datetime
デバッグ・テスト → logging、unittest

標準ライブラリを利用することである程度Pythonでできることの範囲は広がりますが、あくまで標準的な機能をサポートしてくれるものでしかありません。いわゆる「成果物」につなげるには、外部ライブラリの導入が必要となってきます。


外部ライブラリ。

目的に合わせて外部の配布元からインストールするのが「外部ライブラリ」と呼ばれるものです。⬇︎を見れば、より具体的な成果物に向けて用意されているのがわかるかと思います。

webアプリ開発   Flask|Django
機械学習    scikit-learn
メール・自動通知    smtplib

データ解析・可視化   pandas|matplotlib
webスクレイピング  BeauifulSoup|Selenium
Excel効率化・業務自動化  openpyxl|pandas

配布元の代表的なものがPython Package Index (PyPI)です。標準Pythonに同梱されているpip(パッケージマネージャー。外部モジュールなどをインストールするためのツール)を使って、必要なものを取得します。

厳格な呼び名ではない。

なおPythonにおける「モジュール」や「パッケージ」が、厳格に定義された名称であるのに対し、実は「ライブラリ」という呼び方は慣例的なものである、ということを知っておいてください。
ライブラリという名称は、モジュールやパッケージといったようなシステム上の部品を指すものではありません。単品の外部モジュールを「ライブラリ」と呼ぶ場合もあるそうです。



通称「ライブラリ」と呼ばれる「モジュールやパッケージのセット」をインストールしてきて、そのセットの中から目的のモジュールやパッケージを、自分の開発ファイルにインポートして利用する、というスタイルです。




なんでこんなことするの?

扨(さて)ここまでPythonのモジュール|パッケージ|ライブラリについて見てきました。
自分の.pyファイルに別のモジュールやパッケージを取り入れながら開発を進めていく〜その構成はお分かりいただけたかと思います。
が、何で..こんなにも..

面倒な手順を踏むのか?

と思いますよね? 「ふ」も、もちろん同じく疑問を抱きました。
ではこの、Pythonの開発パターンがどのような意図をもって設計されているのか、調べたところ。以下3つの理由が見つかりました。

・ゼロから開発しなくてよい
・全体の軽量化
・モジュールのシンプル化

ゼロから開発しなくてよい。

OSにやwebにアクセスしたりと難解な処理を、個人でゼロから開発するのは無理があります。なのでPythonではすでに公開されている便利なメソッド・スクリプトなどを取り入れながら開発を進めていける設計になっています。
ただ高度なものはもちろん単純な処理まで、外部モジュールへの依存範囲が大きいといった感じを受けます。たとえば.pyファイル単体では乱数のメソッドすら組み込まれていなかったりします。

プロジェクト全体の軽量化。

モジュール|パッケージの中から必要なものだけを選んで取り入れることができるおかげで、プロジェクト(開発に使うファイルの集まり)全体を軽量化することができます。
いずれ詳しくお伝えしますが、モジュールの中から必要なメソッドだけ取り入れるなど、事細かに選んでインポートすることもできます。

逆にライブラリ単位でインポートしたりすると、全体に無駄な負荷が掛かってしまいます。

モジュールのシンプル化。

1つのファイルを肥大化させず、小さいモジュールに独立→インポートで依存関係を構築する。それによって個々のモジュールのメンテナンスが楽になるというメリットがあります。細かく独立しているため、他のプロジェクトでモジュールを使い回したいときも楽になることでしょう。
さらにパッケージを採用してディレクトリ構造にすれば、自分や他の人が理解しやすいプロジェクトに仕上げることができます。

以上3つはいろんなソースから内容をまとめてみたものです(つまり受け売り)。モジュールやプロジェクトの構成を、「とにかくシンプルにする」という意向が伺えますね。

🔹「ふ」自身がちょっと思ったこと。
プログラミングに長けた人であれば自分で便利モジュールを作成することは可能でしょう。ただそこでは、モジュール名や関数などに対して独自の名前をつけることになります。
そして作成されたモジュールをインポートして、開発を進めたとします。 一連のコードを第三者が見たとき。開発者が命名した関数やモジュールを見て、「この部分は何をしているのか?」と認識するのに苦労することでしょう。

一方で(自作しようと思えばできるものの、あえて)十分に名の知れたライブラリを使用している場合。第三者がそのコードを参考にしようとしたとき、何をしているのかが理解しやすい、もしくはちょっと調べればすぐに解る、というメリットが考えられます。


Pythonにおける モジュールとライブラリの比重バランス。その恩恵を実感するのは、「ふ」やみなさんが、もう少し開発を進めていった段階となるのでしょうか..それとも他の言語も試した上で明確になってくるのか?
始まったばかり。まだまだ道は長そうだ。

しかし、フーノページやまかがし(←コーナー名(仮))では、Pythonを挫折・放置しないようにじっくりと学んでいくというコンセプト。今後も土台を固めながら、一緒に丁寧に進んでいきましょう。

最後までお読みくださり、ありがとうございました。

ではまた〜 🎵




「ふ」です。

ふ

ベクターグラフィック、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。