python中的裝飾器

代碼環境:python3.6

什麼是裝飾器

裝飾器是 python 的一種語法糖,本質是一個可調用的對象,其參數是一個被裝飾的函數。裝飾器可能會處理被裝飾的函數並返回,或者將其替換成另外一個函數或對象。python

裝飾器兩種經常使用的用法

  • 原封不動返回被裝飾函數

這種用法出如今不少 python web 框架中,例如把 url 地址映射到 http 響應的函數上的註冊處。web

下面舉個簡單的例子:閉包

def register(func):
    # 內部無新函數
    print('running register {}'.format(func))
    return func


@register
def my_func():
    print('running my_func()')


if __name__ == "__main__":
    my_func()
  • 把被裝飾函數替換成新函數

大多數裝飾器,一般內部會定義一個閉包結構的函數,將其返回替換被裝飾函數。app

這種用法最多見,用於不修改原函數的基礎上增長額外的功能,好比計算函數的運行時間、輸出指定格式日誌等。框架

下面用一個裝飾器輸出函數運行時間:函數

def running_time(func):
    # 內部有新函數
    def print_running_time(*args):
        '''打印函數運行時間'''
        t0 = time.time()
        result = func(*args)
        need_time = time.time() - t0
        print('新列表生成時間(秒):{:.8f}'.format(need_time))
        return result

    return print_running_time


@running_time
def new_list(n):
    '''生成一個新列表'''
    temp_list = []
    for x in range(n):
        temp_list.append(x * (x + 1))
    return temp_list


if __name__ == "__main__":
    print('新列表長度:{}'.format(len(new_list(12345))))
    print('new_list 函數的 __name__ 屬性:{}'.format(new_list.__name__))
    print('new_list 函數的 __doc__ 屬性:{}'.format(new_list.__doc__))

執行結果:url

running register <function my_func at 0x0000000002889B70>
新列表生成時間(秒):0.00250006
新列表長度:12345
new_list 函數的 __name__ 屬性:print_running_time
new_list 函數的 __doc__ 屬性:打印函數運行時間

python什麼時候執行裝飾器

從上述例子中咱們注意到,在調用new_list(12345)打印出結果以前,結果欄先輸出了裝飾器中的print語句,這說明:日誌

裝飾器在導入模塊@func時當即執行。code

functools.wraps裝飾器

在上述結果中,咱們還注意到另外一個特色:new_list函數的__name____doc__屬性都被替換成裝飾器內部函數的相關屬性。因此,咱們須要改進上面的例子,使用functools.wraps裝飾器把相關屬性從func複製到新函數中。orm

改進例子以下:

from functools import wraps


def running_time(func):
    # 內部有新函數

    @wraps(func)
    # 此處使用 wraps 裝飾器
    def print_running_time(*args):
        '''打印函數運行時間'''
        t0 = time.time()
        result = func(*args)
        need_time = time.time() - t0
        print('新列表生成時間(秒):{:.8f}'.format(need_time))
        return result

    return print_running_time


@running_time
def new_list(n):
    '''生成一個新列表'''
    temp_list = []
    for x in range(n):
        temp_list.append(x * (x + 1))
    return temp_list


if __name__ == "__main__":
    print('新列表長度:{}'.format(len(new_list(12345))))
    print('new_list 函數的 __name__ 屬性:{}'.format(new_list.__name__))
    print('new_list 函數的 __doc__ 屬性:{}'.format(new_list.__doc__))

執行結果:

running register <function my_func at 0x0000000002879B70>
新列表生成時間(秒):0.00250006
新列表長度:12345
new_list 函數的 __name__ 屬性:new_list
new_list 函數的 __doc__ 屬性:生成一個新列表

觀察改進後例子的運行結果,new_list函數的相關屬性已恢復正常。

相關文章
相關標籤/搜索