为什么p永远不会超过2

时间:2013-07-28 22:04:05

标签: python python-3.x

我正在玩递归,最后得到了这个:

n = 0

def func(f) :
    print("setting p to 1")  #NEW#
    p = 1 #local var
    global n
    n+=1
    print(n)
    if n > 5 :
        print("returning")
        return
    print("calling f(f) with id() of " + str(id(f)))
    f(f)
    print("incrementing p") #NEW#
    p +=1
    print("p = " + str(p))

print(str(id(func)))
func(func)

好吧我的问题是,如果f始终是相同的id(它是),因此总是相同的对象(我打赌那些我错误的地方),不应该是相同的p,因此增加超过2?相反,它将每个p视为一个不同的对象本地。

输出:

178374636
1
calling f(f) with id() of 178374636
2
calling f(f) with id() of 178374636
3
calling f(f) with id() of 178374636
4
calling f(f) with id() of 178374636
5
calling f(f) with id() of 178374636
6
returning
p = 2
p = 2
p = 2
p = 2
p = 2

新评论的新输出

178374572
setting p to 1
1
calling f(f) with id() of 178374572
setting p to 1
2
calling f(f) with id() of 178374572
setting p to 1
3
calling f(f) with id() of 178374572
setting p to 1
4
calling f(f) with id() of 178374572
setting p to 1
5
calling f(f) with id() of 178374572
setting p to 1
6
returning
incrementing p
p = 2
incrementing p
p = 2
incrementing p
p = 2
incrementing p
p = 2
incrementing p
p = 2

4 个答案:

答案 0 :(得分:5)

p是一个局部变量,与func总是具有相同的id这一事实无关。每次调用func都会创建一个带有新局部变量实例的新堆栈框架

答案 1 :(得分:1)

您似乎对函数和局部变量的工作方式存在误解。您是正确的f始终是同一个对象,但这并不意味着p在您继续调用f时保持其值。局部变量是函数的一个特定执行的本地变量,而不是函数本身。

采取这样一个简单的函数:

def plus1(x):
    y = x + 1
    return y

plus1不会“包含”xy的值。如果确实如此,在我调用函数之前它会有什么价值?相反,定义plus1的数据是一组指令,用于在为x指定值时执行的操作。它只包含x作为引用参数值的方式(尚未给出它),y作为引用它将在执行期间创建的值的方式。

当您实际拨打plus1(5)时,plus1的代码会在x绑定到5的情况下执行。但是该绑定仅在函数的特定调用内部相关,并且一旦调用完成,则该值被丢弃。在任何给定时间,当前正在执行的函数可能有0,1或任何其他数量的调用,并且每个调用都有自己的局部变量绑定。

由于你的函数自己调用(间接),这实际上发生在你的程序中。在致电func之前,存在p的0个“版本”。然后有1个,2个,3个,4个,5个,最后是6个版本(第6个版本从未打印过,因为funcn > 5时返回)。然后这会回落到5,4,3,2,1,0版本。

这就是局部变量的工作原理,以及为什么Python抱怨你必须先分配一个局部变量才能读出它。在特定调用之外,询问p的值是没有意义的,因为可能有零个或多个值可能被称为p。这意味着调用func也无法从p开始,因为其他调用已经采取了行动,因为它应该从p开始?

答案 2 :(得分:1)

我认为你对递归有些困惑。递归函数是一个自我调用的函数。你的示例函数调用它的参数f,这意味着它只是递归,如果它自己作为f传递。

这是一个真正的递归函数:

def recursive(arg):
    if arg <= 0:
        return "base case"
    else:
        return "recursive({}) returned <{}>".format(arg-1, recursive(arg-1))

示例输出:

>>> recursive(0)
'base case'
>>> recursive(3)
'recursive(2) returned <recursive(1) returned <recursive(0) returned <base case>>>'

正如您在该示例中所看到的,您总是需要一个函数不会递归的基本情况,或者您永远不会结束。

通过修改每次调用中传递的参数,可以在递归调用链上“传递”信息。可以通过修改递归调用的返回值来“向下”传递信息,以便创建自己的返回值。

通常,函数调用永远不会修改调用函数中的局部变量(有几种方法可以,但它们并不常见)。对于递归调用,这意味着函数的每次调用都有自己的每个局部变量的版本。函数参数是局部变量,因此它们对于每个调用也是唯一的(并且可以彼此独立地修改)。

def recursive_vars(arg):
    loc = 10 # a local variable

    print("initial values of local variables are: arg = {}, loc = {}".format(arg, loc))

    if arg == 0:
        print("arg is zero, so this is the base case. Returning without recusing!")
        return

    print("decrementing arg and loc by one each")
    arg -= 1
    loc -= 1

    print("before recursion, local variables are: arg = {}, loc = {}".format(arg, loc))

    print("recursing")
    recursive_vars(arg)

    print("after recursion, local variables are: arg = {}, loc = {}".format(arg, loc))

    print("done")

输出:

>>> recursive_vars(0)
initial values of local variables are: arg = 0, loc = 10
arg is zero, so this is the base case. Returning without recusing!
>>> recursive_vars(3)
initial values of local variables are: arg = 3, loc = 10
decrementing arg and loc by one each
before recursion, local variables are: arg = 2, loc = 9
recursing
initial values of local variables are: arg = 2, loc = 10
decrementing arg and loc by one each
before recursion, local variables are: arg = 1, loc = 9
recursing
initial values of local variables are: arg = 1, loc = 10
decrementing arg and loc by one each
before recursion, local variables are: arg = 0, loc = 9
recursing
initial values of local variables are: arg = 0, loc = 10
arg is zero, so this is the base case. Returning without recusing!
after recursion, local variables are: arg = 0, loc = 9
done
after recursion, local variables are: arg = 1, loc = 9
done
after recursion, local variables are: arg = 2, loc = 9
done

如果根据递归的深度缩进,那么输出的最后一部分会是什么样子:

initial values of local variables are: arg = 3, loc = 10
decrementing arg and loc by one each
before recursion, local variables are: arg = 2, loc = 9
recursing
  initial values of local variables are: arg = 2, loc = 10
  decrementing arg and loc by one each
  before recursion, local variables are: arg = 1, loc = 9
  recursing
    initial values of local variables are: arg = 1, loc = 10
    decrementing arg and loc by one each
    before recursion, local variables are: arg = 0, loc = 9
    recursing
      initial values of local variables are: arg = 0, loc = 10
      arg is zero, so this is the base case. Returning without recusing!
    after recursion, local variables are: arg = 0, loc = 9
    done
  after recursion, local variables are: arg = 1, loc = 9
  done
after recursion, local variables are: arg = 2, loc = 9
done

如您所见,在每种情况下,每个层中的局部变量在递归调用的两侧都具有相同的值。因为arg变量作为参数传递,它看起来像是在调用之间共享,但这是一种错觉。正如您在函数调用unwind时所看到的那样,外部调用没有通过内部调用修改其arg变量的值。 (如果你传递可变对象,例如list个实例作为参数,事情就会复杂一些,但这对于递归的基本理解并不重要。)

答案 3 :(得分:0)

你可能会喜欢改变p。例如,在函数中使用p作为全局变量。

p = 0

def my_func(...):
   ...
   p +=1 
   ...
   return p

p = my_func(...)
p = my_func(...)
...