2013-07-28 22 views
1

林去遞歸玩,並結束了與此:爲什麼p從來沒有超過2

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

因爲在你函數的一行上你做'p = 1' .. – hetepeperfan

+0

@het:我也有一條直線n + = 1和一條直線p + = 1,但p從未達到2以上,而n達到6以上。 – jason

+0

這是什麼貸款說。 'p'是一個局部變量,'n'相反是一個全局變量。每次函數運行p都將「重置」,「n」,全局不會重置。 – hetepeperfan

回答

5

p是一個局部變量和無關的事實,FUNC總是具有相同的ID。每次調用函數都會創建一個新的帶有局部變量實例的堆棧幀

+0

所以,愚蠢的跟進,功能如何跟蹤?我認爲如果某件東西具有與其他東西相同的標識,那麼它本質上是另一回事。必須有一些我不知道的內部記錄。 – jason

0

您可以做某事來改變p。例如,使用p作爲函數中的全局變量。

p = 0 

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

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

看來您對功能和局部變量是如何工作的誤解。你是正確的,f始終是相同的對象,但這並不意味着當你繼續呼叫fp保持其價值。局部變量對於函數的一個特定執行是本地的,而不是函數本身。

舉一個簡單的功能是這樣的:

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應該從哪裏開始?

+0

>在任何給定的時間,可能有0,1或任何其他數量的對當前正在執行的函數的調用,並且每個調用都有其自己的局部變量綁定。這對我來說是新的!謝謝。 – jason

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實例作爲參數,但這對於遞歸的基本理解並不重要。)

相關問題