Pythonモジュールとパッケージの使い方とインポート機構

Pythonでモジュールを単体で実行すると、その中のテストコードも実行されてしまうことがある。たとえば、以下のような温度変換モジュール candf.py を作成したとする:

# candf.py
def c2f(celsius):
    return celsius * 9 / 5 + 32

def f2c(fahrenheit):
    return (fahrenheit - 32) * 5 / 9

# テストコード
print("テストデータ:0 摂氏度 = %.2f 華氏度" % c2f(0))
print("テストデータ:0 華氏度 = %.2f 摂氏度" % f2c(0))

このモジュールを別ファイル demo.py からインポートして使うと:

import candf
print("32 摂氏度 = %.2f 華氏度" % candf.c2f(32))
print("99 華氏度 = %.2f 摂氏度" % candf.f2c(99))

実行結果にはモジュール内のテスト出力も含まれてしまう。これを避けるためには、if __name__ == '__main__': を使用する:

# candf.py(修正版)
def c2f(celsius):
    return celsius * 9 / 5 + 32

def f2c(fahrenheit):
    return (fahrenheit - 32) * 5 / 9

if __name__ == '__main__':
    print("テストデータ:0 摂氏度 = %.2f 華氏度" % c2f(0))
    print("テストデータ:0 華氏度 = %.2f 摂氏度" % f2c(0))

これにより、モジュールが直接実行されたときのみテストコードが実行されるようになる。

モジュールが見つからないエラーの対処法

ModuleNotFoundError は、Pythonが指定されたモジュールを検索パスから見つけられなかった場合に発生する。Pythonは以下の順序でモジュールを検索する:

  • 現在のディレクトリ
  • PYTHONPATH 環境変数に設定されたディレクトリ
  • Pythonの標準ライブラリディレクトリ

これらのパスは sys.path に格納されている。モジュールを正しくインポートするための主な方法は以下の3つである。

1. sys.path に一時的にパスを追加

import sys
sys.path.append(r'D:\python_module')
import hello
hello.say()

この方法はスクリプト実行中に有効だが、プロセス終了後は無効になる。

2. モジュールを標準パスに配置

site-packages ディレクトリ(例: D:\python3.6\lib\site-packages)にモジュールを置くことで、すべてのスクリプトから利用可能になる。

3. PYTHONPATH 環境変数の設定

Windows:
システム環境変数またはユーザ環境変数に PYTHONPATH を追加し、値として .;D:\python_module を設定する(複数パスはセミコロン区切り)。

Linux / macOS:
~/.bash_profile または ~/.zshrc に以下を追記:

export PYTHONPATH=".:/home/username/python_module"

その後、source ~/.bash_profile を実行して反映させる。

モジュールのインポートの本質

import 文はモジュール全体をメモリに読み込み、実行し、その内容を module 型のオブジェクトとして名前空間にバインドする。たとえば:

# fk_module.py
print("this is fk_module")
name = 'fkit'
def hello():
    print("Hello, Python")
import fk_module
print(type(fk_module))  # <class 'module'>

一方、from ... import ... はモジュールを実行した上で、指定されたメンバーのみを現在の名前空間に取り込む:

from fk_module import name, hello
print(name)  # fkit
# print(fk_module)  # NameError(モジュール自体はインポートされていない)

また、同じモジュールを複数回インポートしても、実際の実行は1回のみ行われる。これは循環インポートを防ぐ仕組みでもある。

__all__ の役割

from module import * の際に公開するメンバーを制限するには、モジュール内で __all__ を定義する:

# demo.py
def say():
    print("人生苦短、我学Python!")
def CLanguage():
    print("C语言中文网")
def disPython():
    print("Python教程")

__all__ = ["say", "CLanguage"]
from demo import *
say()        # OK
CLanguage()  # OK
disPython()  # NameError

ただし、import demofrom demo import disPython のように明示的にインポートする場合は、__all__ の影響を受けない。

パッケージの作成と利用

パッケージは複数のモジュールを含むディレクトリであり、Python 3.3以降では __init__.py がなくてもパッケージとして認識されるが、初期化コードを実行したい場合は依然として必要である。

例として、以下のような構造のパッケージ my_package を作成する:

my_package/
├── __init__.py
├── module1.py
└── module2.py

__init__.py でサブモジュールを公開することで、利用側の記述を簡潔にできる:

# my_package/__init__.py
from .module1 import display
from .module2 import CLanguage

これにより、以下のように簡単に利用できる:

import my_package
my_package.display("Hello")
lang = my_package.CLanguage()
lang.display()

モジュール内容の確認方法

モジュールに含まれるメンバーを調べるには、dir() 関数や __all__ 属性を使用する:

import string
# 特殊メソッドを除いた公開メンバー
print([name for name in dir(string) if not name.startswith('_')])
# __all__ が定義されていれば
print(string.__all__)

さらに詳細な情報を得るには、help() 関数や __doc__ 属性を利用する:

help(string.ascii_letters)
print(string.capwords.__doc__)

これらにより、モジュールの機能や使い方をインタラクティブに確認できる。

タグ: Python モジュール パッケージ import sys.path

5月17日 00:36 投稿