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


裝飾器:

  裝飾器可以使函數執行前和執行后分別執行其他的附加功能,這種在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(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()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM