裝飾器:
裝飾器可以使函數執行前和執行后分別執行其他的附加功能,這種在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator),裝飾器的功能非常強大。裝飾器一般接受一個函數對象作為參數,以對其進行增強
- 裝飾器本身是一個函數,用於裝飾其他函數
- 功能:增強被裝飾函數的功能
- 裝飾器是一個閉包函數是嵌套函數,通過外層函數提供嵌套函數的環境
- 裝飾器在權限控制,增加額外功能如日志,發送郵件用的比較多
裝飾器知識准備一:
>>> def f1(): ... print("hello world") ... >>> def f2(arg): ... arg() ... >>> f2(f1) hello world
裝飾器知識准備二:
>>> def f1(): ... print("hello world") ... >>> def f1(): ... print("hello python") ... >>> f1() hello python
從上面兩個知識准備應該要知道的幾點:
1.定義函數,需要函數名+()調用才能執行函數
2.函數名是指向函數體,python執行時會把函數體加載到內存,函數名是對函數體的引用
3.函數是可以通過參數的方式傳遞
4.Python執行代碼是從上往下執行,當相同的函數名指向了不同的函數體,越靠近函數執行的函數體才會真正的執行,說白了:函數名在內存中指向了新的函數體,和變量賦值是一樣的。
5.python在函數中碰到return關鍵字就會終止同一級的代碼執行
1、原函數不帶參數的裝飾器
假設我定義了一個函數f,想要在不改變原來函數定義的情況下,在函數運行前打印出before,函數運行后打印出after,要實現這樣一個功能該怎么實現?看下面如何用一個簡單的裝飾器來實現:
def outer(func): def inner(): print("before") result = func() print("after") return result return inner # (1) @ + 函數名 直接作用在需要裝飾的函數上一行 # (2)自動執行outer函數並且將下面的函數名f1當做參數傳遞到outer() # (3)將outer函數的返回值inner,重新賦值給f1 @outer def f1(): print("F1") return "是你" ret = f1() print("返回值是:",ret) 執行后的結果是: before F1 after 返回值是: 是你
python執行代碼從上往下執行過程:
(1)碰到def outer(func)就把函數體加載到內存
(2)碰到@outer裝飾器就會自動執行outer()函數並把f1這個函數名通過參數傳遞到outer(f1)
(3)碰到def inner()就把函數體加載內存(注意:1.函數體中f1()這個函數已經執行過了print("F1")這段代碼
2.此時inner()函數體中就有了print("before"),print("F1"),print("after")3句代碼
3.並將返回值賦值給變量result,然后return result返回)
python碰到return同一級代碼不再執行了,此時inner()函數就結束了(因為inner函數沒有調用,所以沒有執行)
(4)碰到return inner,函數outer()就會把inner的函數名(函數名是指向函數體的)重新賦值給了f1,此時f1->指向inner的函數體,而不是指向原來的函數體(print("F1") return "是你"),f1指向了新的函數體
(5)碰到ret = f1(),f1()被調用了就會執行f1函數就會執行inner的函數體,並用ret接收返回值
(6)就print()打印出返回值
2、原函數帶參數的裝飾器
在實際中,我們的裝飾器可能應用到不同的函數中去,這些函數的參數都不一樣,和函數傳參是一樣的,下面來看看如何將其應用到裝飾器中去。
(1)原函數帶單個參數或者幾個參數,接受參數的時候定義單個形參即可:
def outer(func): def inner(args): print("before") ret = func(args) print("after") return ret return inner @outer def f1(args): print(args) return "是你" ret = f1("aaaaaaaa") print("返回值是:",ret)
(2)接收參數的時候定義萬能參數,不管原參數帶多少參數:
def outer(func): def inner(*args,**kwargs): print("before") ret = func(*args,**kwargs) print("after") return ret return inner @outer def f1(args): print(args) return "是你" ret = f1("aaaaaaaa") print("返回值是:",ret)
3、使用兩個裝飾器
當一個裝飾器不夠用的話,我們就可以用兩個裝飾器,當然理解起來也就更復雜了,當使用兩個裝飾器的話,首先將函數與內層裝飾器結合然后在與外層裝飾器相結合,要理解使用@語法的時候到底執行了什么,是理解裝飾器的關鍵。這里還是用最簡單的例子來進行說明。
def outer2(func2): def inner2(*args,**kwargs): print('開始') r=func2(*args,**kwargs) print('結束') return r return inner2 def outer1(func1): def inner1(*args,**kwargs): print('start') r=func1(*args,**kwargs) print('end') return r return inner1 @outer2 # 這里相當於執行了 f=outer1(f) f=outer2(f),步驟如下 @outer1 #1、f=outer1(f) f被重新賦值為outer1(1)的返回值inner1, def f(): # 此時func1為 f():print('f 函數') print('f 函數') #2、f=outer2(f) 類似f=outer2(inner1) f被重新賦值為outer2的返回值inner2 # 此時func2 為inner1函數 inner1里面func1函數為原來的 f():print('f 函數') f() # 相當於執行 outer2(inner1)()
執行結果如下: 開始 # 在outer函數里面執行,首先打印 ‘開始 ’ start # 執行func2 即執行inner1函數 打印 ‘start’ f 函數 # 在inner1函數里面執行 func1 即f()函數,打印 ‘f 函數’ end # f函數執行完,接着執行inner1函數里面的 print('end') 結束 # 最后執行inner2函數里面的 print('結束')
python執行代碼的時候碰到兩個裝飾器解釋過程:
將@outer1和f()先執行,函數outer1()將返回值inner1重新賦值給f,此時f指向的函數體是inner1的函數體,這里稱f為新f
@outer2將新f當作參數傳入inner2(),然后將返回值inner2重新賦值給f,此時f指向的函數體是inner2的函數體
執行過程和解釋過程是相反的:從上到下的,先通過第一層@outer2 -----> @outer1 -------> f(),結合執行結果你就很明白了。
4、帶參數的裝飾器
前面的裝飾器本身沒有帶參數,如果要寫一個帶參數的裝飾器怎么辦,那么我們就需要寫一個三層的裝飾器,而且前面寫的裝飾器都不太規范,下面來寫一個比較規范帶參數的裝飾器,下面來看一下代碼,大家可以將下面的代碼自我運行一下
import functools def log(k=''): #這里參數定義的是一個默認參數,如果沒有傳入參數,默認為空,可以換成其他類型的參數 def decorator(func): @functools.wraps(func) #這一句的功能是使被裝飾器裝飾的函數的函數名不被改變, def wrapper(*args, **kwargs): print('start') print('{}:{}'.format(k, func.__name__)) #這里使用了裝飾器的參數k r = func(*args, **kwargs) print('end') return r return wrapper return decorator @log() # fun1=log()(fun1) 裝飾器沒有使用參數 def fun1(a): print(a + 10) fun1(10) # print(fun1.__name__) # 上面裝飾器如果沒有@functools.wraps(func)一句的話,這里打印出的函數名為wrapper @log('excute') # fun2=log('excute')(fun2) 裝飾器使用給定參數 def fun2(a): print(a + 20) fun2(10)
一個粗糙的例子:
USER_INFO = {} def check_login(func): def inner(*args,**kwargs): if USER_INFO.get('is_login',None): ret = func() return ret else: print("請登錄") return inner def check_admin(func): def inner(*args,**kwargs): if USER_INFO.get('user_type',None) == 2: ret = func() return ret else: print("無權限查看") return inner @check_login @check_admin def index(): """ 管理員用戶 :return: """ print('index') @check_login def home(): """ 普通用戶 :return: """ print('home') def login(): user = input("請輸入用戶名:") if user == 'admin': USER_INFO['is_login'] = True USER_INFO['user_type'] = 2 else: USER_INFO['is_login'] = True USER_INFO['user_type'] = 1 def main(): while True: inp = input('1、登錄;2、查看信息;3、超級管理員管理\n>>>:') if inp == '1': login() elif inp == '2': home() elif inp == '3': index() main()