web-dev-qa-db-ja.com

-mオプションを使用してPythonコードを実行するかどうか

pythonインタープリターには-mmoduleオプションがあります。これは「ライブラリモジュールmoduleをスクリプトとして実行します」。

このpythonコードa.pyで:

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

python -m aをテストして取得しました

"" <-- Empty String
__main__

一方、python a.pyは戻ります

None <-- None
__main__

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

興味深いことに、python -m runpy aを使用すると、a.pycを取得するためにコンパイルされたpythonモジュールを使用してpython -m aと同じ結果が得られます。

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

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

81
prosseek

-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 __== "__main__":
>     print __package__
>     print __name__
> 
> EOF
$ PYTHONPATH=test python test/foo/bar/baz.py 
None
__main__
$ PYTHONPATH=test bin/python -m foo.bar.baz 
foo.bar
__main__

その結果、Pythonは-mスイッチを使用するときにパッケージを実際に気にしなければなりません。通常のスクリプトではパッケージをにすることはできません。したがって、__package__Noneに設定されます。

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

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

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

$ 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__']に保存されたままです。

115
Martijn Pieters

-mオプションを使用してPythonコードを実行するかどうか

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

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

ドキュメント

docs のように言う:

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 pdbpython /usr/lib/python2.4/pdb.pyと同等です

フォローアップ質問

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

Importステートメントで検索できるモジュールはすべて、プログラムのエントリポイントとして実行できることを意味します。通常、コードブロックがif __== '__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__」属性の目的は何ですか?

15
Aaron Hall

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

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

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

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

0
ddbug