This question already has answers here:
Python function returning None after recursion
(3个答案)
5年前关闭。
I have a question related to change in program behavior


通过下面的代码块,我得到的结果是4,即

def count(x,acc=0):
    if x==0:
        return acc
    return count(x/10,acc+1)

print "Count is %s" %(count(1234))

Result: Count is 4
If I modify the above method so that the last statement does not contain
“return”语句我得到的结果是“None”。
def count(x,acc=0):
    if x==0:
        return acc
    count(x/10,acc+1)

print "Count is %s" %(count(1234))

结果:Count为None
(The version of Python I used is: 2.7.3)
上述行为是否是由于Python没有进行尾调用优化,或者是否涉及到任何其他推理?
A similar chunk of code in perl (which AFAIK doesn't do tail call optimization) provides
未将“return”作为最后一条语句的一部分的预期结果。
sub counter {
    my ($n,$acc) = @_;
    return $acc if ($n==0);
    counter(int($n/10), $acc+1);
}
print "Count is:" . counter(1234,0) ."\n"

Result: Count is:4

我的问题是:
尾部调用优化是上面一段Python代码中显示的行为的原因。
如果是这样的话,那么perl代码的行为为什么不同,这也不影响TCO。

最佳答案

与尾部优化的缺失完全没有关系。Python中的函数显式地需要关键字return,否则将假定它们返回None
我知道Ruby的行为不是这样的,它返回最后执行的表达式的值。Perl必须是相同的。
这没什么聪明的,只是Python程序的行为方式:)
请参见这两个Python函数的反汇编。您可以看到具有return值的函数如何实际调用函数并返回堆栈顶部的值。没有它的那个,在funciont调用后有两条指令,加载常量None并返回它。

def count(x,acc=0):
    if x==0:
        return acc
    return count(x/10,acc+1)

def count2(x,acc=0):
    if x==0:
        return acc
    count(x/10,acc+1)

In [7]: import dis
In [8]: dis.dis(count)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               1 (0)
              6 COMPARE_OP               2 (==)
              9 POP_JUMP_IF_FALSE       16

  3          12 LOAD_FAST                1 (acc)
             15 RETURN_VALUE

  4     >>   16 LOAD_GLOBAL              0 (count)
             19 LOAD_FAST                0 (x)
             22 LOAD_CONST               2 (10)
             25 BINARY_DIVIDE
             26 LOAD_FAST                1 (acc)
             29 LOAD_CONST               3 (1)
             32 BINARY_ADD
             33 CALL_FUNCTION            2
             36 RETURN_VALUE

In [9]: dis.dis(count2)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               1 (0)
              6 COMPARE_OP               2 (==)
              9 POP_JUMP_IF_FALSE       16

  3          12 LOAD_FAST                1 (acc)
             15 RETURN_VALUE

  4     >>   16 LOAD_GLOBAL              0 (count)
             19 LOAD_FAST                0 (x)
             22 LOAD_CONST               2 (10)
             25 BINARY_DIVIDE
             26 LOAD_FAST                1 (acc)
             29 LOAD_CONST               3 (1)
             32 BINARY_ADD
             33 CALL_FUNCTION            2
             36 POP_TOP
             37 LOAD_CONST               0 (None)
             40 RETURN_VALUE

10-01 01:33
查看更多