初學者python筆記(裝飾器、高階函數、閉包)

一個函數被定義完成後,甚至程序發佈後,後期可能需要添加某些功能,但是我們不可能每次都去修改原函數的代碼,這時候裝飾器就可以上場了,本篇文章將會用一個個可實現的代碼,由淺入深、循序漸進得闡述裝飾器的強大之處

裝飾器定義、作用、原則:

  1. 定義: 一個裝飾函數的函數,即爲其他函數添加附加功能
  2. 作用: 給函數添加新功能,但是原來的函數代碼不會被更改
  3. 原則: 不修改被修飾函數的源代碼,不修改被修飾函數的調用方式

裝飾器=高階函數+函數嵌套+閉包

  1. 高階函數: 函數接收的參數是一個函數名 或者 函數的返回值是一個函數名
  2. 未使用裝飾器:
import time
def test(s):
    start_time = time.time()
    res = 0
    for i in s:
        time.sleep(0.1)  #沉睡10秒
        res += i
    stop_time = time.time()
    print('函數的運行時間是{}秒'.format(stop_time - start_time))
    return res
print(test(range(100)))  #運行函數,傳入一個從0到100的範圍

運行結果:
函數的運行時間是10.012572765350342秒
4950

  1. 高階函數:
import time
def foo():
    time.sleep(3)
    print('大海如此寬廣,總有一天你會遇見真心的夥伴')
def test(func):  #傳入的參數是一個函數,即爲高階函數
    print(func)  #輸出函數的內存地址
    func()  #調用形參函數
test(foo)  #調用高階函數

運行結果:
<function foo at 0x02BF7B70>
大海如此寬廣,總有一天你會遇見真心的夥伴

  1. 帶修飾的高階函數(不修改原函數源代碼):
import time
def foo():
    time.sleep(3)
    print('大海如此寬廣,總有一天你會遇見真心的夥伴')
def test(func):  #傳入的參數是一個函數,即爲高階函數
    start_time = time.time()
    print(func)  #輸出函數的內存地址
    func()  #調用形參函數
    stop_time = time.time()
    print('函數運行時間爲 %s' %(stop_time - start_time))
test(foo)  #調用高階函數

運行結果:
<function foo at 0x004DC5D0>
大海如此寬廣,總有一天你會遇見真心的夥伴
函數運行時間爲 3.0281732082366943

  1. 帶修飾的高階函數(不修改原函數調用方式):
import time
def foo():
    time.sleep(3)
    print('大海如此寬廣,總有一天你會遇見真心的夥伴')
def test(func):  #傳入的參數是一個函數,即爲高階函數
    return func  #函數的返回值爲函數名,也稱爲高階函數
foo = test(foo)  #用foo接收test運行的返回值
foo()

運行結果:
大海如此寬廣,總有一天你會遇見真心的夥伴

  1. 不修改源代碼也不修改調用方式:
import time
def foo():
    time.sleep(3)
    print('大海如此寬廣,總有一天你會遇見真心的夥伴')
def timmer(func):  #作爲一個修飾函數
    start_time = time.time()
    func()
    stop_time = time.time()
    print('函數運行時間爲 %s' %(stop_time - start_time))
    return func  #函數的返回值爲函數名,也稱爲高階函數
foo = timmer(foo)  #將原函數名作爲接收的變量,被修飾後,就不會修改原函數的調用方式
foo()
#這樣調用後,其實是會連帶被修飾的結果一起輸出,因爲上一句賦值時已經調用了修飾函數

運行結果:
大海如此寬廣,總有一天你會遇見真心的夥伴
函數運行時間爲 3.018172264099121
大海如此寬廣,總有一天你會遇見真心的夥伴

但是這樣,它僅僅用了高階函數,還是滿足不了裝飾器的功能,因爲它多運行了一步

  1. 函數嵌套:
    一個函數裏定義多個函數(這裏需要用到函數作用域和函數嵌套的知識,在我的這篇文章裏有從淺入深的詳細分析:初學者python筆記(函數)
def father(name):
    #name = '白鬍子_1'  #三個地方對局部變量的修改
    def son():
        #name = '白鬍子_2'
        def grandson():
            #name = '白鬍子_3'
            print('我的老爹是{}'.format(name))  #離它最近的局部變量的修改
        grandson()  
    son()  #運行嵌套函數
father('白鬍子')
#裏面的變量是一個一個層級的關係,從形參,可以一直傳到底層的函數中再調用

運行結果:
我的老爹是白鬍子

  1. 基本裝飾器的實現(帶語法糖):
import time
def timmer(func):
    def wrap():
        start_time = time.time()  #給裝飾器加的功能
        func()  #在裝飾器中運行函數
        stop_time = time.time()
        print('函數運行了{}秒'.format(stop_time - start_time))
    return wrap  #函數的返回值是函數,所以爲高階函數
@timmer  #語法糖的使用,使原函數即foo函數被裝飾。相當於foo = timmer(foo)語句
def foo():
    time.sleep(3)
    print('test函數運行完畢!')
    
foo()  #運行原函數,方式不變,滿足原則

運行結果:
test函數運行完畢!
函數運行了3.0251729488372803秒

  1. 能得到原函數返回值的裝飾器:
import time
def timmer(func):
    def wrap():
        start_time = time.time()  #給裝飾器加的功能
        res = func()  #在裝飾器中運行函數,並且用一個變量來接收以返回原函數的返回值
        stop_time = time.time()
        print('函數運行了{}秒'.format(stop_time - start_time))
        return res  #返回原函數的返回值
    return wrap  #函數的返回值是函數,所以爲高階函數
@timmer  #語法糖的使用,使原函數即foo函數被裝飾。相當於foo = timmer(foo)語句
def foo():
    time.sleep(3)
    print('test函數運行完畢!')
    return '這是test的返回值'  #爲了實現把原函數的返回值也接收到
    
foo()  #運行原函數,方式不變,滿足原則
print(foo())  #輸出原函數的返回值

運行結果:
test函數運行完畢!
函數運行了3.0211730003356934秒
test函數運行完畢!
函數運行了3.0581750869750977秒
這是test的返回值

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章