上の手順に従って、作成したモジュールを利用できるようにしたら、後は前回に説明した方法でインポートできる。前回のおさらいを兼ねて、上で作成した関数を幾つか使ってみよう。
import myutil # myutilモジュールをインポート
from myutil import fib # myutilモジュールからfib関数をインポート
from myutil import fizzbuzz as fb # myutil.fizzbuzz関数をfbとしてインポート
print(myutil.PI) # myutilモジュールで定義されている変数PIを利用
for num in range(5):
print(myutil.fact(num)) # myutilモジュールのfact関数を利用
for num in range(5):
print(fib(num))
for num in range(1, 6):
print(fb(num))
最初のimport文では、myutilモジュールをインポートしている。前回にも述べたが、この方法でモジュールをインポートしたときには、「モジュール名.識別子」という形でそのモジュールで定義されている関数や変数などを利用できる。後続のコードで「myutil.PI」や「myutil.fact(5)」のようにしているのは、この使い方の例だ。
2つ目のimport文はmyutilモジュールからfib関数をインポートするものだ。この方法でインポートした場合には、「fib」という名前(識別子)をそのままコード中に書けるのも前回に述べた通りだ。
3つ目のimport文はmyutilモジュールからfizzbuzz関数を「fb」という名前でインポートするものだ。これも前回に述べた通り、この方法でインポートをすると、「fizbuzz」という名前ではなく「fb」という名前でしかその関数にアクセスできない。
実行結果は以下の通りだ。
「モジュール名.識別子」の形式でモジュール内の関数などにアクセスする場合、「識別子」の部分のことを「モジュールの属性」と呼ぶことがある。ここでいう「属性」とは「何らかのオブジェクトが持つモノ」といった意味だ。本連載では、これまでにもドット「.」を使って何かにアクセスをしてことがあるが、覚えていらっしゃるだろうか。例えば、第6回「Pythonの文字列の操作」の「文字列のfind/rfindメソッド」では次のようなコードがあった。
sample_str = 'find, rfind, index, rindex'
print(sample_str.find('index'))
そのときの説明で、メソッドとは「オブジェクトが自分自身を対象として何らかの処理を行うための操作」と述べ、その書式が「オブジェクト.メソッド(引数)」となることも示した。ここでもドット「.」が使われているが、これは「そのメソッドがオブジェクトの属性」であることを示している。
Pythonでは、文字列や数値に限らず、関数やモジュールも「オブジェクト」であり、それらには個別にさまざまな属性が含まれている。そして、属性には「変数のように値を保持するもの」と「関数のように何かの計算処理を行うもの」の2種類がある。
取りあえずは「何かの何か」という表現をする場合、2つ目の「何か」は1つ目の「何か」の「属性」だと考えておこう。「myutilモジュールのfact関数」「文字列のfindメソッド」なら、「myutilモジュールの属性であるfact関数」「文字列の属性であるfindメソッド」と言い換えられる。
前回も述べたが、Pythonでは「from モジュール名 import *」とすることで、そのモジュールで定義されているもの全てをインポートできる。ただし、モジュールを作成するときには、そのモジュールの中だけで使用することを意図した関数を定義することもある。例えば、複数の関数で共通に利用する定型処理を別の関数に切り出したようなときがそうだ。このような「非公開」の(公開したくない)関数までインポートされるのは困るかもしれない。
そこで、Pythonではモジュールで定義する関数の名前をアンダースコア「_」で始めておくと、「from モジュール名 import *」形式でインポートを行う際に、それらの関数はインポートしないという仕組みが用意されている。以下に例を示す。
def _myhelper(x):
return x * 2
def myfunc1(x):
result = _myhelper(x)
return result
def myfunc2(x):
result = _myhelper(x) * 2
return result
サンプルなので、簡単なコードとなっているが、上のコードでは_myhelper関数はmyfunc1関数とmyfunc2関数から共通に呼び出されるようになっている。これはモジュールの内部でのみ使用されることを意図したものであり、アンダースコア「_」で名前が始まっていることに注意しよう。このモジュールを「somemod.py」という名前で保存して、上で説明した手順でPython環境にアップロードしたとする。
このモジュールから「from somemod import *」形式で「全て」をインポートして、関数を呼び出してみよう。
from somemod import *
print(myfunc1(2))
print(myfunc2(2))
print(_myhelper(2))
実行結果を以下に示す。
上の画像を見ると分かる通り、_myhelper関数を呼び出そうとしたらエラー(NameError例外)が発生した。このように、モジュール内でアンダースコア「_」で始まる名前の関数を定義すると、「from モジュール名 import *」形式のインポートではそれらが無視される。ここでは関数を例に取ったが他のものでも同じだ。
注意が必要なのは、これはあくまでも「from モジュール名 import *」形式でインポートするときにのみ有効なものである点だ。「import somemod」としてsomemodモジュールをインポートして「somemod._myhelper(2)」のようにヘルパー関数を呼び出したり、「from somemod import _myhelper」のように_myhelper関数を明示的にインポートしたりすることは可能だ。例えば、次のコードは問題なく実行できる。
import somemod
print(somemod._myhelper(2))
これを実行すると、次のようになる。上とは異なりエラーが発生しないことに注目しよう。
なお、「from モジュール名 import *」形式のインポートにおいて、何がインポートされるかを明示的に指定する方法として「__all__」変数に「公開してもよいもの」を列挙する方法もあるが、これについてはパッケージの説明をする際に取り上げよう。
今見たように、名前をアンダースコア「_」で始めることで「非公開」(プライベート)であることを示すのは、Pythonでは共通の作法となっている。例えば、まだ説明をしていないが、オブジェクトの属性を非公開とするには、単独のアンダースコア「_」を使う方法と、2つのアンダースコア「__」を使う方法があり、後者の方がより強力なものとなっている。
だが、アンダースコア「_」を使ってモジュールで定義されている関数が非公開であることを示しても、それはあくまでも紳士協定のようなものであって、強制力はないことには注意しよう。
先ほどは、自作したモジュールをJupyter NotebookのPython環境にアップロードしたり、コマンドプロンプトやシェルからはモジュールファイル(myutil.pyファイル)があるフォルダから「python」コマンドを実行したりすることで、そのモジュールを使えるようにしていた。
Pythonはimport文でモジュールをインポートする際には、「モジュール検索パス」を利用して、指定されたモジュールを検索する。多くの場合は、以下のようなフォルダが検索パスに含まれる。
これらを調べるには、sysモジュールのpath変数(sys.path)に保存されている。これを本連載で使用しているJupyter NotebookのPython環境で表示してみると、次のようになる。
この出力結果の1行目、右側にある「''」というのは「現在の作業フォルダ」(カレントフォルダ)を意味している。そこで、今度はosモジュールのgetcwd関数を使ってカレントフォルダを取得してみよう。
すると、「'/home/jovyan/binder'」という値が得られた(これは環境によって異なるかもしれない)。実は、先ほどmyutil.pyファイルをアップロードした場所が、このフォルダになっていたということだ。
myutilモジュールが使えるようになったのは、このような仕組みからだ。Jupyter Notebook上のPython環境を使う場合には、自作のモジュールはこのフォルダにアップロードするがよいだろう。
そうではなく、何らかのテキストエディタなどを使ってプログラムを作成しているのであれば、モジュールをどこに配置しておくかが問題になることも将来的にはあるだろう。Pythonの学習目的であれば、プログラムを作成しているフォルダに自作のモジュールをコピーすれば済む。が、何度もファイルのコピーをするのが面倒になってきたら、自作モジュールを置くフォルダを決めて、それをモジュール検索パスに追加することになるだろう。
詳しく解説はしないが、これを行う方法は幾つかある。
これらについては、機会があれば再度取り上げることにしよう。
今回はPythonのモジュールを作成して、利用する方法を見た。次回は複数のモジュールを束ねた「パッケージ」について見ていこう。
「Python入門」
Copyright© Digital Advantage Corp. All Rights Reserved.