Python递归与int不同于列表?

时间:2014-10-28 20:40:37

标签: python recursion

有点蟒蛇新手,所以请耐心等待我。我在python中有两个代码片段:

def summer(step, track, lst):
    if track<10:
        lst.append(1)
        summer(step,track+step,lst)
    return lst

调用summer(1,1,[])返回

[1, 1, 1, 1, 1, 1, 1, 1, 1]

现在我要说:

def summer(step, track):
    if track<10:
        track +=step
        summer(step,track)
    return track

调用summer(1,1)返回2

注意这两个片段之间的相似性,在第一个片段中,我将附加到列表中,最后返回该列表。它按预期工作。

在第二个中,我改为增加变量,但最后当我返回它时,它不能按预期工作(当它应该返回11时返回2)。我理解这是因为在第二个片段中我递归调用

summer(step,track)

我什么时候该说

return summer(step,track)

但为什么在第一个片段中,我可以简单地调用

summer(step,track+step,lst)

而不是

return summer(step,track+step,lst)

它仍然有效吗?即,跟踪列表和递归中的int似乎有不同的处理方式?

3 个答案:

答案 0 :(得分:6)

不同之处在于列表是可变的,而int则不是。这条线

lst.append(1)

修改lst引用的对象。这条线

track += step

track = track + step相同,因为track指的是不可变对象(int)。因此,本地名称track只是设置为一个新对象,而不是以某种方式修改作为参数传递给summer()的对象。

虽然+= 打算是一个就地操作符,但没有严格要求它是这样的。 int.__iadd__正好返回一个新的int对象,因为int是不可变的,因此无法对其进行修改。

答案 1 :(得分:1)

因此list不作为值传递,而int则是。{{1}}。所以你正在修改递归调用中的实际列表,而调用之前的int与递归调用中修改的int不同。

答案 2 :(得分:0)

@chepner已经提供了一个很好的解释来证明可观察行为的差异。另一方面,我想谈谈你可以使用的两种模式,这些模式将来在使用可变/ im-mutable变量时会留下更少的混淆。

>>> def summer(step, track, lst):
    if track<10:
        return summer(step,track+step,lst + [1])
    else:
        # Trivial Case
        return lst

>>> summer(1,1,[])
[1, 1, 1, 1, 1, 1, 1, 1, 1]
>>> 
>>> def summer(step, track):
    if track<10:
        return summer(step,track + step)
    else:
        # Trivial Case
        return track

>>> summer (1,1)
10

正如您所看到的,两种方法看起来非常相似,对可变对象和不可变对象没有单独处理,并且准确地返回结果。

因此,需要注意的重要事项是,以递归模式,

  1. 如果函数最终应该返回一个值,则永远不会忽略函数调用的返回值。
  2. 永远不要将琐碎的事情与非平凡的案件混在一起,并明确区分
  3. 如果修改变量,请不要指望该值会向下渗透到调用者。这种行为模式很难调试和分析