cProfile によって関数が異なる値を返すのはなぜですか?
-
21-12-2019 - |
質問
私は Python (3.3.1) で、長期的なキャッシュフローを伴うかなり単純だが面倒な契約のモデルを構築しています。完成したモデルは所要時間の点でかなり複雑なので、プロファイリングを試みることにしました。ただし、プロファイリングの有無で異なる答えが得られます。
コードを次の例に縮小しました。
def generate_cashflows( income ):
contingent_expense = [1000.0]
income_cf = [income]
outgo_cf = [ -0.001 * contingent_expense[0] ]
bank = [ income_cf[0] + outgo_cf[0] ]
for t in range(1, 20):
contingent_expense.append(1000.0)
income_cf.append( income )
outgo_cf.append( -contingent_expense[t] * 0.001 )
bank.append( bank[t-1] * (1+0.05)**(1/12)
+ income_cf[t]
+ outgo_cf[t]
)
return bank[-1]
print(str(generate_cashflows(0)))
出力:
calum@calum:~/pricing/model$ ./scratch.py
-20.793337746348953
calum@calum:~/pricing/model$ python -m cProfile scratch.py
-20.0
80 function calls in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 scratch.py:5(<module>)
1 0.000 0.000 0.000 0.000 scratch.py:5(generate_cashflows)
76 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.000 0.000 0.000 0.000 {range}
calum@calum:~/pricing/model$
これが毎回異なる答えを出力する理由についての簡単な説明はありますか?マニュアルを読みましたが、明らかな点は何もありません。
解決
まず、python3 で再現しようとしました。「python3Scratch.py」と「python3 -m cProfileScratch.py」を実行すると、どちらも -20.7933 と表示されました。私のマシンでは。
Python 2.x で -20.0 が返される理由は、除算演算子「/」が Python2.x では異なる動作をするためです (http://legacy.python.org/dev/peps/pep-0238/)
Python2では、1/12 == 0
Python3では、1/12 == 0.08333333....
つまり、Python2 では次の行が
bank.append( bank[t-1] * (1+0.05)**(1/12)
単純化すると
bank.append( bank[t-1] * (1+0.05)**(0)
または
bank.append( bank[t-1] * 1
これはおそらく意図したものではありません。python3 の解釈はおそらく正しいですが、python2 の解釈はかなり役に立ちません。余談ですが、(1/12) を (1.0/12) に変更すると、python2 と python3 の両方で同じ出力が得られ、コードはプロファイリングの有無にかかわらず同じ出力を返しますが、これは原因ではなく症状を治療するものです。 。
プロファイリングの有無で異なる出力が得られる理由についての私の推測では、プロファイリングなしで python3 を使用し、プロファイリングありで python2 を使用しているためです。プロファイリングの有無にかかわらずコードを実行するときに同じバージョンの Python を使用することは、有意義な結果を得るために不可欠です。
./scratch.py を使用しているという事実は、おそらく次のような行があることを示しています。
#!/usr/bin/python3
Scratch.py の先頭にあります (ただし、提供されたコードには含まれていません)。走るとき
./scratch.py
/usr/bin/python3 はファイルの実行に使用されます
走るとき
python -m cProfile scratch.py
ファイルの実行にはデフォルトの Python インタープリターが使用されます (おそらく python2 だと思います)
コマンド ラインから (他の引数なしで) 「python」を実行すると、おそらくデフォルトのインタプリタが 2.X であることがわかります。
したがって、プロファイリングの有無にかかわらずコードで同じ出力を返すことは、プロファイリング時に python3 を指定するだけで簡単になります。
python3 -m cProfile scratch.py