日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Python裝飾器-閉包與函數(shù)裝飾器

一、閉包

在學(xué)習(xí)裝飾器前,需要先了解閉包的概念。形成閉包的要點:

  • 函數(shù)嵌套
  • 將內(nèi)部函數(shù)作為外部函數(shù)的返回值
  • 內(nèi)部函數(shù)必須要使用到外部函數(shù)的變量

下面以一個計算列表平均值的案例來講解閉包:

def make_average():
# 創(chuàng)建一個列表,用來保存數(shù)值
nums = []

# 定義一個內(nèi)部函數(shù),用來計算列表的平均值
def average(n):
# 將數(shù)值添加到列表中
nums.append(n)
# 返回平均值
return sum(nums) / len(nums)

return average
  1. 首先,定義一個函數(shù)make_average;
  2. 其次,在make_average函數(shù)內(nèi)定義一個空列表,用來存儲數(shù)值;
  3. 再次,定義一個內(nèi)部函數(shù),用來計算列表平均值;
  4. 最后,將這個內(nèi)函數(shù)作為外函數(shù)make_average的返回值,注意不要加( ),加( )就變成調(diào)用這個函數(shù)了。
# 調(diào)用外部函數(shù),并將其復(fù)制給一個變量,注意:此時返回的是內(nèi)函數(shù)的內(nèi)存地址
a = make_average()
# 給這個變量加(),就相當(dāng)于調(diào)用了內(nèi)函數(shù)average
print(a(20))
print(a(30))

運行結(jié)果如下:當(dāng)傳入的數(shù)值為20時,列表中只有一個數(shù),所以計算結(jié)果是20;當(dāng)再傳入一個數(shù)值30時,此時列表中就有兩個數(shù)20和30,所以平均值的計算結(jié)果是25.

二、裝飾器

1.裝飾器引入

例如,有以下兩個函數(shù),分別計算兩個數(shù)的和以及成績:

def add(a, b):
"""計算兩數(shù)之和"""
res = a + b
return res

def mul(a, b):
"""計算兩數(shù)之積"""
res = a * b
return res

現(xiàn)在有個需求:我想要在每個函數(shù)的計算開始前打印“開始計算...”,在計算結(jié)束后打印“計算結(jié)束...”。我們可以通過直接修改函數(shù)代碼的方式來滿足這個需求,但這樣會面臨以下問題:

  1. 如果要修改的函數(shù)過多,十個甚至一百個函數(shù),未免不現(xiàn)實;
  2. 不便于后期維護(hù),例如我不想打印“開始計算...”了,而是要打印“begin...”,豈不是又要重新修改一遍;
  3. 違反開閉原則(OCP),即程序的設(shè)計,要求開放對程序的擴(kuò)展、關(guān)閉對程序的修改;

所以,上述直接修改函數(shù)代碼的方式不可行。我們希望在不修改原函數(shù)的情況下,實現(xiàn)對函數(shù)的擴(kuò)展。例如:

def new_add(a, b):
print("開始計算...")
r = add(a, b)
print("計算結(jié)束...")
return r


print(new_add(22, 33))

執(zhí)行結(jié)果如下:

這種新創(chuàng)建一個函數(shù)的方式雖然沒有修改原函數(shù),但面臨一個很嚴(yán)重的問題:

只能擴(kuò)展指定函數(shù),不能通用于其它函數(shù),例如擴(kuò)展上述的add函數(shù),而不能擴(kuò)展mul函數(shù),如果想要擴(kuò)展mul函數(shù),只能再創(chuàng)建一個擴(kuò)展函數(shù);

因為,我們希望可以定義一個通用的擴(kuò)展函數(shù),可以作用域所有函數(shù)。這類不改變原函數(shù)代碼的通用函數(shù)就是:裝飾器。

2.函數(shù)裝飾器

裝飾器本質(zhì)上是一個python函數(shù)或類,它可以讓其他函數(shù)或類在不需要做任何代碼修改的前提下增加額外功能,也就是為已經(jīng)存在的對象添加額外功能,裝飾器的返回值也是一個函數(shù)/類對象。它經(jīng)常用于有切面需求的場景,比如:插入日志、性能測試、事務(wù)處理、緩存、權(quán)限校驗等場景。

