Warum führt cProfile dazu, dass Funktionen unterschiedliche Werte zurückgeben?
-
21-12-2019 - |
Frage
Ich erstelle in Python (3.3.1) ein Modell für einen recht einfachen, aber umständlichen Vertrag mit langfristigen Cashflows.Das komplette Modell ist im Hinblick auf den Zeitaufwand ziemlich komplex und deshalb habe ich beschlossen, es zu profilieren.Allerdings bekomme ich mit und ohne Profiling unterschiedliche Antworten.
Ich habe den Code auf folgendes Beispiel reduziert:
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)))
Ausgabe:
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$
Gibt es eine einfache Erklärung dafür, warum jedes Mal eine andere Antwort ausgegeben wird?Ich habe das Handbuch gelesen, aber ich sehe nichts Offensichtliches.
Lösung
Zuerst habe ich versucht, mit Python3 zu reproduzieren, und beim Ausführen von „python3 scratch.py“ und „python3 -m cProfile scratch.py“ wird jeweils -20,7933 ausgegeben ...auf meiner Maschine.
Der Grund, warum -20.0 in Python 2.x zurückgegeben wird, liegt darin, dass der Divisionsoperator „/“ in Python2.x anders funktionierte (http://legacy.python.org/dev/peps/pep-0238/)
In Python2 ist 1/12 == 0
In Python3 ist 1/12 == 0,08333333 ....
Das bedeutet, dass in Python2 die Zeile
bank.append( bank[t-1] * (1+0.05)**(1/12)
vereinfacht zu
bank.append( bank[t-1] * (1+0.05)**(0)
oder
bank.append( bank[t-1] * 1
Das ist wahrscheinlich nicht das, was Sie beabsichtigt haben.Die Python3-Interpretation ist wahrscheinlich korrekt und die Python2-Interpretation ist ziemlich nutzlos.Als Randbemerkung: Die Änderung von (1/12) in (1.0/12) führt zu einer identischen Ausgabe auf Python2 und Python3 und führt dazu, dass Ihr Code mit oder ohne Profilerstellung dieselbe Ausgabe zurückgibt, aber das behandelt das Symptom und nicht die Ursache .
Meine beste Vermutung, warum Sie die unterschiedlichen Ausgaben mit und ohne Profilerstellung erhalten, ist, dass Sie Python3 ohne Profilerstellung und Python2 mit Profilerstellung verwenden.Um aussagekräftige Ergebnisse zu erhalten, ist es wichtig, beim Ausführen Ihres Codes mit und ohne Profilerstellung dieselbe Python-Version zu verwenden.
Die Tatsache, dass Sie ./scratch.py verwenden, weist darauf hin, dass Sie wahrscheinlich eine Zeile wie
#!/usr/bin/python3
oben in scratch.py (obwohl es nicht im bereitgestellten Code enthalten ist).Wenn du rennst
./scratch.py
/usr/bin/python3 wird zum Ausführen der Datei verwendet
Wenn du rennst
python -m cProfile scratch.py
Ihr Standard-Python-Interpreter wird zum Ausführen der Datei verwendet (ich vermute, dass es sich um Python2 handelt).
Wenn Sie „python“ über die Befehlszeile ausführen (ohne weitere Argumente), werden Sie wahrscheinlich feststellen, dass der Standardinterpreter 2.X ist.
Daher sollte es so einfach sein, Ihren Code dazu zu bringen, mit und ohne Profilierung eine identische Ausgabe zurückzugeben, indem Sie bei der Profilierung einfach python3 angeben:
python3 -m cProfile scratch.py