Python コマンドラインオプション -m の動作原理と実用例

Python インタープリタの -m オプション

Python インタープリタをコマンドラインから起動する際、さまざまなオプションを指定できます。その中で特に有用なのが「-m」スイッチです。このオプションは、特定のモジュールをスクリプトとして直接実行するための機能を提供します。

公式ドキュメントにおける定義は以下の通りです。

-m mod : library module をスクリプトとして実行する(オプションリストを終了させる)

ここで指定する「mod」はモジュール名を指します。このオプションが指定されると、その後に続く他のオプションは無効化され、解釈器は指定されたモジュールの実行を試みます。これは「-c」と同様に、オプション列を終端させる Interface options に分類されます。

主な活用シーン

-m オプションを用いることで、標準ライブラリに含まれる便利なツールを手軽に起動できます。代表的な 5 つのユースケースを以下に示します。

1. 簡易 HTTP サーバーの起動

Python 3 環境では、任意のディレクトリをルートとして簡易な HTTP サーバーを立ち上げることが可能です。ポート番号を 9000 に指定し、ローカルホストのみで受け付ける場合は次のように記述します。

python -m http.server --bind 127.0.0.1 9000

これにより、ブラウザで「http://127.0.0.1:9000」にアクセスすると、実行時のディレクトリにあるファイル一覧が表示され、ダウンロードや閲覧が可能になります。

2. ドキュメントサーバーの生成

pydoc モジュールを利用することで、インストールされているパッケージのドキュメントを HTML 形式で閲覧できるサーバーを起動できます。

python -m pydoc -p 8080

実行後、ブラウザから指定ポートにアクセスすれば、標準ライブラリや追加パッケージの API ドキュメントを検索・閲覧できます。

3. デバッガの実行

スクリプトをデバッグモードで実行したい場合、pdb モジュールを指定します。これにより、プログラム実行前にデバッガプロンプトが表示され、ステップ実行やブレークポイント設定が行えます。

python -m pdb target_script.py

4. パフォーマンス計測

timeit モジュールを使うと、コマンドライン上で短いコード断片の実行時間を計測できます。例えば、数値のリスト生成における異なるアプローチの効率を比較する場合、以下のような命令が有効です。

python -m timeit '"-".join(str(n) for n in range(100))'
python -m timeit '"-".join(map(str, range(100)))'
python -m timeit -s "lst=[]" "for i in range(100): lst.append(str(i)); '-'.join(lst)"

これらの結果を比較することで、ジェネレータ表現、map 関数、および明示的なループ処理之间的な性能差を把握できます。

5. パッケージ管理ツールの呼び出し

パッケージインストールにおいて「pip install」ではなく「python -m pip install」を使用することが推奨される場合があります。特に複数の Python バージョンが共存する環境では、どのインタープリタに関連付ける庫を明確にする必要があります。

python3.9 -m pip install requests

この写法により、Python 3.9 環境に対して確実にパッケージがインストールされ、バージョン混同によるエラーを防ぐことができます。

動作機制の解説

「python -m module_name」と入力された際、内部でどのような処理が行われるのでしょうか。簡潔に言えば、インタープリタはsys.pathに含まれるディレクトリを捜索し、該当するモジュールまたはパッケージを探し出し、それを__main__として実行します。

単独モジュールの場合

拡張子「.py」を持つファイルはモジュールとして扱われます。-m オプション指定時には拡張子を省略し、モジュール名のみを記述します。ただし、これは C 言語で記述された拡張モジュールには適用されません。

存在しないモジュール名を指定すると「No module named xxx」というエラーが発生します。また、誤って拡張子を含めて「xxx.py」と指定した場合、属性エラーが生じる可能性があります。

直接スクリプトを実行する「python script.py」と「python -m script」は、一見同じように見えますが、内部処理に違いがあります。

  • 直接実行:ファイルシステム上のパス(絶対または相対)を基に脚本を特定し実行します。
  • -m 実行:_import_を行わずにモジュール命名空間内を捜索し、パスを特定してから実行します。この過程でpkgutilやrunpyといった内部モジュールが利用されます。

重要な点は、-m 実行時、対象スクリプトの__name__属性が「__main__」に設定されることです。これにより、スクリプト内で「if __name__ == '__main__':」ブロックが有効になり、エントリーポイントとして機能します。

また、直接実行ではカレントディレクトリにファイルが存在する必要がありますが、-m 実行ではsys.path上のどこにモジュールが存在しても検出可能です。この特性により、パスを気にせずモジュール名だけで実行できる利点があります。

パッケージの実行

パッケージを指定した場合、インタープリタはまずパッケージを特定し、その内部にある__main__.pyサブモジュールを実行しようとします。つまり、「python -m package」は実質的に「python -m package.__main__」と同義です。

例えば、標準ライブラリの http パッケージには__main__.pyが存在しないため、「python -m http」とするとエラーになります。代わりに「python -m http.server」のように、実行可能なサブモジュールを指定する必要があります。

一方、pip パッケージには__main__.pyが用意されています。このファイルがエントリーポイントとなり、内部の main 関数を呼び出す構造になっています。そのため、「python -m pip」だけでコマンドが完結します。

歴史的経緯と進化

-m オプションは Python 2.4(2004 年)で初めて導入されました。当初は pdb や profile などの一部の組み込みモジュールに限定された機能でした。

その後、PEP 338「Executing modules as scripts」が提案され、Python 2.5(2006 年)で実装されました。この改善により、以下の機能が追加されました。

  • PEP 302 の import hook 機制との連携による、パッケージ内モジュール検索能力の向上
  • zipimport や frozen modules など、他の导入機制との統合
  • CPython 本体の変更ではなく、runpy.run_moduleによる実装により、他インタープリタへの移植性を確保

これにより、あらゆる命名空間からコマンドライン指定のモジュールを定位・実行できるようになりました。

さらに Python 3.1(2009 年)では、パッケージ名のみを指定してその__main__サブモジュールを実行する機能が追加されました。2014 年には名前空間パッケージ(namespace packages)への対応も完了し、現在では標準的な機能として広く利用されています。

タグ: Python command-line Modules PIP debugging

10月27日 04:04 投稿