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