Python自動化運維之六、函數裝飾器

裝飾器:

  裝飾器可使函數執行前和執行後分別執行其餘的附加功能,這種在代碼運行期間動態增長功能的方式,稱之爲「裝飾器」(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()
相關文章
相關標籤/搜索