Pythonファイルの実行(2): Pythonモジュールを作る

前回はPythonをスクリプト装置として使い、簡単な自動処理を書きました。スクリプトを使う方法なら、Pythonのコンソールを介することなくPythonコードを実行できます。ただ複雑な関数など、「コンソール内で使うオブジェクトをファイルで定義しておきたい」という場面も出てきます。そのようなときのために、今回はPythonモジュールの作成について紹介します。

目次:Pythonではじめるプログラミング入門

今回の内容

  1. まずは作ってみる
  2. import文とモジュールオブジェクト
  3. 次のステップ

(1) まずは作ってみる

今回も例のごとく、まずはPythonモジュールなるものを作ってみて、用法を確認してみましょう。

ファイルに定義内容を書く

スクリプト作成の際と同様に、最初にするのは「.py」ファイルに読み込みたい定義を書くことです。前回同様、以下のポイントに注意してください:

  • 最後に空行を追加する
  • 標準テキストファイル
  • 拡張子「.py」で保存

とくに今回の場合、「.py」の拡張子がなければPythonはモジュールとして認識しないので注意してください。

たとえば簡単に、以下のような内容を「mymodule1.py」というファイルに書き込んでみましょう:


# モジュール内で変数を定義する
theanswer = 42

# モジュール内で関数を定義する
def greet():
    print("モジュールからこんにちは!")

def ask():
    print("the answer is:", theanswer)

コンソールからモジュールを読み込む

モジュール使用時には、モジュールがあるディレクトリで端末エミュレータを開く必要があります(参照:特定のディレクトリで端末エミュレータを開く)。開いたら、通常どおりPythonコンソールを開いてください(以下の$は端末エミュレータのプロンプトです):

$ python
...(最初の表示は省略)...
>>> ■

コンソールの環境にモジュールを読み込むには、「import文」を使います。モジュールの名前は、保存したファイル名から拡張子を除いたものになります。エラーが出ず、何の出力もなければ成功です:

>>> import mymodule1
>>> ■

読み込んだモジュールは、importで使用した名前でアクセスすることができます。モジュール内で定義した関数や変数にアクセスするには、モジュール名と「.」(ドット、ピリオド)に続けて名前を入力します:

>>> mymodule1.theanswer
42
>>> mymodule1.greet()
モジュールからこんにちは!
>>> mymodule1.ask()
the answer is: 42

(2) import文とモジュールオブジェクト

モジュールオブジェクト

モジュールとはいったい何なのかについて、もう少し詳しく見てみます。まずはモジュール自体がどのような型なのかを見てみましょう。ちなみに以下の例では「#」を使ったコメントを利用しています。コンソール上でもコメントはコメントとして認識されます。

>>> mymodule1 # mymodule1をインタプリタに評価させる
<module 'mymodule1' from '/Users/gwappa/Code/mymodule1.py'>
>>> type(mymodule1) # 型をチェック
<class 'module'>

mymodule1が「mymodule1.py」というファイルから、「module」という型のオブジェクトとしてPython環境内に読み込まれていることがわかります。

従って、モジュール読み込みに使うimport文には、以下のような働きがあることがわかります:

  • ファイル内のコードを実行し、変数や関数を定義する
  • 定義された関数を、新しいmodule型オブジェクトの中にまとめる
  • できあがったmodule型オブジェクトを、特定の名前(変数)に代入する

上記の例での「mymodule1」は他のxやyと同じただの変数ですので、代入したり消去したりすることができます。ただしモジュールが代入された変数への代入や消去はほとんどの場合悪影響しか生みませんので、そのようなテクニックは実際には使わないようにしてください

>>> mod = mymodule1 # 別の変数modにモジュールオブジェクトを代入
>>> mod
<module 'mymodule1' from '/Users/gwappa/Code/mymodule1.py'>

>>> mymodule1
<module 'mymodule1' from '/Users/gwappa/Code/mymodule1.py'>
>>> del mymodule1 # 'mymodule1'という名前をPython環境から消去
>>> mymodule1
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'mymodule1' is not defined

名前インポートと別名インポート

import文にはいくつかの変種があります。そのひとつは、モジュール内の名前を直接Python実行環境に読み込む方法です(ここでは便宜的に名前インポートと呼びますが、とくに呼び方が決まっているわけではないようです):

>>> from mymodule1 import greet, ask
>>> greet()
モジュールからこんにちは!
>>> ask()
the answer is: 42

上の例のように「from モジュール名 import 名前(複数のときはカンマ区切り)」という形式の文にすることで、mymodule1という名前を定義せずにモジュール内のgreet、askを使えるようになります。とくに上のask関数はモジュールを介さずによばれているにも関わらず、モジュール内の変数theanswerを参照できていることに注意してください。名前インポートを用いることで、ドット区切りの冗長な記法を用いることなくシンプルにモジュール内の関数が使えるようになります。

