読者です 読者をやめる 読者になる 読者になる

CUBE SUGAR CONTAINER

技術系のこと書きます。

Python: モジュールとパッケージ

Python Mac OS X

Python のモジュールとパッケージという概念についてまとめておく。

今回使った環境は次の通り。

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.11.3
BuildVersion:   15D21
$ python --version
Python 3.5.1

モジュールを作ってみる

Python におけるモジュールという概念は、ただの Python ファイル (*.py) にすぎない。 例えば helloworld.py という名前のファイルがあれば、それは Python インタプリタにとって helloworld モジュールになる。

実際に helloworld.py というファイルを用意して確認してみよう。 この中には greet() という関数を定義している。

$ cat << 'EOF' > helloworld.py
# -*- coding: utf-8 -*-


def greet():
    print('Hello, World!')

EOF

この状態では、カレントディレクトリに helloworld.py だけがある。

$ tree
.
└── helloworld.py

0 directories, 1 file

ちょっと脱線するけど tree コマンドは OS X に標準で入っていないのでほしいときは Homebrew で入れよう。

$ brew install tree

helloworld.py がカレントディレクトリにある状態で Python インタプリタを起動しよう。 そして helloworld モジュールをインポートする。 インポートしたモジュールの関数はもちろん使うことができる。

$ python
>>> import helloworld
>>> helloworld.greet()
Hello, World!

ばっちり。 helloworld.py が helloworld モジュールとしてインポートできた。

パッケージを作ってみる

次はパッケージを作ってみよう。 パッケージというのは、複数のモジュールを配下に持つことのできるモジュールをいう。 つまり、ファイルシステムでいえばモジュールがファイルだとしたらパッケージはディレクトリに相当する。

そしてモジュールがファイル (*.py) に対応していたように、パッケージは実際のところディレクトリで表現される。 パッケージというのは __init__.py という名前の Python ファイルの入ったディレクトリに過ぎない。

先ほど作った helloworld モジュールを helloworld パッケージに変えてみよう。 まずは helloworld というディレクトリを作る。 そして、先ほどの helloworld.py を __init__.py という名前に変えてディレクトリの中に移動させよう。

$ mkdir helloworld
$ mv helloworld.py helloworld/__init__.py

ここで __init__.py という Python ファイル (モジュール) は、パッケージ自身を表すモジュールになっている。 つまり、先ほどの greet() 関数は helloworld パッケージの直下に存在することになる。

これでカレントディレクトリの状態は次のようになった。 ちなみにキャッシュファイルはあると分かりにくくなるので削除している。

$ tree
.
└── helloworld
    └── __init__.py

1 directory, 1 file

先ほどと同じように Python インタプリタを起動して helloworld パッケージをインポートしてみよう。 とはいえ、この状態ではモジュールだったときと特に違いはない。

$ python
>>> import helloworld
>>> helloworld.greet()
Hello, World!

パッケージにモジュールを追加してみる

やはりパッケージなので配下にモジュールを追加してみないといけない。 今度は helloworld パッケージの中に spam モジュールを追加してみよう。 spam モジュールの中には ham() 関数を定義している。

$ cat << 'EOF' > helloworld/spam.py
# -*- coding: utf-8 -*-


def ham():
    print('Eggs')

EOF

これでカレントディレクトリの構成は次のようになった。

$ tree
.
└── helloworld
    ├── __init__.py
    └── spam.py

1 directory, 2 files

また Python インタプリタを起動して、今度は helloworld パッケージの spam モジュールをインポートしてみよう。 そして spam モジュールの ham() 関数を実行してみる。

$ python
>>> from helloworld import spam
>>> spam.ham()
Eggs

ばっちり。 helloworld パッケージの配下に spam モジュールが追加できた。

パッケージをインストールできるようにする

先ほど作ったモジュールやパッケージは、そのままでは使いづらい。 なぜならカレントディレクトリにあるか、あるいはどこか決まった場所に置いて PYTHONPATH を手動で通す必要があるためだ。

そんな手間はかけていられないので通常はインストールできるようにする。 これには Python の標準ライブラリにある distutils モジュールや、サードパーティ製ながらデファクトスタンダードになっている setuptools というパッケージを使う。

パッケージをインストールできるようにするにはセットアップスクリプトというファイルを用意する。 これは慣例として setup.py という名前をもった Python モジュールだ。

先ほど作った helloworld パッケージ用のセットアップスクリプトを用意しよう。

$ cat << 'EOF' > setup.py
# -*- coding: utf-8 -*-

from setuptools import setup
from setuptools import find_packages


def main():
    setup(
        name='helloworld',
        version='0.0.1',
        zip_safe=False,
        packages=find_packages(),
    )


if __name__ == '__main__':
    main()

EOF

カレントディレクトリの状態は次の通り。 helloworld パッケージがあるのと同じディレクトリにセットアップスクリプトがある。

$ tree
.
├── helloworld
│   ├── __init__.py
│   └── spam.py
└── setup.py

1 directory, 3 files

自作パッケージをインストールする

それでは、セットアップスクリプトを使って helloworld パッケージを実際にインストールしてみよう。 これにはセットアップスクリプトに install サブコマンドを渡して実行する。

$ python setup.py install

システムの Python 実行環境にインストールするときは適宜 sudo などをつける必要がある。 ただ、個人的には virtualenv などを使って仮想環境を分けるのがおすすめ。 今回もそうしている。

これで pip list サブコマンドから helloworld パッケージが見えるようになった。

$ pip list | grep helloworld
helloworld (0.0.1)

ちなみに pip 自体が入っていないときは次のようにしてインストールしておく必要がある。

$ curl -kL https://bootstrap.pypa.io/get-pip.py | sudo python

さて、インストールされた状態なら今いる場所に関わらずパッケージが使えるようになる。 試しに /tmp とかに移動した上で helloworld パッケージをインポートして使ってみよう。

$ cd /tmp
$ python
>>> import helloworld
>>> helloworld.greet()
Hello, World!

ばっちり。

まとめ

今回は Python のモジュールとパッケージの作り方、そしてパッケージをインストールできるようにする方法について書いた。 Python のモジュールはただの Python ファイル (*.py) に過ぎない。 そしてパッケージは __init__.py という名前のファイルが入っているディレクトリということを知っているのが重要となる。 そして、パッケージをインストールできるようにするには setup.py という名前のセットアップスクリプトを書けば良い。