1. 程式人生 > 實用技巧 >15 Python學習之裝飾器

15 Python學習之裝飾器

裝飾器

裝飾器:在不改變源函式的程式碼及呼叫方式的前提下,為其增加新的功能,裝飾器就是一個函式,他的本質是閉包

裝飾器開放封閉的原則:

開放:對程式碼的擴充套件開放

封閉:對原始碼的修改是封閉的

被裝飾函式無引數,無返回值

例1:

import time

def timer(func):
    def inner():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f'裝飾器列印執行時間為:{round(end_time - start_time, 5)}')
    return inner            # 閉包,返回內層函式的引用

# show_info = timer(show_info)第一個show_info是一個新的變數,第二個show_info的函式名,@timer是show_info = timer(show_info)的簡寫,官方叫做語法糖
@timer
def show_info():
    print(f"我的姓名是:張三")
    time.sleep(2)


show_info()

執行結果:

我的姓名是:張三
裝飾器列印執行時間為:2.00051

被裝飾函式有返回值

被裝飾函式有返回值,那麼在設計裝飾器的時候,就應該有變數進行接收,然後再將該返回值返回給呼叫者

例1:

import time

def timer(func):
    def inner():
        start_time = time.time()
        inner_ret = func()              # 呼叫原函式並接受返回值
        end_time = time.time()
        print(f'裝飾器列印執行時間為:{round(end_time - start_time, 5)}')
        return inner_ret                # 將呼叫原函式的返回值返回給呼叫者
    return inner            # 閉包,返回內層函式的引用


@timer          # show_info = timer(show_info)第一個show_info是一個新的變數,第二個show_info的函式名,@timer是show_info = timer(show_info)的簡寫,官方叫做語法糖
def show_info():
    print(f"我的姓名是:張三")
    time.sleep(2)
    return 'show_info函式的返回值'


ret = show_info()           # ret接受的是inner的返回值inner_ret
print(ret)

我的姓名是:張三
裝飾器列印執行時間為:2.00168
show_info函式的返回值

被裝飾函式有引數

被裝飾函式有引數,由於實際呼叫的時候,掉的是閉包中的內部函式,所以內部函式相應的也要設計成跟原函式一樣的帶引數

例1:

import time

def timer(func):
    def inner(name):               # 設計時需要新增引數
        start_time = time.time()
        inner_ret = func(name)              # 呼叫原函式indxe,傳入引數並接受返回值
        end_time = time.time()
        print(f'裝飾器列印執行時間為:{round(end_time - start_time, 5)}')
        return inner_ret                # 將呼叫原函式的返回值返回給呼叫者
    return inner            # 閉包,返回內層函式的引用


@timer          # show_info = timer(show_info)第一個show_info是一個新的變數,第二個show_info的函式名,@timer是show_info= timer(show_info)的簡寫,官方叫做語法糖
def show_info(name):
    print(f"我的姓名是:{name}")
    time.sleep(2)
    return 'show_info函式的返回值'


ret = show_info('張三')       # 相當於ret = inner('張三')
print(ret)

我的姓名是:張三
裝飾器列印執行時間為:2.00031
show_info函式的返回值

標準裝飾器

由於被裝飾函式的引數個數不確定,所以我們在設計裝飾器時,內部函式的引數個數就無法確定,因此要設計成不定長的形式

例1:

import time

def timer(func):
    def inner(*args, **kwargs):               # 設計時需要新增引數
        start_time = time.time()
        inner_ret = func(*args, **kwargs)     # 呼叫原函式indxe,傳入引數並接受返回值
        end_time = time.time()
        print(f'裝飾器列印執行時間為:{round(end_time - start_time, 5)}')
        return inner_ret                # 將呼叫原函式的返回值返回給呼叫者
    return inner            # 閉包,返回內層函式的引用


@timer          # show_info = timer(show_info)第一個show_info是一個新的變數,第二個show_info的函式名,@timer是show_info= timer(show_info)的簡寫,官方叫做語法糖
def show_info(name, age):
    print(f"我的姓名是:{name}, 今年{age}歲")
    time.sleep(2)
    return 'show_info函式的返回值'


ret = show_info('張三', 25)
print(ret)

我的姓名是:張三, 今年25歲
裝飾器列印執行時間為:2.0017
show_info函式的返回值

特別注意

在標準裝飾器中,def inner(*args, **kwargs): 中的 * 在函式 定義 的時候是將傳入函式的引數聚合成一個元素,在 呼叫函式 inner_ret = func(*args, **kwargs) 的時候 * 的作用是將一個可迭代的物件進行打散,即將變數 args或kwargs 打散,拆分成一個個要傳入的實參

標準裝飾器模板:

def decorator(real_func_name):
    def inner(*args, **kwwargs):
        '''呼叫裝飾器訪問函式前要執行的操作'''
        ret = real_func_name(*args, **kwwargs)
         '''呼叫裝飾器訪問函式後要執行的操作'''
        return ret
   	return inner

裝飾器的應用

裝飾器一般用於登入驗證和日誌

例1:

def login():
    name = input('請輸入使用者名稱:')
    pwd = input('請輸入密碼:')
    login_status['name'] = name
    if pwd == '123456':
        login_status['status'] = True
        return 1
    else:
        return 0


login_status = {
    'name': None,
    'status': False
}


def decorator(func_name):
    def inner(*args, **kwargs):
        if login_status['status']:
            ret = func_name(*args, **kwargs)
            return ret
        else:
            ret = login()
            if ret:
                ret = func_name()
                return ret
            else:
                print('登入失敗')

    return inner


@decorator
def index():
    print("index頁面")

@decorator
def logger():
    print("logger頁面")

# 只有登入成功後才會執行函式,並列印資訊
index()
logger()

請輸入使用者名稱:張三
請輸入密碼:123456
index頁面
logger頁面