为啥 cProfile 会导致函数返回不同的值?

Posted

技术标签:

【中文标题】为啥 cProfile 会导致函数返回不同的值?【英文标题】:Why does cProfile cause functions to return different values?为什么 cProfile 会导致函数返回不同的值? 【发布时间】:2014-04-13 11:03:25 【问题描述】:

我正在用 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$ 

是否有一个简单的解释来解释为什么每次都会输出不同的答案?我已经阅读了手册,但我没有看到任何明显的内容。

【问题讨论】:

不是答案,但我在 Windows 中的 Python 2.7.3 中尝试了您的代码,无论是否进行分析,它确实给出了相同的结果 (-20.0)。 嗯。神秘!好的,在这里用 2.7 运行它,两种方式都得到 20.0。 【参考方案1】:

首先,我尝试使用 python3 进行复制,并在我的机器上运行“python3 scratch.py​​”和“python3 -m cProfile scratch.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

【讨论】:

哦,天哪,我不敢相信我没有看到。非常感谢你指出我的愚蠢。

以上是关于为啥 cProfile 会导致函数返回不同的值?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 cProfile 只运行一次代码?

为啥当我让这个函数返回它的值时,函数会无限重复自己?

从 Excel 调用的访问 vba 函数导致返回不同的值

为啥我的脚本字典函数在 access vba 中只返回键?

如何省略 cProfile 中的方法

为啥在构造函数中抛出异常会导致空引用?