1. 程式人生 > >Python 裝飾器理解與使用

Python 裝飾器理解與使用

Python裝飾器

本質是函式 為其他函式新增附加功能。裝飾器本身也是Python的一個重點,所以無論如何你必須弄懂它。裝飾器的存在其實就是為了在需要新增新功能時不影響之前版本的使用的同時來增加新功能,其實也是一種“偷懶”的辦法。它的使用場景較多,比如:插入日誌、效能測試、事務處理、快取、許可權校驗等場景。有了裝飾器,我們就可以抽離出大量與函式功能本身無關的雷同程式碼並繼續重用。

Python裝飾器的兩大原則:

(1)在不修改被修飾函式原始碼的同時為其增加功能

(2)不能修改被裝飾的函式的呼叫方式

Python裝飾器使用詳解

裝飾器的使用其實也並非很複雜,只要你理解了它的基本原理以後,你便會愛上這個方法。在這裡做一個簡單講解,所有的函式本身就是一個“變數”,我們其實是可以直接把函式當作引數來進行傳遞的(但是在傳遞函式時不需要加括號,只需要函式名),其實傳遞函式的本質就是傳遞了函式的地址,拿到這個地址後加上括號就可以運行了。
(1)無引數的裝飾器

import time
 
 
# 裝飾器
def decorator(func):  # func為裝飾器繫結的方法(繫結裝飾器後自動傳入)
    def Deco():
        start_time = time.time()
        func()  # 呼叫test方法
        end_time = time.time()
        print("time:", end_time - start_time, "秒")
 
    return Deco  # 返回Deco方法
 
 
@decorator  # 新增裝飾器
def test():
    time.sleep(2)
    print("run test")
 
 
test()  # 呼叫函式

(2)有引數的裝飾器

import time
 
 
# 裝飾器
def decorator(func):  # func為裝飾器繫結的方法(繫結裝飾器後自動傳入)
    def Deco(arg1):  # 傳入test方法的引數
        start_time = time.time()
        func(arg1)  # 呼叫test方法
        end_time = time.time()
        print("time:", end_time - start_time, "秒")
 
    return Deco  # 返回Deco方法
 
 
@decorator  # 新增裝飾器
def test(mStr):
    time.sleep(2)
    print("run test:"+mStr)
 
 
test("傳入引數")  # 呼叫函式

(3)裝飾器即可裝飾帶參函式也可以裝飾不帶參函式

import time
 
 
# 裝飾器
def decorator(func):  # func為裝飾器繫結的方法(繫結裝飾器後自動傳入)
    def Deco(*arg1, **kwargs):  # (傳入非固定引數)這樣即使裝飾函式不帶引數也可被裝飾 
        start_time = time.time()
        func(*arg1, **kwargs)
        end_time = time.time()
        print("time:", end_time - start_time, "秒")
 
    return Deco  # 返回Deco方法
 
 
@decorator  # 新增裝飾器
def test():
    time.sleep(2)
    print("run test:")
 
 
test()  # 呼叫函式

(4)裝飾器的高階使用

以上的裝飾器已經可以滿足基本需求了,但是他們第一存在一個問題,被裝飾的函式的返回結果會被修改。

這裡我們模擬一個登陸裝飾器,裝飾器需要對不同的被裝飾函式使用不同的登陸方法,並且是返回值不被修改。

user, password = 'db', '12345'
 
# 當裝飾器也需要參入引數時我們需要給裝飾器再加一層函式,
# 此時裝飾器接受到的方法需要進入第二層函式進行接受,
# 第一層需要接受裝飾器自己的引數
 
 
def login(login_type):
    def outer_wrapper(func):
        def wrapper(*agr1, **kwargs):
            usernameInput = input("UserName:").strip()
            passwordInput = input("Password:").strip()
            if login_type == "local":
                if user == usernameInput and password == passwordInput:
                    print("login successful")
                    res = func(*agr1, **kwargs)  # 接受返回結果
                    return res
                else:
                    print("login fail")
            elif login_type == "ldap":
                print("遠端登入")
        return wrapper
    return outer_wrapper
 
 
def index():
    print("welcome to index page")
 
 
@login(login_type="local")  # 對裝飾分類
def home():
    print("welcome to home page")
    return "from home"
 
 
@login(login_type="ldap")  # 對裝飾分類
def blog():
    print("welcome to blog page")
 
 
index()
print(home())
blog()

總結

(1) 要想裝飾器不修改被裝飾函式的返回值,我們需要在裝飾器中接受被裝飾函式的返回值並Return即可。

(2) 如果希望對被裝飾函式進行分類處理,我們可以在繫結裝飾器時傳入一個引數用於對被裝飾函式進行分類,但是這樣我們需要在裝飾器中在套一層函式,在第一層接收裝飾器傳遞的引數,在第二層函式中接收被裝飾函式。

(3) 如果希望裝飾器既能裝飾帶參的函式也可以修飾不帶參的函式,我們只需要在裝飾器中接收引數時,把引數定義為非固定引數即可。