-mオプションを使用した、または使用しないPythonコードの実行


111

Pythonインタプリタには、「ライブラリモジュールモジュールをスクリプトとして実行する」という-m モジュールオプションがあります

このpythonコードa.pyの場合:

if __name__ == "__main__":
    print __package__
    print __name__

私はpython -m a得るためにテストしました

"" <-- Empty String
__main__

一方、python a.py戻ります

None <-- None
__main__

私には、これらの2つの呼び出しは、-mオプションを指定して呼び出された場合に__package__がNoneでないことを除いて、同じように見えます。

興味深いことに、を使用するとpython -m runpy apython -m aa.pycを取得するためにコンパイルされたpythonモジュールの場合と同じになります。

これらの呼び出しの(実用的な)違いは何ですか?それらの間の長所と短所はありますか?

また、David BeazleyのPython Essential Referenceは、「-mオプションは、メインスクリプトの実行前に__main__モジュール内で実行されるスクリプトとしてライブラリモジュールを実行する」と説明しています。どういう意味ですか?

回答:


169

-mコマンドラインフラグを使用すると、Pythonはモジュールまたはパッケージをインポートし、スクリプトとして実行します。-mフラグを使用しない場合、指定したファイルは単なるスクリプトとして実行されます

パッケージを実行しようとする場合、区別は重要です。大きな違いがあります:

python foo/bar/baz.py

そして

python -m foo.bar.baz

後者の場合と同様に、foo.barがインポートされ、相対インポートはfoo.bar開始点として正しく機能します。

デモ:

$ mkdir -p test/foo/bar
$ touch test/foo/__init__.py
$ touch test/foo/bar/__init__.py
$ cat << EOF > test/foo/bar/baz.py 
> if __name__ == "__main__":
>     print __package__
>     print __name__
> 
> EOF
$ PYTHONPATH=test python test/foo/bar/baz.py 
None
__main__
$ PYTHONPATH=test python -m foo.bar.baz 
foo.bar
__main__

その結果、-mスイッチを使用する場合、Pythonはパッケージを実際に気にする必要があります。通常のスクリプトはパッケージにはならないため、に__package__設定されNoneます。

しかし、パッケージまたはモジュールを実行でのパッケージ-mとなりましたが、少なくともある可能性パッケージのは、その__package__変数が文字列値に設定されています。上記のデモではfoo.bar、に設定されています。パッケージ内にないプレーンモジュールの場合、空の文字列に設定されます。

__main__ モジュールについては; Pythonは、通常のモジュールと同様に、実行中のスクリプトをインポートします。新しいモジュールオブジェクトが作成され、グローバルネームスペースを保持しますsys.modules['__main__']。これは__name__変数が参照するものであり、その構造のキーです。

パッケージの場合、__main__.pyモジュールを作成し、実行時にそれを実行させることができますpython -m package_name。実際、これがパッケージをスクリプトとして実行できる唯一の方法です。

$ PYTHONPATH=test python -m foo.bar
python: No module named foo.bar.__main__; 'foo.bar' is a package and cannot be directly executed
$ cp test/foo/bar/baz.py test/foo/bar/__main__.py
$ PYTHONPATH=test python -m foo.bar
foo.bar
__main__

したがって、で実行するパッケージに名前を付けると-m、Pythonは__main__そのパッケージに含まれるモジュールを探し、それをスクリプトとして実行します。その後、その名前は引き続きに設定され__main__、モジュールオブジェクトは引き続きに格納されsys.modules['__main__']ます。


1
コマンドとはPYTHONPATH=test python -m foo.barどういう意味ですか?詳しく説明していただけますか?
Andriy 2017

3
@Andriy:PYTHONPATH環境変数を設定します。インポート時にPythonがモジュールを探す一連のディレクトリを展開します。ここでは、testそのシリーズにディレクトリを追加します。同じコマンドラインに置くと、その単一のコマンドにのみ適用されますpython-mを実行したかのように、特定のモジュールをインポートするようにPythonに指示しますimport foo.bar。ただし、__main__そのスイッチを使用すると、Pythonはパッケージ内のモジュールをスクリプトとして自動的に実行します。
Martijn Pieters

1
having to use -m always is not that user-.friendly.使用と非使用の混在-mは、ユーザーフレンドリーではないと思います。
Simin Jie

1
@SiminJie:スクリプトは任意のパスで開くことができ、その親ディレクトリがモジュール検索パスに追加されます。-m現在のディレクトリまたは検索パスにすでに登録されているディレクトリに対してのみ機能します。それが私のポイントでした。-m非常にユーザビリティの問題でエンドユーザーに与えるものではありません。
Martijn Pieters

1
@ flow2k:それfrom Photos import ...は文句を言うでしょう。そうだろうimport Photos.<something>import PhotosPythonが名前空間付きパッケージをサポートしているためにのみ機能します(2つの個別のディストリビューションがPhotos.fooとをPhotos.bar個別に提供し、個別に管理できます)
Martijn Pieters

25

-mオプションを使用した、または使用しないPythonコードの実行

-mフラグを使用します。

スクリプトがある場合も結果はほとんど同じですが、-mフラグを使用せずにパッケージを開発する場合、パッケージ内のサブパッケージまたはモジュールをメインエントリとして実行する場合、インポートを正しく機能させる方法はありません。あなたのプログラムを指します(そして私を信じてください、私は試しました。)

