您好,登錄后才能下訂單哦!
這篇文章主要介紹“Python閉包與裝飾器怎么定義”,在日常操作中,相信很多人在Python閉包與裝飾器怎么定義問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Python閉包與裝飾器怎么定義”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
在學習裝飾器前,需要先了解閉包的概念。形成閉包的要點:
函數嵌套
將內部函數作為外部函數的返回值
內部函數必須要使用到外部函數的變量
下面以一個計算列表平均值的案例來講解閉包:
def make_average(): # 創建一個列表,用來保存數值 nums = [] # 定義一個內部函數,用來計算列表的平均值 def average(n): # 將數值添加到列表中 nums.append(n) # 返回平均值 return sum(nums) / len(nums) return average
首先,定義一個函數make_average;
其次,在make_average函數內定義一個空列表,用來存儲數值;
再次,定義一個內部函數,用來計算列表平均值;
最后,將這個內函數作為外函數make_average的返回值,注意不要加( ),加( )就變成調用這個函數了。
# 調用外部函數,并將其復制給一個變量,注意:此時返回的是內函數的內存地址 a = make_average() # 給這個變量加(),就相當于調用了內函數average print(a(20)) print(a(30))
運行結果如下:當傳入的數值為20時,列表中只有一個數,所以計算結果是20;當再傳入一個數值30時,此時列表中就有兩個數20和30,所以平均值的計算結果是25.
例如,有以下兩個函數,分別計算兩個數的和以及成績:
def add(a, b): """計算兩數之和""" res = a + b return res def mul(a, b): """計算兩數之積""" res = a * b return res
現在有個需求:我想要在每個函數的計算開始前打印“開始計算...”,在計算結束后打印“計算結束...”。我們可以通過直接修改函數代碼的方式來滿足這個需求,但這樣會面臨以下問題:
如果要修改的函數過多,十個甚至一百個函數,未免不現實;
不便于后期維護,例如我不想打印“開始計算...”了,而是要打印“begin...”,豈不是又要重新修改一遍;
違反開閉原則(OCP),即程序的設計,要求開放對程序的擴展、關閉對程序的修改;
所以,上述直接修改函數代碼的方式不可行。我們希望在不修改原函數的情況下,實現對函數的擴展。例如:
def new_add(a, b): print("開始計算...") r = add(a, b) print("計算結束...") return r print(new_add(22, 33))
執行結果如下:
這種新創建一個函數的方式雖然沒有修改原函數,但面臨一個很嚴重的問題:
只能擴展指定函數,不能通用于其它函數,例如擴展上述的add函數,而不能擴展mul函數,如果想要擴展mul函數,只能再創建一個擴展函數;
因為,我們希望可以定義一個通用的擴展函數,可以作用域所有函數。這類不改變原函數代碼的通用函數就是:裝飾器。
裝飾器本質上是一個python函數或類,它可以讓其他函數或類在不需要做任何代碼修改的前提下增加額外功能,也就是為已經存在的對象添加額外功能,裝飾器的返回值也是一個函數/類對象。它經常用于有切面需求的場景,比如:插入日志、性能測試、事務處理、緩存、權限校驗等場景。
例如:
def wrapper_info(func): def inner(): print("開始介紹...") res = func() print("介紹結束...") return res return inner def introduce1(): print("我是周潤發,我來自HONG KONG") info = wrapper_info(introduce1) info()
運行結果如下:
可見,在沒有改變原函數代碼的情況下,即給原函數增加了一些額外的功能,func是要修飾的函數,作為一個變量傳入裝飾函數,能夠通用于其他函數,這個wrapper_info就是裝飾器。但目前面臨的問題是,被裝飾函數如果帶參數怎么辦?例如:
def introduce2(name, age): print(f"我叫{name}, 我今年{age}歲了")
盡管可以在裝飾器wrapper_info中傳入name、age,但并不是每個被裝飾的函數都只有name、age,亦或是指定類型的參數,還有可能傳入的是字典、列表、元組等。也就是傳入參數的類型和數量不固定怎么辦?
此時就需要用到不定長參數:(*args, **kwargs)
def wrapper_info(func): """ 用來對其他函數進行擴展,使其他函數可以在執行前做一些額外的動作 :param func: 要擴展的函數對象 :return: """ def inner(*args, **kwargs): print("開始介紹...") res = func(*args, **kwargs) print("介紹結束...") return res return inner
例如:
def introduce3(name, age, city): print(f"我叫{name}, 我今年{age}歲了, 我來自{city}")
運行結果如下:
上述提到的是裝飾器,一種是應用于被裝飾的函數不帶參數,一種是被裝飾的函數帶參數,那裝飾器本身能否帶參數呢?比如我定義一個變量,想通過傳入不同的值來控制這個裝飾器實現不同的功能。答案是肯定的,例如:
def use_log(level): def decorator(func): def inner(*args, **kwargs): if level == "warn": logging.warning("%s is running by warning" % func.__name__) elif level == "info": logging.warning("%s is running by info" % func.__name__) else: logging.warning("%s is running by other" % func.__name__) return func(*args, **kwargs) return inner return decorator def introduce4(name, age, city): print(f"我叫{name}, 我今年{age}歲了, 我來自{city}") info1 = use_log(introduce4('周星馳', 28, '香港')) info1('info') info2 = use_log(introduce4('周潤發', 28, '香港')) info2('warn') info3 = use_log(introduce4('成龍', 28, '香港')) info3('xxx')
運行結果如下:
info3 = wrapper_info(introduce3) info3('劉德華', 28, '香港')
如果是裝飾器函數帶參數,則調用方式為:
info4 = use_log(introduce4('周星馳', 28, '香港')) info4('info')
即在被裝飾函數上方以@符號進行修飾
@wrapper_info def introduce3(name, age, city): print(f"我叫{name}, 我今年{age}歲了, 我來自{city}") introduce3('劉德華', 28, '香港')
如果是裝飾器函數帶參數,例如上述的use_log,則需要在裝飾器中傳入參數:
@use_log('info') def introduce4(name, age, city): print(f"我叫{name}, 我今年{age}歲了, 我來自{city}")
到此,關于“Python閉包與裝飾器怎么定義”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。