これらと並列して使える機能に別名インポートと呼ばれるものがあります。モジュールや関数・変数を読み込む先の名前を、自分で指定できる機能です。

>>> import mymodule1 as m  # 名前'm'でmymodule1を読み込む
>>> mymodule1  # 'mymodule1'には何も代入されない
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'mymodule1' is not defined
>>> m  # 'm'にmymodule1が代入されている
<module 'mymodule1' from '/Users/gwappa/Codes/Code/mymodule1.py'>


>>> from mymodule1 import greet as gr  # greet関数を'gr'という名前で読み込む
>>> gr()
モジュールからこんにちは!

このように、import文や名前インポートの文のあとに「as 名前」を続けることで、指定した名前にモジュールやオブジェクトを読み込むことができます。複数の名前を別名でインポートする場合は「from モジュール名 import 名前1 as 別名1, 名前2 as 別名2, …」という形で動きます。

別名インポートもまた長い名前の略記や名前衝突(*)の回避に役立ちますが、シンプルにしすぎたりするとどのモジュールを何の名前にしたかわからなくなることがありますので、自分なりのルールを設けるのがよいでしょう。

(*) 名前衝突:複数のモジュール(あるいはメインのPython実行環境)に同じ名前の関数や変数が定義されているとき、名前インポートによって起きる問題のこと。単なる名前インポートの場合、もともとあった内容が上書きされてしまう。別名インポートを使うことで、もともとの内容を上書きすることなく両方をシンプルな名前で参照できるようになる。

モジュールを読み込みなおす(impモジュール)

たとえばmymodule1を一度読み込んだ後、関数を書き直したり、新たに関数を付け加えたい場合を考えます。通常のステップとしては、以下のとおりになります。

  1. mymodule1.pyの定義を変更・追加する
  2. mymodule1を読み込みなおす

ただこのときPython実行環境に一度mymodule1を読み込んでしまっていると、もう一度import文を書いてもモジュールは再読み込みされません

例として、以下のモジュールを作ってみましょう:

print("モジュールが読み込まれました")

今回はなんとモジュール内にはprint関数しかありません!モジュール読み込みの際にはファイル内のコードは全て実行されますので、ファイル内にprint関数を書いておけば印字されるのです。

もちろん変数も関数も宣言されていませんので、読み込みの結果として生成されるのは空き箱のようなモジュールです。

>>> import mymodule2
モジュールが読み込まれました

さて、import文を再び実行してみます:

>>> import mymodule2
>>> ■

先ほど指摘したとおり、print関数が実行されていないようです。実は、実行環境内でモジュールの内容がキャッシュ(一時記憶)されてしまうため、Python実行環境は「既に読み込まれているこのモジュールオブジェクトを使えばいいや」と判断してしまうのです。

たとえ名前インポートで別の読み込み方をしたり、del文でモジュールに対応する名前をなくしたりしたとしても、うまくいきません(大本の参照先がひとつであるため):

>>> import mymodule2 as mm  # 別名インポートをしても再読み込みは起こらない
>>> del mymodule2     # 名前'mymodule2'を消して…
>>> del mm            # 名前'mm'を消して…
>>> import mymodule2  # 改めてインポートしても再読み込みは起こらない
>>> ■

そこでPythonで使われるのがimpモジュールで、それ自体がモジュールになっています。impはimportの略で、その名の通りインポート関連のより具体的な関数を持っています。再読み込みの際に、impで定義されているreloadという関数を使います:

>>> import imp             # impモジュールの読み込み
>>> imp.reload(mymodule2)  # mymodule2を再読み込み
モジュールが読み込まれました
<module 'mymodule2' from '/Users/gwappa/Code/mymodule2.py'>
>>> ■

上記のように「imp.reload(モジュールの名前)」という文を使うことで、モジュールを再読み込みすることができます。ただしモジュールオブジェクトに対応する名前であれば別名でも再読み込みできますが、名前インポートされた変数や関数はただの変数や関数として扱われますのでimp.reloadを使っても再読み込みできませんので注意してください。

reload関数は、自分で作ったモジュールを試用したりデバッグしたりする際にとても役立ちます。

(3) 次のステップ

今回はモジュールについて紹介しました。モジュールを使えば、よく使う関数をコンソール上でいちいち定義する必要がない上、一回作ったら次回もその次も再利用していくことができとても便利です。

次回はモジュールについてもう少し詳しく触れつつ、スクリプトとモジュールの違いを探ります。

続き:Pythonファイルの実行(3): スクリプトとモジュールの違い

目次:Pythonではじめるプログラミング入門

投稿者: gwappa

A free-floating DIY programmer who loves to automate chores in a royalty-free manner. Mainly uses Python, Java and C++.

コメントを残す