Python基礎講座 #20 モジュールとパッケージ 第2回 (Module and Package – vol.2)
前回の講座でモジュールの使い方について見てきましたが、今回の講座ではパッケージについて勉強していきましょう。 前回の講座を読んでいない方は、下のリンクをクリックして必ず前回の講座を先に読んでから、この講座をお読みください。
Python基礎講座 #20 モジュールとパッケージ 第1回 (Module and Package – vol.1)
モジュールが似た機能を持つ関数たちを一つのファイルに集めたものだとすれば、パッケージは似た機能を持つモジュールたちを一つのフォルダの中に集めたものだと考えてください。Pythonにおけるパッケージは、ドット(.)ノーテーションを使ってモジュールをコンピュータのフォルダのように階層化する方法です。Windowsではバックスラッシュ()を使ってフォルダのパスを表示し、Linuxではスラッシュ(/)を使うように、Pythonではパッケージのパスをドット記号を使って表記します。
パッケージを使う最も重要な理由は、プログラムを配布するためですが、自分が作ったモジュールたちを他の人にあげる時、ただ複数のPythonファイルを渡すよりは、フォルダに入れてきれいに名前を付けて渡せば、他のパッケージに同じ名前のモジュールがあっても、同じ名前を持つモジュール同士の衝突を避けることができます。モジュールを使う時、モジュール名の後にドットを付けてモジュール内の関数や変数にアクセスするように、パッケージも同じく、パッケージ名の後にドットを付けて、その後に使いたいモジュール名を付けてモジュールにアクセスします。ユーザーがインストールするパッケージは、Lib フォルダの下の site packges というフォルダの中にインストールされます。そしてインストールされるパッケージは、パッケージ名の付いたフォルダの中に保存されます。
pip コマンドで numpy パッケージをインストールしてみましょう。numpy というパッケージがフォルダとして作成されるのが分かります。最上位フォルダであるパッケージフォルダの中には、サブフォルダを作って機能ごとにモジュールを分類して保管することができますが、これらのサブフォルダをサブパッケージと呼びます。
それでは今から簡単なパッケージを作ってみましょう。前回の講座と似たゲームプログラムを作るために、次のように攻撃機能を持つ関数たちを集めて attack.py というモジュールを作りました。
def arrow():
print('[マジック: 矢] [ダメージ: 30]')
def fireball():
print('[マジック: ファイアボール] [ダメージ: 50]')
def goblin():
print('[ユニット: ゴブリン] [ダメージ: 20]')
def giant():
print('[ユニット: ジャイアント] [ダメージ: 60]')まずgameというパッケージを作ってattackモジュールをその中に含めましょう。PyCharmでパッケージを作ると、自動的にパッケージフォルダとともに __init__.py というファイルが作成されるのが分かります。Pythonインタプリタはフォルダ内の __init__.py ファイルを探して、このフォルダが一般のフォルダかパッケージフォルダかを区別します。Pythonバージョン3.3以上からは、パッケージを作る時に __init__.py ファイルがなくてもパッケージを作成することができますが、__init__.py ファイルはパッケージを初期化(イニシャライジング)する時によく使われるため、ほとんどのパッケージに __init__.py ファイルがあるのを見ることができるでしょう。もしゲームのattack機能がだんだん増えてくるとモジュールも大きくなるので、攻撃機能を種類別に分類して別のモジュールに細分化しなければなりません。こういう場合に分類されたモジュールたちをまた、サブパッケージというフォルダの中に集めて分類します。
attack機能を種類別に magic と unit というモジュールに分けてみましょう。
def arrow():
print('[マジック: 矢] [ダメージ: 30]')
def fireball():
print('[マジック: ファイアボール] [ダメージ: 50]')def goblin():
print('[ユニット: ゴブリン] [ダメージ: 20]')
def giant():
print('[ユニット: ジャイアント] [ダメージ: 60]')普通ゲームには攻撃機能だけがあるのではなく、防御機能もあるので、防御機能を持つサブパッケージを作りましょう。もちろんdefenseサブパッケージにいろいろなモジュールが入らなければなりませんが、時間の都合上defenseパッケージのモジュールは作成しません。
今からユーザーがgameというパッケージを自分のプログラムにインポートして使う例をお見せします。ユーザーはエントリーポイントとなるPythonファイルを作って、そのファイル内でgameパッケージをインポートして使うことになるでしょう。パッケージ内のモジュールをインポートするには、先ほど説明した通り、パッケージ.サブパッケージ.モジュールの形式を使えば大丈夫です。
import game.attack.magic
game.attack.magic.arrow()[マジック: 矢] [ダメージ: 30]しかし、このようにインポートすると、毎回 magic モジュールを使うためにパッケージ名から入力しなければならないので、次のように as キーワードを使うか、from キーワードを使う方が良いです。
# as キーワード使用
import game.attack.magic as magic
magic.arrow()# from キーワード使用
from game.attack import magic
magic.arrow()パッケージがさらに複雑になると、開発者はユーザーがパッケージ内のモジュールをより便利にアクセスできるように、__init__.py ファイルで自動的にインポートするようにしてあげます。パッケージをインポートすると __init__.py ファイルは自動的に実行されます。gameパッケージの __init__.py ファイルにコードを追加して実行してみましょう。gameパッケージをインポートすると __init__.py ファイル内のprint関数が出力されたことが確認できました。__init__.py ファイルに次のようなインポート構文を入れておくと、モジュールが自動的にgame名前空間にロードされ、ユーザーはattackサブパッケージ名を使わずに、gameパッケージ名を通じてmagicモジュールとunitモジュールにアクセスできるようになります。
from .attack import magic, unitfrom game import magic, unit
magic.arrow()
unit.giant()[マジック: 矢] [ダメージ: 30]
[ユニット: ジャイアント] [ダメージ: 60]関数名だけですぐに関数にアクセスできるよう、次のようなコードを使うこともできます。
from .attack.magic import *
from .attack.unit import *from game import *
arrow()
giant()[マジック: 矢] [ダメージ: 30]
[ユニット: ジャイアント] [ダメージ: 60]このように作られたパッケージは、ユーザーに配布するために setuptools などのライブラリを使ってパッケージングをします。setuptools の使用は初級過程では少し難しいので、基礎過程ではここまで理解していただければ十分だと思います。setuptools の使い方については、上級過程で詳しく扱うことにします。
これまでPython基礎講座シリーズをご愛読いただき、誠にありがとうございました。🚀