1)被裝飾函數(shù)不帶參數(shù)

例如:

def wrapper_info(func):
def inner():
print("開始介紹...")
res = func()
print("介紹結(jié)束...")
return res

return inner

def introduce1():
print("我是周潤發(fā),我來自HONG KONG")

info = wrapper_info(introduce1)
info()

運行結(jié)果如下:

可見,在沒有改變原函數(shù)代碼的情況下,即給原函數(shù)增加了一些額外的功能,func是要修飾的函數(shù),作為一個變量傳入裝飾函數(shù),能夠通用于其他函數(shù),這個wrapper_info就是裝飾器。但目前面臨的問題是,被裝飾函數(shù)如果帶參數(shù)怎么辦?例如:

def introduce2(name, age):
print(f"我叫{name}, 我今年{age}歲了")

2)被裝飾函數(shù)帶參數(shù)

盡管可以在裝飾器wrapper_info中傳入name、age,但并不是每個被裝飾的函數(shù)都只有name、age,亦或是指定類型的參數(shù),還有可能傳入的是字典、列表、元組等。也就是傳入?yún)?shù)的類型和數(shù)量不固定怎么辦?

此時就需要用到不定長參數(shù):(*args, **kwargs)

def wrapper_info(func):
"""
用來對其他函數(shù)進(jìn)行擴(kuò)展,使其他函數(shù)可以在執(zhí)行前做一些額外的動作
:param func: 要擴(kuò)展的函數(shù)對象
:return:
"""
def inner(*args, **kwargs):
print("開始介紹...")
res = func(*args, **kwargs)
print("介紹結(jié)束...")
return res

return inner

例如:

def introduce3(name, age, city):
print(f"我叫{name}, 我今年{age}歲了, 我來自{city}")

運行結(jié)果如下:

3)裝飾器帶參數(shù)

上述提到的是裝飾器,一種是應(yīng)用于被裝飾的函數(shù)不帶參數(shù),一種是被裝飾的函數(shù)帶參數(shù),那裝飾器本身能否帶參數(shù)呢?比如我定義一個變量,想通過傳入不同的值來控制這個裝飾器實現(xiàn)不同的功能。答案是肯定的,例如:

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('周潤發(fā)', 28, '香港'))
info2('warn')
info3 = use_log(introduce4('成龍', 28, '香港'))
info3('xxx')

運行結(jié)果如下:

3.裝飾器調(diào)用

方式一:以函數(shù)方式調(diào)用

info3 = wrapper_info(introduce3)
info3('劉德華', 28, '香港')

如果是裝飾器函數(shù)帶參數(shù),則調(diào)用方式為:

info4 = use_log(introduce4('周星馳', 28, '香港'))
info4('info')

方式二:以語法糖方式調(diào)用

即在被裝飾函數(shù)上方以@符號進(jìn)行修飾

@wrapper_info
def introduce3(name, age, city):
print(f"我叫{name}, 我今年{age}歲了, 我來自{city}")

introduce3('劉德華', 28, '香港')

如果是裝飾器函數(shù)帶參數(shù),例如上述的use_log,則需要在裝飾器中傳入?yún)?shù):

@use_log('info')
def introduce4(name, age, city):
print(f"我叫{name}, 我今年{age}歲了, 我來自{city}")

小結(jié)

什么是裝飾器?

在不改變原函數(shù)代碼的情況下,給原函數(shù)增加了一些額外的功能,并且能夠通用于其他函數(shù),這樣的函數(shù)就稱作為裝飾器。

裝飾器的調(diào)用

可以通過傳統(tǒng)調(diào)用函數(shù)的方式進(jìn)行調(diào)用,也可以通過@裝飾器的方式調(diào)用

裝飾器的特點

  • 通過裝飾器,可以在不修改原來函數(shù)的情況下對函數(shù)進(jìn)行擴(kuò)展
  • 一個函數(shù)可以同時指定多個裝飾器

網(wǎng)頁名稱:Python裝飾器-閉包與函數(shù)裝飾器
轉(zhuǎn)載源于:http://www.dlmjj.cn/article/dhjsdgs.html