裝飾器可使函數執行前和執行後分別執行其餘的附加功能,這種在代碼運行期間動態增長功能的方式,稱之爲「裝飾器」(Decorator),裝飾器的功能很是強大。裝飾器通常接受一個函數對象做爲參數,以對其進行加強python
裝飾器知識準備一:閉包
>>> def f1(): ... print("hello world") ... >>> def f2(arg): ... arg() ... >>> f2(f1) hello world
裝飾器知識準備二:app
>>> def f1(): ... print("hello world") ... >>> def f1(): ... print("hello python") ... >>> f1() hello python
從上面兩個知識準備應該要知道的幾點:函數
1.定義函數,須要函數名+()調用才能執行函數spa
2.函數名是指向函數體,python執行時會把函數體加載到內存,函數名是對函數體的引用日誌
3.函數是能夠經過參數的方式傳遞code
4.Python執行代碼是從上往下執行,當相同的函數名指向了不一樣的函數體,越靠近函數執行的函數體纔會真正的執行,說白了:函數名在內存中指向了新的函數體,和變量賦值是同樣的。orm
5.python在函數中碰到return關鍵字就會終止同一級的代碼執行對象
假設我定義了一個函數f,想要在不改變原來函數定義的狀況下,在函數運行前打印出before,函數運行後打印出after,要實現這樣一個功能該怎麼實現?看下面如何用一個簡單的裝飾器來實現:blog
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()打印出返回值
在實際中,咱們的裝飾器可能應用到不一樣的函數中去,這些函數的參數都不同,和函數傳參是同樣的,下面來看看如何將其應用到裝飾器中去。
(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)
當一個裝飾器不夠用的話,咱們就能夠用兩個裝飾器,固然理解起來也就更復雜了,當使用兩個裝飾器的話,首先將函數與內層裝飾器結合而後在與外層裝飾器相結合,要理解使用@語法的時候到底執行了什麼,是理解裝飾器的關鍵。這裏仍是用最簡單的例子來進行說明。
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 #一、f=outer1(f) f被從新賦值爲outer1(1)的返回值inner1, def f(): # 此時func1爲 f():print('f 函數') print('f 函數') #二、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(),結合執行結果你就很明白了。
前面的裝飾器自己沒有帶參數,若是要寫一個帶參數的裝飾器怎麼辦,那麼咱們就須要寫一個三層的裝飾器,並且前面寫的裝飾器都不太規範,下面來寫一個比較規範帶參數的裝飾器,下面來看一下代碼,你們能夠將下面的代碼自我運行一下
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('一、登陸;二、查看信息;三、超級管理員管理\n>>>:') if inp == '1': login() elif inp == '2': home() elif inp == '3': index() main()