モジュール
pythonスクリプトのソースファイルひとつひとつがモジュールである。
【 目次 】
プログラムが大きくなってくるとひとつのソースファイルにすべてのロジックを入れてしまうとプログラムの把握が難しくなってくる。
そのため、スクリプトファイルを分割して管理する必要が出てくる。
いわゆるファイル分割である。
pythonスクリプトから別のソースファイルを読み込み利用することができる。
分割した他のソースファイル(モジュール)のコードを利用するにはimport文を使う。
importはPerlやPHPなどの他のスクリプト言語のrequireみたいなもんだけど、いろいろなバリエーションがありrequireと比べてそう単純なものじゃない。
import モジュール名 - 別のモジュールで定義されたものを呼び出す
import文を使うと他のモジュールを読み込んで利用する事ができる。
pythonインタープリタより最初に起動されるモジュールをメインモジュール,メインモジュールより呼び出される別モジュールをサブモジュールと呼ぶ事にする。
import文
import モジュール名
mainモジュールからsubモジュールの変数や関数,クラスを呼び出す例を示す。
まず、サブモジュールに変数,関数,クラスを定義
サブモジュール(sub.py)
#coding: UTF-8 str = u"subモジュールの変数" def func(): print u"subモジュールの関数funcを実行" class MyClass: def func(self): print u"subモジュールの関数MyClassのfuncメソッドを実行"
mainとsubに同じ名前のシンボルを定義してみる。
メインモジュールの変数などを定義した後、自身で定義されたものを呼び出しその後subのものを呼び出す。
#coding: UTF-8 str = u"mainモジュールの変数" def func(): print u"mainモジュールの関数funcを実行" class MyClass: def func(self): print u"mainモジュールの関数MyClassのfuncメソッドを実行" # ------------------------------- print u"自身(main)のモジュールのシンボルにアクセス" print u"str=",str func() MyClass().func() # ------------------------------- import sub print u"subモジュールのシンボルにアクセス" print "sub.str=",sub.str sub.func() sub.MyClass().func()
import文は通常、モジュールの先頭に記述するがこの例のように使う前であればどこに置いても良い
実行結果
自身(main)のモジュールのシンボルにアクセス str= mainモジュールの変数 mainモジュールの関数funcを実行 mainモジュールの関数MyClassのfuncメソッドを実行 subモジュールのシンボルにアクセス sub.str= subモジュールの変数 subモジュールの関数funcを実行 subモジュールの関数MyClassのfuncメソッドを実行
subモジュールで定義されたものを呼び出す時には先頭にモジュール名とドットを付けてsub.で呼び出せば良い。
これにより、メインモジュールとサブモジュールの同名のシンボルを区別して呼び出す事ができる。
モジュールのシンボルにアクセスするためのオブジェクト
import sub
は subモジュールのシンボルにアクセスするためのオブジェクト(モジュールオブジェクトと呼ぶ事にする)を sub という変数に代入しているだけ。
pythonソースファイルsub.pyで定義されたシンボル情報を保持するモジュールオブジェクトを生成して、sub という変数に代入していると考えられる。
前回の記事のシンボルの集合を返す組込み関数を使うとmainモジュールのグローバル名前空間にimport文によってsubモジュールの情報を保持するsub変数が追加されるのがわかる。
#coding: UTF-8 # 辞書の内容を列挙する関数 def print_dict(label,dict): print "*** %s ***" % label for key, value in dict.items(): print "\t%s=%s" % (key, value) print_dict(u"*** import前 ***",globals()) import sub print_dict(u"*** import後 ***",globals()) # subの要素を列挙 print "*** dir(sub) ***" for symbol in dir(sub): full_symbol = "sub." + symbol print "\t",full_symbol,"->", eval(full_symbol)
実行結果
*** *** import前 *** *** print_dict=<function print_dict at 0x02342430> __builtins__=<module '__builtin__' (built-in)> __file__=ソースファイルのパス __package__=None __name__=__main__ __doc__=None *** *** import後 *** *** print_dict=<function print_dict at 0x02342430> sub=<module 'sub' from 'sub.pycファイルのパス'> __builtins__=<module '__builtin__' (built-in)> __file__=ソースファイルのパス __package__=None __name__=__main__ __doc__=None *** dir(sub) *** sub.MyClass -> sub.MyClass sub.__builtins__ -> {'bytearray':, ... 途中略 sub.__doc__ -> None sub.__file__ -> sub.pycファイルのパス sub.__name__ -> sub sub.__package__ -> None sub.func -> <function func at 0x023424F0> sub.str -> subモジュールの変数
import後、subというシンボルが追加されている。
dir関数の出力結果より変数subを使ってアクセスできるsubモジュールのシンボルを知る事ができる。
importによって追加されるシンボルってなにものなのだろう
type関数を使ってimportによって追加されるシンボルの型を調べてみる。
print type(sub)
結果は
<type 'module'>
どうもsubはmodule型(moduleクラス)のオブジェクトらしい。
pycという拡張子
前記の実行結果のsub.pycという拡張子pycのファイルは、import時にpythonが生成するバイトコンパイル済ファイルらしい。
ソースファイルxxx.pyを実行すると、作成した覚えの無いxxx.pycというファイルが自動的に作成されるが特に気にする必要は無い。
xxx.pycはxxx.pyをコンパイルした結果できるファイルのようだ。
- 6. モジュール — Python 2.7ja1 documentation
「spam.py が見つかったディレクトリに spam.pyc という名前のファイルがあった場合には、 このファイルをモジュール spam の “バイトコンパイルされた” バージョンであると仮定します。 spam.py が無事コンパイルされると、常にコンパイルされたバージョンを spam.pyc へ書き出すよう試みます。 .pyc ファイルの内容はプラットフォームに依存しないので、 Python のモジュールのディレクトリは異なるアーキテクチャのマシン間で共有することができます。」
- とりあえず: [Python][お勉強] Python入門(35) - インポートの処理
Pythonにはいろいろな拡張子のファイルが使われている。
as - importしたシンボルに別名を付ける
asを使うとimportしたオブジェクトに対してソースファイル名と異なる変数名でアクセスする事ができる。
以下はasを使ってsubモジュールオブジェクトをanotherという変数に代入してsubモジュールオブジェクトのシンボルを呼び出す例である。
モジュールオブジェクトを別名で使う
import sub as another print another.str another.func() another.MyClass().func()
from モジュール名 import シンボル名 - 別のモジュールで定義されたものを自身で定義されているもののように呼び出す
前記のimportの例ではsubで定義されたものを呼び出す時には先頭にsub.を付けて呼び出す必要があった。
これをsub.を付けずに呼び出すには、別の変数に代入してしまえば良い。
別の変数に代入
import sub other_module_func = sub.func other_module_func()
もう一つ、sub.を付けずに呼び出す方法として
from モジュール名 import シンボル名
を使うと、シンボルの名前が重複していないのであればモジュール名.を付けずに呼び出す事ができる。
from モジュール名 import シンボル名
では長たらしいのでfrom付きのimportと呼ぶ事にする。
以下に例を示す。
別モジュール(other.py)
#coding: UTF-8 other_var=u"otherモジュールの変数" class OtherCls: def func(self): print u"otherモジュールのOther_clsのメソッド"
メインモジュールより別モジュール(other.py)の変数やクラスを使う
from other import other_var, OtherCls print other_var OtherCls().func()
from モジュール名 import * - アンダースコア (_) で始まる名前以外の全ての名前を import
from付きのimport文は便利なのだが、いちいち他のモジュールのシンボルを一個一個シンボル名を指定してimportするのは面倒くさいという場合には、「from モジュール名 import *」を使って アンダースコア (_) で始まる名前以外の全ての名前をimportする事ができる。
アンダースコア (_) で始まる名前
ところで、なぜ、アンダースコアで始まる名前以外となっているのかというと、pythonにはprivate変数が存在せず、アンダースコアで始まる名前はprivateな変数といて扱うというルールがあるため外部に公開すべきでないというのが理由のようだ。
from付きのimport文は使わない方が良い
「from モジュール import 名前1, 名前2」も「from モジュール import *」も便利なのだが使うべきでは無いとされている。
- from モジュール import */Python 良い慣用句、悪い慣用句 — Python 2.7ja1 documentation
- from モジュール import 名前1, 名前2/Python 良い慣用句、悪い慣用句 — Python 2.7ja1 documentation
- 10. 標準ライブラリミニツアー ― Python 2.7ja1 documentation
from os import * ではなく、 import os 形式を使うようにしてください。
importされるモジュールレベルのコードとモジュールの初期化と__name__
importされるモジュールレベルのコードはimport時に実行される。
以下にその例を示す。
importされるモジュールのコード
サブモジュール(sub.py)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #coding: UTF-8 print u"subモジュールのコードの実行を開始しました。" str = u"subモジュールの変数" def func(): print u"subモジュールの関数funcを実行" class MyClass: def func(self): print u"subモジュールの関数SsmClassのfuncメソッドを実行" print u"subモジュールのトップレベルのコードの実行を完了しました。" |
実行されるメインモジュールのコード
メインモジュール(main.py)
1 2 3 4 5 6 7 8 9 | #coding: UTF-8 print u"「import sub」の実行前です。" import sub print u"「import sub」の実行後です。" sub.func() |
実行結果
「import sub」の実行前です。 subモジュールのコードの実行を開始しました。 subモジュールのトップレベルのコードの実行を完了しました。 「import sub」の実行後です。 subモジュールの関数funcを実行
mainモジュールの3行目が実行された後5行目のimport文が実行されて、subモジュールのコードが読み込まれ、subモジュールのコードが上から順に実行されてsubモジュール3行目のコードが実行された後、subモジュールの5行目から12行目のsubモジュールのグローバル変数,関数,クラスが認識されメインモジュールのsub変数の指し示すモジュールオブジェクトの辞書(シンボルテーブル)に各シンボルが追加され後14行目のprint文が実行される。 次にメインモジュールに戻ってimport文の後のコードが実行される事になる。
サブモジュールの初期化
上記の性質を利用してサブモジュールの初期化のコードを記述する事ができる。
例えば
サブモジュール(sub.py)
#coding: UTF-8 a = u"subモジュールの初期化時の値です。" def _module_init_func(): # ここでsubモジュールを初期化するためのコードを記述する。 print u"subモジュールを初期化しました。" module_init_func()
メインモジュールは前のコードと同じとする。
実行結果
「import sub」の実行前です。 subモジュールのコードの実行を開始しました。 subモジュールのトップレベルのコードの実行を完了しました。 「import sub」の実行後です。 subモジュールの関数funcを実行
reload関数 - モジュールの再読み込み
モジュールの初期化を複数回実行させようとして同じモジュールを複数回importしてもモジュールの読み込みは1回しかおこなわれず、初期化処理も最初のimport時の1回しかおこなわれない。
しかしreload関数を使うと再読み込み処理を複数回おこなわせる事ができる。
以下はその動作を確認するためのコードである。
サブモジュール(sub.py)
#coding: UTF-8 print u"「import sub」の実行前です。" import sub print u"「import sub」の実行後です。" import sub print u"「import sub」の2回目実行後です。" reload(sub) reload(sub) print u"「reload(sub)」を2回目実行。"
importされるsubモジュールのコードは前と同じであるとする。
実行結果
「import sub」の実行前です。 subモジュールを初期化しました。 「import sub」の実行後です。 「import sub」の2回目実行後です。 subモジュールを初期化しました。 subモジュールを初期化しました。 「reload(sub)」を2回目実行。
1回目のimportの実行によりsubモジュールを初期化が実行されるが2回目のimportでは実行されていないのがわかる。
その後、reload関数を実行する事により再初期化がおこなわれる。
reload関数を2回実行すれば初期化は2回実行される。
グローバル変数 __name__とモジュールとモジュールの初期化
「他言語,多言語プログラマーの初めてのPython - 愚鈍人」で述べたように、モジュールのグローバル変数 __name__の値を確認する事でモジュールの初期化処理に利用できる。
サブモジュール(sub.py)
1 2 3 4 5 6 7 8 9 10 | #coding: UTF-8 if __name__ == '__main__': # sub.pyがメインモジュールとして実行された時のみ実行されるコードをここに記述 print "sub.pyがメインモジュールとして実行されたました。" else: # sub.pyがimportされた時のみ実行されるコードをここに記述 print "sub.pyがimportされました。" # sub.pyがメインモジュールとして実行された時とimportされた時のどちらでも実行されるコードはif-elseの外に記述 |
メインモジュールとサブモジュールでグローバル変数 __name__の値がどのようになっているかはこれまでのサンプルプログラムの実行結果をみれば明らか。
モジュール検索パス - importするモジュール(ソースファイル)をどこにおくか
import文でモジュールを指定する時に、モジュールのパスは指定されていない。
ではpythonはどのようにしてimportするモジュールを探すのだろう。
6. モジュール — Python 2.7ja1 documentationによると
spam という名前のモジュールが import されると、インタプリタは spam.py という名前のファイルを現在のディレクトリ内で探し、次に環境変数 PYTHONPATH に指定されているディレクトリのリストから探します。 PYTHONPATH はシェル変数 PATH と同じ構文、すなわちディレクトリ名を並べたものです。 PYTHONPATH が設定されていないか、探しているファイルが見つからなかった場合は、検索対象をインストール方法に依存するデフォルトのパスにして続けます。
つまり、カレントデレクトリ,環境変数PYTHONPATH ,インストール依存のデフォルトの検索パスの順という事になる。
モジュール検索パスはsysモジュールのpath変数にリストとして格納されており、これを調べる事で確認できる。
サブモジュール(sub.py)
import sys for path_text in sys.path: print path_text
私の環境では環境変数PYTHONPATHを設定していない状態で以下のような結果となった。
実行結果
カレントデレクトリ
C:\Python27\lib\site-packages\setuptools-7.0-py2.7.egg
C:\Python27\lib\site-packages\pip-1.5.6-py2.7.egg
C:\windows\system32\python27.zip
C:\Python27\DLLs
C:\Python27\lib
C:\Python27\lib\plat-win
C:\Python27\lib\lib-tk
C:\Python27
C:\Python27\lib\site-packages
C:\Python27\lib\site-packages\win32
C:\Python27\lib\site-packages\win32\lib
C:\Python27\lib\site-packages\Pythonwin
[pythonインストールデレクトリ]\Lib\site-packagesデレクトリに拡張子.pthのファイルにパスを記述する事で検索パスを追加する事もできる。
Windowの環境でpython2.7をデフォルトのインストールデレクトリC:\Python27にインストールした状態で、
setuptools,pip,Pythonwin等の後からインストールしたパッケージはC:\Python27\lib\site-packagesデレクトリ配下にインストールされている。
これらのモジュールはインストール時に自動的に拡張子.pthのファイルがインストールされ検索パスに追加されているようだ。
-
27.14. site — サイト固有の設定フック — Python 2.7ja1 documentation
このモジュールをインポートすることで、サイト固有のパスをモジュール検索パスへ付け加えます。
-
.pth という拡張子のファイルを検索パスに入れておくことで、 sys.path の末尾に検索パスを追加しておくことができます。
.pth ファイルは検索パスに含まれていれば問題なく動作はしますが、推奨される置き場所は site-package 下です。自作およびサードパーティ製モジュール・パッケージの推奨置き場と同じ場所です。
モジュール検索パスはsysモジュールのpath変数のリストを操作する事でプログラムのコードより動的に変更できる。
サブモジュール(sub.py)
#coding: UTF-8 import sys # 検索パスの追加 sys.path.append('追加する検索パス') # 検索パスの削除 sys.path.remove('削除する検索パス')
モジュールを調べる
参考記事
「利用可能なモジュールの一覧」を調べる。 - help関数
組込み関数helpの引数にmodulesを指定すると「利用可能なモジュールの一覧」を調べる事ができる。
help('modules')
ロード済みモジュールを調べる - sys.modules
sysモジュールのmodulesを使うとロード済みモジュールを調べる事ができる。
import sys print sys.modules