1

我玩递归,结果是这样的:

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 不应该是相同的 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

4 回答 4

5

p 是一个局部变量,这与 func 始终具有相同的 id 的事实无关。对 func 的每次调用都将创建一个带有新的局部变量实例的新堆栈帧

于 2013-07-28T22:07:10.557 回答
1

您似乎对函数和局部变量的工作方式有误解。你是对的,f总是同一个对象,但这并不意味着p当你继续调用时它会保持它的价值f。局部变量是函数的特定执行的局部变量,而不是函数本身的局部变量。

采用这样的简单函数:

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

plus1不“包含” xor的值y。如果是这样,在我调用该函数之前它会有什么价值?相反,数据定义plus1是一组指令,当它被赋予一个值时要做什么x。它仅包含x作为引用参数值(尚未给出)y的一种方式,以及作为引用它将在执行期间创建的值的一种方式。

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

由于您的函数(间接)调用自身,这实际上发生在您的程序中。在您调用之前,func存在 0 个“版本” p。然后有 1、2、3、4、5,最后是 6 个版本(第 6 个版本从不打印,因为func在 时返回n > 5)。然后,这会下降到 5、4、3、2、1、0 版本。

这就是局部变量的工作原理,也是 Python 抱怨必须先分配局部变量才能读取它的原因。在特定调用之外,请求 的值是没有意义的p,因为可能有零个或多个值可能被调用p。这意味着调用func也不能从p其他调用已经执行的操作开始,因为它p应该从哪个开始?

于 2013-07-28T22:34:05.077 回答
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变量是作为参数传递的,所以看起来像是在调用之间共享,但这是一种错觉。正如您所看到的,当函数调用展开时,外部调用的arg变量值没有被内部调用修改。(如果您传递可变对象,例如作为参数的实例,事情会稍微复杂list一些,但这对于递归的基本理解并不重要。)

于 2013-07-28T23:37:31.037 回答
0

你可以做某事喜欢改变p。例如,p用作函数中的全局变量。

p = 0

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

p = my_func(...)
p = my_func(...)
...
于 2013-07-28T22:21:10.033 回答