Python3之從遞迴到閉包再到裝飾器

geek_xiong發表於2018-09-02

遞迴

遞迴指的是函式直接或間接的呼叫自身。
有一則故事:
從前有座山,山裡有座廟,廟裡有個老和尚,老和尚在給小和尚講故事,故事的內容是:從前有座山,山裡有座廟。。。。

可能圖片更直觀,相信都見過類似的圖片吧
這裡寫圖片描述

說明:

  • 遞迴一定要控制遞迴的次數,當符合某一條件時要終止遞迴呼叫
  • 幾乎所有的遞迴都能用while迴圈來代替

遞迴的優缺點:
優點:可以把問題簡單化,讓思路更加清晰,程式碼更簡潔
缺點:遞迴因系統環境影響大,當遞迴深度太大時,可能會得到不可預知的結果

遞迴呼叫分為兩個階段:
遞推階段:從原問題出發,按遞迴公式遞推,從未知到已知,最終達到遞迴終止條件
迴歸條件:按遞迴終止條件求出結果,逆向逐步帶入遞迴公式,迴歸到原問題求解

先舉個遞迴的栗子吧,我就喜歡舉例子,文字描述總不如栗子更容易理解,也正好了解一下在什麼請況下使用遞迴比較好吧

如老和尚講故事:

def f():
    print('從前有座山,山裡有座廟,廟裡有個老和尚講故事,講的是:')
    f()
f()

結果呢:
這裡寫圖片描述

這個就是沒有加限制此數,最後崩潰掉了

再如,用遞迴求階乘:

def myfac(n):
    if n == 1:   # 限制條件,當為 1 時終止遞迴
        return 1
    return n * myfac(n - 1)

print(myfac(5))  # 5 的階乘 120

遞推過程:
5! = 5 * 4!
5! = 5 * 4 * 3!
5! = 5 * 4 * 3 * 2!
5! = 5 * 4 * 3 * 2 * 1!
5! = 5 * 4 * 3 * 2 * 1
迴歸過程:
5! = 5 * 4 * 3 * 2
5! = 5 * 4 * 6
5! = 5 * 24
5! = 120

配個圖:
這裡寫圖片描述

閉包

什麼是閉包:

如果一個內嵌函式訪問了外部巢狀函式的變數,則這個內嵌函式就是閉包

閉包必須滿足三個條件:

  1. 必須是一個內嵌函式
  2. 內嵌函式必須引用外部函式的變數
  3. 外部函式返回值必須是內嵌函式

舉個栗子:
這裡寫圖片描述

分析以上程式碼:
pow2接收的返回值是什麼? —–> fn , 沒錯,就是fn,因為maker_power內部的 fn 是一個函式,但此時沒被呼叫,所以return 的 fn 就是一個函式名。強調:此函式雖然沒被呼叫,但是 y 是不會被釋放的。
此時的pow2實際上就是 fn 了
pow2(5) 在這時其實就是呼叫fn 函式了,並把 5 傳進去,即x = 5,上邊說的 有 y 還沒有被釋放,返回x 的 y 次方,即返回5^2=25

注意:fn 是函式名 fn()才是呼叫 fn 函式(即,函式名加括號才是呼叫)

再來個溫習一下:
這裡寫圖片描述

裝飾器

裝飾器是一個函式,傳入的是一個函式,返回的也是一個函式

例如:

def 裝飾器函式名(引數):
    語句塊
    return 函式物件

@裝飾器函式名
def 函式名(形參列表):
    語句塊

裝飾器的原理:
被裝飾函式的變數(函式名)繫結裝飾器函式呼叫後返回的函式

舉幾個例子:

def mydeco(fn):
    def fx():
        print('fx正在被呼叫')
    return fx

def myfun():
    print('myfun正在被呼叫')

myfun = mydeco(myfun)
myfun()  # 結果:fx正在被呼叫    為什麼不是myfun正在被呼叫?

先理解這個例子,
mydeco(myfun) 這句是呼叫mydeco函式,引數是一個函式myfun,即fn = myfun,返回fx,即接收的myfun=fx,
myfun()就是呼叫fx(),執行print(‘fx正在被呼叫’)這句話,所以列印的不是myfun正在被呼叫,跟他根本沒有關係。

另一個寫法:

def mydeco(fn):
    def fx():
        print('fx正在被呼叫')
    return fx

@mydeco
def myfun():
    print('myfun正在被呼叫')

# myfun = mydeco(myfun)
myfun() 

再深入一層講解裝飾器:
先看程式碼:

def mydeco(fn):
    def fx():
        print('這是被裝飾之前')
        fn()  # 呼叫被裝飾函式
        print('這是被裝飾之後')
    return fx

@mydeco
def myfun():
    print('myfun正在被呼叫')

myfun()

myfun() 再次講解這句話,加深理解上邊的栗子
myfun實際可以寫成兩句話:
myfun = mydeco(myfun)
myfun()
初步可認為是呼叫myfun函式,但是myfun有裝飾器,所以myfun會當做一個引數傳入裝飾器函式裡,即fn = myfun,呼叫mydeco函式,先執行print(‘這是被裝飾之前’)這就話,然後執行fn()函式,即呼叫myfun()函式,執行print(‘myfun正在被呼叫’)這句話,fn()函式執行完畢,繼續執行裝飾器函式,執行print(‘這是被裝飾之後’)這句話,
所以最後列印結果是:

這是被裝飾之前
myfun正在被呼叫
這是被裝飾之後

裝飾器應用:

def privieged_check(fn):
    def fx(name, x):
        print('正在進行驗證...')
        if True:
            fn(name, x)
        else:
            print('許可權驗證失敗')
    return fx

def message_send(fn):
    def fy(name, money):
        fn(name, money)
        print('正在傳送短息給', name)
    return fy

@message_send
@privieged_check
def savemoney(name, x):
    print(name, '存錢', x, '元')

savemoney('小張', 200)

注意:如果被裝飾的函式有多個裝飾器,優先執行最近的
執行結果:

正在進行驗證...
小張 存錢 200 元
正在傳送短息給 小張

執行過程:
這裡寫圖片描述

其實自己繞繞也就繞出來了!

願意和大家一起交流,huamuxiong_2018@163.com
本節圖有網上百度的,有助於理解,如果有侵權,還請告知,我會刪除,謝謝

相關文章