【问题标题】:Behavior of python method in absence of return statement [duplicate]没有返回语句的python方法的行为[重复]
【发布时间】:2014-03-29 11:48:24
【问题描述】:

我有一个与程序行为变化有关的问题 return 语句的缺失导致在 python 方法中。

下面的 count 方法打印给定整数中的位数。 使用下面的代码块,我得到的结果为 4,即 预期的结果。

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

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

结果: 计数为 4

如果我修改上述方法,使最后一条语句不包含 'return' 语句我得到的结果是'None'。

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

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

结果:计数为零

(我使用的Python版本是:2.7.3)

上述行为是否是由于 Python 没有进行尾调用优化,还是涉及其他任何原因?

perl 中的类似代码块(AFAIK 不进行尾调用优化)提供 最后一条语句中没有“return”的预期结果。

sub counter {
    my ($n,$acc) = @_;
    return $acc if ($n==0);
    counter(int($n/10), $acc+1);
}
print "Count is:" . counter(1234,0) ."\n"

结果:计数为:4

(我在代码块上面运行的 Perl 版本是:5.14.4 和 5.8.5)。

我的问题是:

  • 尾调用优化是否是上述 Python 代码块中显示的行为的原因。
  • 如果是这种情况,那么为什么 perl 代码的行为会有所不同,这也不会造成 TCO。

【问题讨论】:

标签: python perl


【解决方案1】:

根本与缺少尾部优化无关。 Python 中的函数需要显式地使用关键字return,否则假定它们返回None

我知道 Ruby 不会那样做,它会返回最后执行的表达式的值。对于 Perl,它必须是相同的。

没什么聪明的,只是 Python 程序的行为方式是这样的 :)

查看这两个 Python 函数的反汇编。您可能会看到具有return 值的那个实际上是如何调用函数并返回堆栈顶部的值。没有的,看函数调用后有两条指令,加载常量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

【讨论】:

  • 我会告诉 OP,没有返回语句类似于没有参数的“返回”。通过类比其他语言,可能更容易看到结果。
  • @sabbahillel 这也是一种看的方式,谢谢指出!
【解决方案2】:

没有return,只有当acc == 0时你返回一些东西;对于您的 count 递归方法的任何其他调用,您不会返回任何内容,因此收到了 None

与其他语言(scala,显然还有 perl,可能还有其他语言)不同,python 默认不返回最后一条语句,您必须显式调用 return

【讨论】:

    【解决方案3】:

    尾调用消除不会妨碍 Perl 的语义(尽管没有 Perl 语义,尾调用会少得多,因为每个函数都会隐含地以 return; 结尾)。这纯粹是一种语言设计选择。 Perl 和 Python 不同的原因在于它们不是由同一个人设计的。

    返回值的语法因选择而异。 Python 的设计者选择要求明确的return 语句。在 QBasic 中,您必须分配值以返回与函数同名的变量。

    【讨论】:

      【解决方案4】:

      没有return 语句的Python 函数返回值None

      在这种情况下,您只在x == 0 时返回一个值,因此您得到的None。查看 Python 教程了解更多信息:http://docs.python.org/2/tutorial/controlflow.html#defining-functions

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-04-22
        • 1970-01-01
        • 1970-01-01
        • 2014-06-06
        • 2020-10-30
        • 2017-12-09
        • 2016-08-21
        • 1970-01-01
        相关资源
        最近更新 更多