Python 闭包、单个装饰器、多个装饰器、装饰器修饰类、应用场景
1. 閉包
在 Python 中,函數(shù)也可以作為參數(shù)。我們可以執(zhí)行下面的代碼:
def func(a, b):return a + bprint(func)
我們直接輸出函數(shù)名,而沒(méi)有加括號(hào)。輸出結(jié)果如下:
<function func at 0x000002C83CC96678>
可以看懂結(jié)果是一個(gè) function 的對(duì)象,我們可以把它賦值給另外的變量:
def func(a, b):return a + bmy_func = func
print(my_func(1, 3))
我們把 func 賦值給 my_func 變量,然后發(fā)現(xiàn) my_func 可以和 func 一樣使用。輸出結(jié)果如下:
4
結(jié)果和 func 的結(jié)果一樣。那我們能不能把函數(shù)當(dāng)做參數(shù)或者返回值呢?我們可以嘗試一下:
def func():def inner():print("執(zhí)行了 inner 函數(shù)")return innerin_func = func()
in_func()
我們?cè)?func 里面定義了一個(gè) inner 函數(shù),按理我們是不能在外部調(diào)用 inner 函數(shù)的,但是我們把 inner 當(dāng)作返回值返回到外部,這樣我們就能在外部調(diào)用 inner 函數(shù)了。我們來(lái)看看運(yùn)行結(jié)果:
執(zhí)行了 inner 函數(shù)
可以看到確實(shí)執(zhí)行了 inner 函數(shù)。其實(shí) inner 函數(shù)我們就可以叫做閉包函數(shù)。我們可以利用閉包來(lái)實(shí)現(xiàn)裝飾器。那裝飾器又是什么呢?下面我們來(lái)詳細(xì)看一下。
?
2. 裝飾器
裝飾器如果從字面意思來(lái)講就是用來(lái)裝飾的東西,它裝飾的對(duì)象是函數(shù)。我們可以用裝飾器在不改變?cè)瘮?shù)的情況下對(duì)函數(shù)進(jìn)行擴(kuò)展。我們先不說(shuō)裝飾器,我們想想要怎么才能擴(kuò)展一個(gè)函數(shù),最簡(jiǎn)單的辦法就是如下:
def func(a, b):return a + bprint("在 func 之前執(zhí)行")
func(1, 2)
print("在 func 之后執(zhí)行")
我們直接調(diào)用這個(gè)函數(shù),然后在函數(shù)前面添加一些代碼,再在函數(shù)執(zhí)行后面添加一些代碼。這樣很好的完成了我們的任務(wù),但是這種方式屬實(shí)有點(diǎn)矬略。如果我們經(jīng)常要用到這種擴(kuò)展,或者多個(gè)函數(shù)需要用到這樣的擴(kuò)展我們這種方式就束手無(wú)策了。
?
這個(gè)時(shí)候我們就可以使用閉包,閉包能很好的解決這個(gè)問(wèn)題。我們看下面這段代碼:
def say_hi(func):print("nice to meet you")def inner(*args, **kwargs):return func(*args, **kwargs)print("good bye")return innerdef func(greet):print(greet)say_hi_func = say_hi(func)
我們先定義一個(gè)函數(shù) say_hi,它的參數(shù)是一個(gè)函數(shù),也就是我們要擴(kuò)展的函數(shù)。然后我們?cè)诶锩娑x一個(gè) inner 函數(shù),在 inner 函數(shù)中完成 func 函數(shù)的調(diào)用,并接收 func 的參數(shù)和返回值。最后將 inner 函數(shù)作為參數(shù)返回。
?
我們想對(duì) func 函數(shù)進(jìn)行擴(kuò)展,我們只需要調(diào)用 say_hi(func),它就會(huì)給我們返回一個(gè)加強(qiáng)后的函數(shù)。我們可以執(zhí)行看看:
def say_hi(func):print("nice to meet you")def inner(*args, **kwargs):return func(*args, **kwargs)print("good bye")return innerdef func(a, b):return a + bsay_hi_func = say_hi(func)
result = say_hi_func(1, 2)
print(result)
執(zhí)行結(jié)果如下:
nice to meet you
good bye
3
可以看到我們成功擴(kuò)展了 func 函數(shù)。但是這樣有點(diǎn)混亂,我們還需要先加強(qiáng)函數(shù)才能使用,而另一種簡(jiǎn)單的方式就是使用 @ 符號(hào):
def say_hi(func):print("nice to meet you")def inner(*args, **kwargs):return func(*args, **kwargs)print("good bye")return inner@say_hi
def func(a, b):return a + bprint(func(1, 2))
我們?cè)?func 函數(shù)定義的時(shí)候執(zhí)行了添加了 @say_hi,這個(gè)時(shí)候 func 就是解釋器會(huì)幫我們完成下面幾句代碼:
func = say_hi(func)
這樣就不需要再使用一個(gè)新的函數(shù)了。
3. 多裝飾器
我們可以給一個(gè)函數(shù)添加多個(gè)裝飾器,其實(shí)使用也是一樣的。我們可以這樣里面,假如我們有如下函數(shù):
def func():print("這是函數(shù)體")
假如我們有如下裝飾器:
def decorate(func):print("函數(shù)執(zhí)行前")def inner(*args, **kwargs):return func(*args, **kwargs)print("函數(shù)執(zhí)行后")return inner
在外面對(duì) func 函數(shù)進(jìn)行裝飾后,func 的內(nèi)容變?yōu)?#xff1a;
def func():print("函數(shù)執(zhí)行前")print("函數(shù)體")print("函數(shù)執(zhí)行后")
這時(shí)使用裝飾器后的函數(shù)和普通函數(shù)沒(méi)有區(qū)別,外面依舊可以用同樣的方式給他添加多個(gè)裝飾器:
def dec1(func):print("start dec1")def inner(*args, **kwargs):return func(*args, **kwargs)print("end dec1")return innerdef dec2(func):print("start dec2")def inner(*args, **kwargs):return func(*args, **kwargs)print("end dec2")return inner@dec2
@dec1
def func(a, b):return a + bprint(func(1, 2))
不過(guò)外面需要注意一下生效的順序:
start dec1
end dec1
start dec2
end dec2
3
可以看到時(shí) dec1 裝飾器先起作用,對(duì)于多個(gè)裝飾器也是一樣的。
3.1 帶參裝飾器
def add(func):def fun(a, b):print("相乘", a * b)print("相除", a / b)func(a, b)return fun@add
def add_num(a, b):# 打印兩個(gè)數(shù)相加print("相加:", a + b)add_num(11, 22)
3.2 通用裝飾器
果同一個(gè)裝飾器既要裝飾有參數(shù)的函數(shù),又要裝飾無(wú)參數(shù)的函數(shù)。那么我們?cè)趥鲄⒌臅r(shí)候就設(shè)置成不定長(zhǎng)參數(shù),這樣不管被裝飾的函數(shù)有沒(méi)有參數(shù)都能用。
# 通用裝飾器
def add(func):def fun(*args, **kwargs):print("裝飾器的功能代碼:登錄前")func(*args,**kwargs)print("裝飾器的功能代碼:登錄后")return fun@add
def index():print("這個(gè)是網(wǎng)站的首頁(yè)")@add
def good_list(num):print("這個(gè)是商品列表第{}頁(yè)".format(num))index()
print("------------")
good_list(9)
其實(shí)裝飾器也是可以帶參數(shù)的,我們可以定義一個(gè)更加復(fù)雜的裝飾器:
def dec1(name):def decorator(func):def inner(*args, **kwargs):if name == "wohu":print("hi wohu")elif name == "jack":print("hi jack")return func(*args, **kwargs)return innerreturn decorator@dec1("wohu")
def func(a, b):return a+bprint(func(1, 2))
我們先來(lái)看下面的代碼:
@dec1("wohu")
def func(a, b):return a +b
我們可以對(duì) @dec1("wohu") 進(jìn)行如下理解,其中 dec1("wohu") 的操作是執(zhí)行 dec1 函數(shù),并傳入?yún)?shù),而 dec1 的返回值是一個(gè)裝飾器。這樣我們就很好理解了。
?
因此,我們需要在 dec1 函數(shù)中定義一個(gè)裝飾器。這可以從代碼中看到。其它部分和之前沒(méi)有區(qū)別,下面是輸出結(jié)果:
hi wohu
3
這種帶參數(shù)的裝飾器可以讓我們的裝飾器更加靈活,我們只需要通過(guò)不同參數(shù)就能讓裝飾器裝飾不同的效果。
?
4. 裝飾器裝飾類(lèi)
4.1 不帶參數(shù)
#裝飾器裝飾類(lèi)def add(func):def fun(*args, **kwargs):print("裝飾器的功能代碼:登錄")return func(*args,**kwargs)return fun@add # MyClass=add(MyClass)
class MyClass:def __init__(self):passm = MyClass()
print("m 的值:",m)
把類(lèi)當(dāng)作一個(gè)參數(shù)傳到裝飾器里面。return fun返回的是 fun,MyClass接收到的是 fun。
MyClass()調(diào)用的是 fun。
?
執(zhí)行代碼:
def fun(*args, **kwargs):print("裝飾器的功能代碼:登錄")return func(*args,**kwargs)
這里面的功能。先執(zhí)行裝飾器的功能,return func(*args,**kwargs),func()來(lái)自def add(func)。
?
調(diào)用 MyClass這個(gè)類(lèi),return func(*args,**kwargs)創(chuàng)建了個(gè)對(duì)象,MyClass()調(diào)用完了接收,m 就能接收這個(gè)對(duì)象了。
4.2 帶參數(shù)
#裝飾器裝飾類(lèi)def add(func):def fun(*args, **kwargs):print("裝飾器的功能代碼:登錄")# 裝飾器裝飾類(lèi)和裝飾函數(shù)的不同點(diǎn),下面這個(gè) return 必須要寫(xiě) 類(lèi)需要把對(duì)象返回出來(lái)。return func(*args,**kwargs)return fun@add # MyClass=add(MyClass)
class MyClass:def __init__(self,name,age):self.name=nameself.age=agem = MyClass("qinghan","18")
print("m 的值:",m)
這里用的是不定行參數(shù),所以不管你裝飾的類(lèi)是有參數(shù)的還是沒(méi)參數(shù)的,都可以。
5. 裝飾器的應(yīng)用場(chǎng)景
- 函數(shù)運(yùn)行時(shí)間統(tǒng)計(jì);
- 執(zhí)行函數(shù)之前做準(zhǔn)備工作;
- 執(zhí)行函數(shù)后清理功能;
?
總結(jié)
以上是生活随笔為你收集整理的Python 闭包、单个装饰器、多个装饰器、装饰器修饰类、应用场景的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2022-2028年中国美瞳行业应用市场
- 下一篇: 2022-2028年中国顺丁橡胶行业发展