まず、これは実装の詳細です。私はCPythonとPyPyに精通しているので、それらに答えを制限しています。Jython、IronPython、およびその他の実装に対する回答は、おそらく根本的に異なります。
Pythonは「仮想マシンモデル」に近いです。Pythonコードは、知識レベルが高すぎる人々の発言とは対照的に、誰もが(私を含めて)カジュアルな議論で混乱させているにもかかわらず、解釈されることはありません。ロードされると、常にバイトコードにコンパイルされます(これもCPythonとPyPyで)。モジュールがインポートされ、.pyファイルからロードされたためにロードされた場合、コンパイル出力をキャッシュするために.pycファイルが作成される場合があります。この手順は必須ではありません。さまざまな方法でオフにすることができ、プログラムの実行は最も小さなビットには影響しません(ただし、モジュールをロードする次のプロセスで再度オフにする必要があります)。ただし、バイトコードへのコンパイルは避けられません。ディスクからロードされていない場合、バイトコードはメモリに生成されます。
このバイトコード(正確な詳細は実装の詳細であり、バージョンによって異なります)は、モジュールレベルで実行されます。これには、関数オブジェクトやクラスオブジェクトなどの構築が含まれます。これらのオブジェクトは、すでにメモリにあるバイトコードを再利用(ポインタを保持)するだけです。これは、コンパイル中/コンパイル後にコードとクラスが石で設定されるC++やJavaとは異なります。実行中に、import
ステートメントが検出される場合があります。輸入機械を説明するためのスペース、時間、理解が不足していますが、簡単な話は次のとおりです。
- すでに一度インポートされている場合は、そのモジュールオブジェクトを取得します(静的言語がコンパイル時にのみ持つものの別のランタイム構造)。いくつかの組み込みモジュール(この質問の範囲を超えた理由で、すべてPyPyにあります)は、インタープリターのコアと非常に緊密に統合されており、非常に基本的であるという理由だけで、Pythonコードが実行される前にすでにインポートされています。
sys
そのようなモジュールです。一部のPythonコードは、特に対話型インタープリターを起動するときに、事前に実行される場合もあります(ルックアップsite.py
)。
- それ以外の場合、モジュールはにあります。このためのルールは私たちの関心事ではありません。最終的に、これらのルールはPythonファイルまたは動的にリンクされたマシンコードのいずれかに到達します(Windowsでは.DLL、Pythonモジュールは特に拡張子.pydを使用しますが、これは単なる名前です。UNIXでは同等の.soが使用されます)。
- モジュールは最初にメモリにロードされます(動的にロードされるか、解析されてバイトコードにコンパイルされます)。
- 次に、モジュールが初期化されます。拡張モジュールには、と呼ばれるもののための特別な機能があります。Pythonモジュールは、上から下に実行するだけです。正常に動作するモジュールでは、これはグローバルデータを設定し、関数とクラスを定義し、依存関係をインポートするだけです。もちろん、他のことも起こり得ます。結果のモジュールオブジェクトはキャッシュされ(ステップ1を思い出してください)、返されます。
これはすべて、標準ライブラリモジュールとサードパーティモジュールに適用されます。そのため、スクリプトにインポートする標準ライブラリモジュールのようにスクリプトを呼び出すと、紛らわしいエラーメッセージが表示される可能性があります(キャッシュが原因でクラッシュすることはありませんが、それ自体がインポートされます。
バイトコードの実行方法(質問の最後の部分)は異なります。CPythonは単にそれを解釈しますが、正しくご存知のように、それは魔法のようにCPUを使用しないという意味ではありません。代わりに、次に実行されるバイトコード命令を検出し、その命令のセマンティクスを実行するネイティブコードにジャンプする、大きな醜いループがあります。PyPyはもっと面白いです。それは解釈から始まりますが、途中でいくつかの統計を記録します。そうする価値があると判断すると、インタプリタが行うことの詳細な記録を開始し、高度に最適化されたネイティブコードを生成します。インタプリタは、Pythonコードの他の部分でも引き続き使用されます。多くのJVMとおそらく.NETでも同じですが、引用する図はそれを覆い隠していることに注意してください。