2017-08-10 59 views
-1

我有下面的代碼,我正在裝飾一個遞歸函數,但我無法理解代碼的某些部分。理解遞歸裝飾函數的困難

def deco_func(f): 
    def wrapper(*args): 
     print('Decorating', args) 
     res = f(*args) 
     print(res) 

    return wrapper 


@deco_func 
def fact(n): 
    if n == 0: 
     return 1 
    else: 
     return n * fact(n - 1) 

fact(4) 

注:

1. F中的包裝函數裏面實際上指向的事實功能的原始版本,因爲它來自於封閉的範圍,它的值是通過包裝關閉。

2.事實函數內部的遞歸調用調用包裝器的事實的裝飾版本。

所以根據我的理解,堆棧跟蹤將如下所示。

res = f(4) 
    = 4 * wrapper(3) = 4 * f(3) 
    = 4 * 3 * wrapper(2) = 4 * 3 * f(2) 
    = 4 * 3 * 2 * wrapper(1) = 4 * 3 * 2 * f(1) 
    = 4 * 3 * 2 * 1 * wrapper(0) = 4 * 3 * 2 * 1 * f(0) 

在這點n爲0,因此F(0)將返回1,這樣棧看上去可能會像下面

res = 4 * 3 * 2 * 1 * 1

所以res應該是24,但它的值是1我無法理解。此外,在這一點上,我得到一個異常

TypeError: unsupported operand type(s) for *: 'int' and 'NoneType'

但只要我在像return res包裝函數的底部添加一個return語句,一切工作正常。

我不明白爲什麼沒有返回正在創建一個問題在這裏,因爲在包裝函數控制內永遠不會超越res = f(*args)直到遞歸完成。因此,像f(0)這樣的最終調用將導致res = 5*4*3*2*1,並且如上所示,res應該評估爲24,然後控件應該轉到下一個語句。那麼,爲什麼不使用包裝返回語句創建問題時,回報將只執行一次res已計算

我知道我在這裏失去了一些重要的東西,但我無法從概念上弄清楚什麼出了問題,如果我們刪除返回語句。

+0

任何人都可以回覆!!!! – Rohit

回答

0

您原來的功能f返回一個數值。

您裝飾的功能沒有,這意味着它返回None。這就是爲什麼遞歸調用嘗試將乘以None,因此錯誤。

缺失的部分是return res

+0

我知道這裏是缺失的部分,但是我對棧幀的返回值的生成過程進行了解釋, – Rohit

+0

您能否幫助我理解相同的內容? – Rohit

+0

你的'wrapper',裝飾器應用的結果,現在取代'f'。如果'f'返回一個值,'wrapper'應該將它從調用中收到的值返回到原來的'f'。如果'wrapper'沒有返回值,則不能替代'f'。它不需要任何遞歸顯示。裝飾一個返回參數+1的簡單函數,並查看其差異。 – 9000

1

裝飾器取代的功能,所以當你指的factfact函數內部,你實際上指的是裝飾功能,這是wrapper。因此,wrapper對於每個遞歸級別都被調用一次,並且在展開返回值時,如果沒有顯式返回語句,則返回wrapper返回的值,即None

要正常工作,wrapper必須返回res值,以便遞歸正確展開。

def deco_func(f): 
    def wrapper(*args): 
     print('Decorating', args) 
     res = f(*args) # calls undecorated 'fact' 
     print(res) 
     return res # !!! so None is not returned implicitly 

    return wrapper 


@deco_func 
def fact(n): 
    if n == 0: 
     return 1 
    else: 
     return n * fact(n - 1) # calls 'wrapper' (aka decorated 'fact') 

fact(4) 
+0

我明白,但正如我張貼在我的問題,每個調用包裝將意味着這樣一個新的調用這樣的f(n-1),並且這繼續到f(0),在這一點事實(0)返回1,所以res = 4 * 3 * 2 * 1 * 1。所以不會返回來自事實(0),實際上是1. – Rohit

+0

@Rohit否,'fact(0)'返回'None',因爲正如我所說的,裝飾器*會替換函數。這是錯誤來自的地方。 – Frxstrem

+0

事實上,(0)會轉化爲包裝(0),是正確的?,現在通過包裝(0)將使調用像f(0)這將調用undercoated版本fact(0)並返回1 ..我認爲這是我的唯一當我們有n = 0的基本情況時,即使修飾fact(0)也不會返回任何結果。 – Rohit