0

次のリンクに基づいて、いくつかのことを理解しています (間違っている可能性があります!)。

http://docs.python.org/2/glossary.html#term-bytecode

  1. .pycキャッシュされたファイルであり、モジュールが別の場所にインポートされた場合にのみ生成されます

  2. .pyc実行パフォーマンスではなく、読み込みパフォーマンスを支援することです。

  3. runningはどこかにインポートされない限りpython foo.py生成されません。foo.pycfoo

  4. Pythonにはバイトコードコンパイラがあります(を生成するために使用されます.pyc

  5. Python の仮想マシンはバイトコードを実行します。

を実行するpython foo.pyと、 iffoo.pyがどこにもインポートされていない場合、Python は実際にインメモリ バイトコードを作成しますか?

この欠落.pycは、Python VM のアイデアを壊しているようです。

pythonこの質問は、Python インタープリター (ターミナルで実行) でのコード実行に拡張されます。私は、CPython (またはほぼすべての言語実装) は純粋な解釈を行うことができないと考えています。

質問の核心は、VM が実際に.pycファイルを読み取るかどうかだと思います。VM が.pycを実行環境にロードするとします。

4

3 に答える 3

4

あなたのポイント 1 から 5 は正しいですが、(正確であれば) ポイント 4 は例外です。Python インタープリターには、ソース コードを に変換するバイトコード コンパイラーと呼ばれる部分があり、任意の関数<code object at 0x...>を入力して調べることができます。これは、解釈される実際のバイトコードです。これらのコード オブジェクトは、別の手順として、ファイル内に保存できます。f.__code__f.pyc

ここでは、より詳細な操作について説明します。foo.pyバイトコード コンパイラは、インポートする および各モジュールをロードするときに、モジュールごとに 1 回だけ実行されます。それほど長い操作ではありませんが、特にモジュールが他の多くのモジュールをインポートする場合は、まだ時間がかかります。これは、.pycファイルが画像に入る場所です。ステートメントがバイトコード コンパイラを呼び出した後、結果をファイル内importに保存しようとします。次回、ファイルがすでに存在し、ファイルが変更されていない場合は、そこから が再ロードされます。これは単なる最適化です。バイトコード コンパイラを呼び出すコストを回避します。どちらの場合も、結果は同じです。 aはメモリ内に作成され、解釈されます。<code object>.pyc.pyc.py<code object><code object>

importたとえば、メインモジュール(つまり、foo.pyコマンドライン内)ではなく、ステートメントに対してのみ機能しますpython foo.py。アイデアは、それは実際には問題にならないということです --- 典型的な中規模から大規模のプログラムでバイトコード コンパイラが時間を浪費するのは、foo.py.

于 2013-03-07T08:41:16.373 に答える
1

Pythonは、ソースコードを直接実行することはできません(Bashなどのアドホック解析を行う他のスクリプト言語とは異なります)。ソースが何であれ、すべてのPythonソースコードはバイトコードにコンパイルする必要があります。(これには、たとえばコードランスルーevalとが含まれますexec)。バイトコードの生成はパーサーの実行を伴うためかなりコストがかかるため、バイトコードを(.pycとして)キャッシュすると、解析フェーズを回避することでモジュールのロードが高速化されます。

import fooとの違いはpython foo.py、後者は生成されたバイトコードをキャッシュしないということです。

于 2013-03-07T09:13:33.680 に答える
1

興味深い...私が最初にしたことは、--help

$ python --help
usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ...
Options and arguments (and corresponding environment variables):
-B     : don't write .py[co] files on import; also PYTHONDONTWRITEBYTECODE=x
...

そして、私が目にする最初のオプションは、インポート時にpycおよびpyoファイルの自動生成を無効にすることですが、それはおそらくアルファベット順になります。

いくつかのテストを実行しましょう

$ echo "print 'hello world'" > test.py
$ python test.py 
hello world
$ ls test.py*
test.py
$ python -c "import test"
hello world
$ ls test.py*
test.py     test.pyc

そのため、インポート時にのみpycファイルが生成されました。

どのファイルが使用されているかを確認するために、Linux truss に似た OS X dtruss を使用して完全なトレースを実行します ...

$ echo '#!/bin/sh 
 python test.py' > test.sh 
$ chmod a+x test.sh
$ sudo dtruss -a ./test.sh 2>&1 | grep "test.py*"
975/0x5713:    244829       6      3 read(0x3, "#!/bin/sh \npython test.py\n\b\0", 0x50)         = 26 0
975/0x5713:    244874       4      2 read(0xFF, "#!/bin/sh \npython test.py\n\b\0", 0x1A)        = 26 0
977/0x5729:    658694       6      2 readlink("test.py\0", 0x7FFF5636E360, 0x400)        = -1 Err#22
977/0x5729:    658726      10      6 getattrlist("/Users/samyvilar/test.py\0", 0x7FFF7C0EE510, 0x7FFF5636C6E0 = 0 0
977/0x5729:    658732       3      1 stat64("test.py\0", 0x7FFF5636DCB8, 0x0)        = 0 0
977/0x5729:    658737       5      3 open_nocancel("test.py\0", 0x0, 0x1B6)      = 3 0
977/0x5729:    658760       4      2 stat64("test.py\0", 0x7FFF5636E930, 0x1)        = 0 0
977/0x5729:    658764       5      2 open_nocancel("test.py\0", 0x0, 0x1B6)      = 3 0

その見た目から、pythonはtest.pycファイルにまったく触れていませんでした!

$ echo '#!/bin/sh 
 python -c "import test"' > test.sh
$ chmod a+x test.sh
$ sudo dtruss -a ./test.sh 2>&1 | grep "test.py*"
$ sudo dtruss -a ./test.sh 2>&1 | grep "test.py*"
1028/0x5d74:    654642       8      5 open_nocancel("test.py\0", 0x0, 0x1B6)         = 3 0
1028/0x5d74:    654683       8      5 open_nocancel("test.pyc\0", 0x0, 0x1B6)        = 4 0
$

興味深いことに、test.py を開いてから test.pyc を開いたようです。

pyc ファイルを削除するとどうなりますか。

$ rm test.pyc
$ sudo dtruss -a ./test.sh 2>&1 | grep "test.py*"
1058/0x5fd6:    654151       7      4 open_nocancel("/Users/samyvilar/test.py\0", 0x0, 0x1B6)        = 3 0
1058/0x5fd6:    654191       6      3 open_nocancel("/Users/samyvilar/test.pyc\0", 0x0, 0x1B6)       = -1 Err#2
1058/0x5fd6:    654234       7      3 unlink("/Users/samyvilar/test.pyc\0", 0x1012B456F, 0x1012B45E0)        = -1 Err#2
1058/0x5fd6:    654400     171    163 open("/Users/samyvilar/test.pyc\0", 0xE01, 0x81A4)         = 4 0

最初にtest.pyを開いてから、エラーを返したtest.pycを開こうとした後、unlinkを呼び出してpycファイルを再度生成しました...興味深いことに、チェックすると思いました。

元の py ファイルを削除するとどうなりますか。

$ sudo dtruss -a ./test.sh 2>&1 | grep "test.py*"
1107/0x670d:    655064       4      1 open_nocancel("test.py\0", 0x0, 0x1B6)         = -1 Err#2
1107/0x670d:    655069       8      4 open_nocancel("test.pyc\0", 0x0, 0x1B6)        = 3 0

驚くことではありませんが、test.pyを開くことができませんでしたが、今日まで続いていました。これが実際に「OK」かどうかはわかりません。Pythonは何らかの警告を発するはずです。誤ってファイルを削除したり、テストを実行したり、ソースコードが見つからないときに汗をかき始めるだけで合格したので、安堵のため息をついた!

このテストの後、Python は pyc ファイルのみを直接使用するか python test.pyc、インポート時に間接的に使用するかを想定しています。それ以外の場合は使用していないようです。

おそらくCPythonコンパイラはかなり高速になるように設計されており、型チェックをあまり行わず、おそらく非常に高レベルのバイトコードを生成するため、ほとんどの作業負荷は実際には仮想マシンによって実行されます...おそらく単一のパスを実行します、lexing->compiler->byte-codeを一度に実行すると、毎回これが実行され、コマンドラインから、またはインポート時にpythonファイルが読み取られ、pycファイルが存在しない場合は作成されます。

これが、他のいくつかの実装がコンパイルに時間がかかるため高速である理由かもしれませんが、十分に最適化できるはるかに生のバイトコードを生成します。

純粋な解釈を効率的に行う仮想マシンを構築することは非常に困難です...

バランスがすべてです。バイトコードが強力であるほど、コンパイラは単純になりますが、仮想マシンはより複雑で遅くなる必要があり、その逆も同様です...

于 2013-03-07T09:05:06.420 に答える