検索
連載

[Python入門]モジュールの作り方Python入門(2/2 ページ)

Pythonでは複数の関数や変数などを「モジュール」にまとめることで、コードの再利用が可能になる。その作り方について見ていこう。

PC用表示 関連情報
Share
Tweet
LINE
Hatena
前のページへ |       

作成したモジュールを利用する

 上の手順に従って、作成したモジュールを利用できるようにしたら、後は前回に説明した方法でインポートできる。前回のおさらいを兼ねて、上で作成した関数を幾つか使ってみよう。

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'))

findメソッドは文字列の属性

 そのときの説明で、メソッドとは「オブジェクトが自分自身を対象として何らかの処理を行うための操作」と述べ、その書式が「オブジェクト.メソッド(引数)」となることも示した。ここでもドット「.」が使われているが、これは「そのメソッドがオブジェクトの属性」であることを示している。

 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文でモジュールをインポートする際には、「モジュール検索パス」を利用して、指定されたモジュールを検索する。多くの場合は、以下のようなフォルダが検索パスに含まれる。

  • Python処理系を起動したときのカレントフォルダ(あるいは、pythonコマンドに指定したスクリプトファイルがあるフォルダ)
  • 環境変数PYTHONPATHに記述されているフォルダ
  • インストールされているPythonに固有のフォルダ

 これらを調べるには、sysモジュールのpath変数(sys.path)に保存されている。これを本連載で使用しているJupyter NotebookのPython環境で表示してみると、次のようになる。

Jupyter NotebookのPython環境におけるモジュール検索パス
Jupyter NotebookのPython環境におけるモジュール検索パス

 この出力結果の1行目、右側にある「''」というのは「現在の作業フォルダ」(カレントフォルダ)を意味している。そこで、今度はosモジュールのgetcwd関数を使ってカレントフォルダを取得してみよう。

Jupyter NotebookのPython環境で現在の作業フォルダを調べているところ
Jupyter NotebookのPython環境で現在の作業フォルダを調べているところ

 すると、「'/home/jovyan/binder'」という値が得られた(これは環境によって異なるかもしれない)。実は、先ほどmyutil.pyファイルをアップロードした場所が、このフォルダになっていたということだ。

 myutilモジュールが使えるようになったのは、このような仕組みからだ。Jupyter Notebook上のPython環境を使う場合には、自作のモジュールはこのフォルダにアップロードするがよいだろう。

 そうではなく、何らかのテキストエディタなどを使ってプログラムを作成しているのであれば、モジュールをどこに配置しておくかが問題になることも将来的にはあるだろう。Pythonの学習目的であれば、プログラムを作成しているフォルダに自作のモジュールをコピーすれば済む。が、何度もファイルのコピーをするのが面倒になってきたら、自作モジュールを置くフォルダを決めて、それをモジュール検索パスに追加することになるだろう。

 詳しく解説はしないが、これを行う方法は幾つかある。

  • 環境変数PYTHONPATHを編集する
  • プログラムファイル内でsys.pathにモジュールを置いたフォルダを追加する
  • モジュール検索パスに記述されているフォルダに、パス設定ファイル(.pthファイル)を作成して、そのパス設定ファイルに自作のモジュールを置いたフォルダを記述する

 これらについては、機会があれば再度取り上げることにしよう。

まとめ

 今回はPythonのモジュールを作成して、利用する方法を見た。次回は複数のモジュールを束ねた「パッケージ」について見ていこう。

今回のまとめ:モジュールの作り方

  • モジュールとは「Pythonのコードを含んだ、拡張子がpyのテキストファイル」のこと
  • Jupyter Notebookでは、Pythonファイルとしてダウンロードすることで、セルに入力した内容をモジュールにできる
  • Jupyter Notebookでは、作成したモジュールをアップロードすることで、それをインポートできるようになる
  • モジュールで定義された関数や変数などは、そのモジュールの「属性」と呼ばれることがある
  • あるオブジェクトが持つ属性には「オブジェクト.属性」としてアクセスできる
  • 「公開したくない」関数の名前はアンダースコア「_」で始める
  • 「from モジュール名 import *」形式のインポートでは、それらの関数はインポートされない
  • アンダースコア「_」で始まる名前の関数や変数を定義しても、「from モジュール名 import *」以外の形式のインポートでそれらの関数を使用することを抑止はできない
  • モジュールをインポートする際には、モジュール検索パスを利用して、そのモジュールが検索される

「Python入門」のインデックス

Python入門

Copyright© Digital Advantage Corp. All Rights Reserved.

前のページへ |       
[an error occurred while processing this directive]
ページトップに戻る