ドキュメント

同様-mフラグのドキュメント言います:

名前付きモジュールのsys.pathを検索し、その内容を__main__モジュールとして実行します。

そして

-cオプションと同様に、現在のディレクトリはsys.pathの先頭に追加されます。

そう

python -m pdb

とほぼ同等

python /usr/lib/python3.5/pdb.py

(現在のディレクトリにpdb.pyというパッケージまたはスクリプトがない場合)

説明:

動作は「意図的にスクリプトに似ている」ようになります。

多くの標準ライブラリモジュールには、実行時にスクリプトとして呼び出されるコードが含まれています。例はtimeitモジュールです:

一部のpythonコードはモジュールとして実行することを目的としています(この例はコマンドラインオプションのdocの例よりも優れていると思います)

$ python -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 40.3 usec per loop
$ python -m timeit '"-".join([str(n) for n in range(100)])'
10000 loops, best of 3: 33.4 usec per loop
$ python -m timeit '"-".join(map(str, range(100)))'
10000 loops, best of 3: 25.2 usec per loop

そしてPython 2.4のリリースノートのハイライトから

-mコマンドラインオプション-python -m modulenameは、標準ライブラリでモジュールを検索して呼び出します。たとえば、python -m pdb と同等ですpython /usr/lib/python2.4/pdb.py

フォローアップ質問

また、David BeazleyのPython Essential Referenceは、「-mオプション__main__は、メインスクリプトの実行前にモジュール内で実行されるスクリプトとしてライブラリモジュールを実行する」と説明しています。

これは、importステートメントで検索できるモジュールは、プログラムのエントリポイントとして実行できることを意味しますif __name__ == '__main__':。通常、コードブロックの最後にが付いている場合。

-m 現在のディレクトリをパスに追加せずに:

ここの他の場所でのコメントは言う:

-mオプションが現在のディレクトリをsys.pathに追加することも、明らかにセキュリティ上の問題です(参照:プリロード攻撃)。この動作は、Windowsのライブラリ検索順序と似ています(最近強化される前)。Pythonがこのトレンドに従わず、追加を無効にする簡単な方法を提供していないのは残念です。sys.path

まあ、これは考えられる問題を示しています-(Windowsでは引用符を削除してください):

echo "import sys; print(sys.version)" > pdb.py

python -m pdb
3.5.2 |Anaconda 4.1.1 (64-bit)| (default, Jul  5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)]

-Iフラグを使用して、これを本番環境用にロックダウンします(バージョン3.4の新機能):

python -Im pdb
usage: pdb.py [-c command] ... pyfile [arg] ...
etc...

ドキュメントから:

-I

Pythonを分離モードで実行します。これは、-Eおよび-sも意味します。分離モードでは、sys.pathにはスクリプトのディレクトリもユーザーのサイトパッケージディレクトリも含まれていません。PYTHON *環境変数もすべて無視されます。ユーザーが悪意のあるコードを挿入できないように、さらに制限が課される場合があります。

何をし__package__ますか?

これは明示的な相対インポートを可能にしますが、この質問に特に密接に関係するものではありません-この回答をここで参照してください:Pythonの "__package__"属性の目的は何ですか?


-mスイッチを使用すると、sys.pathにどのパスが追加されますか?
変数

「-cオプションと同様に、現在のディレクトリはsys.pathの先頭に追加されます。」しかし、引用が何を指しているのかを明確にしました。
アーロンホール

つまり、D:\ testディレクトリでコマンドpython -m foo.bar.booを実行すると、pythonインストールフォルダーまたはD:\ testディレクトリがsys.pathに追加されますか。私の理解では、d:\ testをsys.pathに追加し、foo.barをインポートしてbooスクリプトを実行します
変数

@変数-はい、試してください。
アーロンホール

1

モジュール(またはパッケージ)を-mを使用してスクリプトとして実行する主な理由は、特にWindowsでのデプロイメントを簡略化するためです。PATHや〜/ .localなどのグローバル実行可能ディレクトリを汚染する代わりに、モジュールが通常移動するPythonライブラリの同じ場所にスクリプトをインストールできます(ユーザーごとのスクリプトディレクトリは、Windowsで見つけるのが途方もなく困難です)。

次に、-mと入力するだけで、Pythonが自動的にスクリプトを見つけます。たとえば、python -m pipはそれを実行するPythonインタープリターの同じインスタンスの正しいpipを見つけます。-mなしで、ユーザーが複数のPythonバージョンをインストールしている場合、どれが「グローバル」pipになりますか?

ユーザーがコマンドラインスクリプトの「クラシック」エントリポイントを希望する場合、これらはPATHのどこかに小さなスクリプトとして簡単に追加できます。または、pipは、インストール時にsetup.pyのentry_pointsパラメータを使用してこれらを作成できます。

したがって__name__ == '__main__'、他の信頼できない実装の詳細を確認して無視してください。


-mオプションがsys.pathに現在のディレクトリも追加することは、明らかにセキュリティ上の問題です(プリロード攻撃を参照)。この動作は、Windowsのライブラリ検索順序と似ています(最近強化される前)。Pythonがこのトレンドに従わず、追加を無効にする簡単な方法を提供していないのは残念です。sys.pathに。
ddbug 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.