为什么 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 进行重现,并运行“python3 scrap.py”和“python3 -m cProfile scrap.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
在 scrap.py 的顶部(尽管它不包含在提供的代码中)。当你跑步时
./scratch.py
/usr/bin/python3 用于执行该文件
当你跑步时
python -m cProfile scratch.py
你的默认 python 解释器用于执行该文件(我猜是 python2)
如果您从命令行运行“python”(没有任何其他参数),您可能会看到默认解释器是 2.X。
因此,让您的代码在进行分析和不进行分析时返回相同的输出应该像在分析时指定 python3 一样简单:
python3 -m cProfile scratch.py