興味深い...私が最初にしたことは、--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ファイルが存在しない場合は作成されます。
これが、他のいくつかの実装がコンパイルに時間がかかるため高速である理由かもしれませんが、十分に最適化できるはるかに生のバイトコードを生成します。
純粋な解釈を効率的に行う仮想マシンを構築することは非常に困難です...
バランスがすべてです。バイトコードが強力であるほど、コンパイラは単純になりますが、仮想マシンはより複雑で遅くなる必要があり、その逆も同様です...