我正在玩递归,最后得到了这个:
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
答案 0 :(得分:5)
p是一个局部变量,与func总是具有相同的id这一事实无关。每次调用func都会创建一个带有新局部变量实例的新堆栈框架
答案 1 :(得分:1)
您似乎对函数和局部变量的工作方式存在误解。您是正确的f
始终是同一个对象,但这并不意味着p
在您继续调用f
时保持其值。局部变量是函数的一个特定执行的本地变量,而不是函数本身。
采取这样一个简单的函数:
def plus1(x):
y = x + 1
return y
plus1
不会“包含”x
或y
的值。如果确实如此,在我调用函数之前它会有什么价值?相反,定义plus1
的数据是一组指令,用于在为x
指定值时执行的操作。它只包含x
作为引用参数值的方式(尚未给出它),y
作为引用它将在执行期间创建的值的方式。
当您实际拨打plus1(5)
时,plus1
的代码会在x
绑定到5
的情况下执行。但是该绑定仅在函数的特定调用内部相关,并且一旦调用完成,则该值被丢弃。在任何给定时间,当前正在执行的函数可能有0,1或任何其他数量的调用,并且每个调用都有自己的局部变量绑定。
由于你的函数自己调用(间接),这实际上发生在你的程序中。在致电func
之前,存在p
的0个“版本”。然后有1个,2个,3个,4个,5个,最后是6个版本(第6个版本从未打印过,因为func
在n > 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(...)
...