python筆記35-裝飾器

前言

python裝飾器本質上就是一個函數,它可讓其餘函數在不須要作任何代碼變更的前提下增長額外的功能,裝飾器的返回值也是一個函數對象。
不少python初學者學到面向對象類和方法是一道大坎,那麼python中的裝飾器是你進入Python高級語法大門的一道坎。python

計算函數運行時間

假設你寫了幾個函數,有一天領導心血來潮說,你把每一個函數的運行時長(結束時間-開始時間)統計下,做爲一個python實習生的你可能會這樣寫web

原始函數

import time


def func_a():
    print("hello")
    time.sleep(0.5)


def func_b():
    print("world")
    time.sleep(0.8)

if __name__ == '__main__':
    func_a()
    func_b()

添加運行時長

做爲一個實習生的你,可能想到的解決辦法以下app

import time


def func_a():
    start = time.time()
    print("hello")
    time.sleep(0.5)
    end = time.time()
    print("運行時長:%.4f 秒" % (end-start))


def func_b():
    start = time.time()
    print("world")
    time.sleep(0.8)
    end = time.time()
    print("運行時長:%.4f 秒" % (end-start))

if __name__ == '__main__':
    func_a()
    func_b()

運行結果:框架

hello
運行時長:0.5009 秒
world
運行時長:0.8008 秒

上面的代碼雖然知足了領導的要求,可是若是你寫的函數不少的話,每一個函數都這樣去添加,會顯得代碼很臃腫,有不少重複代碼。
有一天你邊上的一個python老司機看了下你的代碼,給你指了條路:裝飾器函數

函數裝飾器

裝飾器能夠寫成函數式裝飾器,也能夠寫成一個類裝飾器,先從簡單的函數裝飾器開始學習。
python裝飾器本質上就是一個函數,它可讓其餘函數在不須要作任何代碼變更的前提下增長額外的功能,裝飾器的返回值也是一個函數對象。學習

runtime函數就是一個裝飾器了,它對原函數作了包裝並返回了另一個函數,額外添加了一些功能。在函數上方使用@語法糖就能夠調用這個裝飾器了測試

import time


def runtime(func):
    def wrapper():
        start = time.time()
        f = func()     # 原函數
        end = time.time()
        print("運行時長:%.4f 秒" % (end-start))
        return f
    return wrapper


@runtime
def func_a():
    print("hello")
    time.sleep(0.5)


@runtime
def func_b():
    print("world")
    time.sleep(0.8)

if __name__ == '__main__':
    func_a()
    func_b()

運行結果ui

hello
運行時長:0.5001 秒
world
運行時長:0.8001 秒

函數帶參數裝飾器

上面的runtime就是一個簡單的裝飾器模型了,但並不強壯,若是函數裏面帶有參數,那就無論用了,而且函數的參數是不固定的,這時候就須要用到*args,**kwargs兩兄弟了日誌

import time


def runtime(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        f = func(*args, **kwargs)     # 原函數
        end = time.time()
        print("運行時長:%.4f 秒" % (end-start))
        return f
    return wrapper


@runtime
def func_a(a):
    print("hello"+a)
    time.sleep(0.5)


@runtime
def func_b(b, c="xx"):
    print("world"+b+c)
    time.sleep(0.8)

if __name__ == '__main__':
    func_a("a")
    func_b("b", c="xxx")

類裝飾器

關於__call__方法,不得不先提到一個概念,就是可調用對象(callable),咱們平時自定義的函數、內置函數和類都屬於可調用對象,
但凡是能夠把一對括號()應用到某個對象身上均可稱之爲可調用對象,判斷對象是否爲可調用對象能夠用函數 callable。
若是在類中實現了__call__方法,那麼實例對象也將成爲一個可調用對象code

import time


class runtime(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        start = time.time()
        f = self.func(*args, **kwargs)     # 原函數
        end = time.time()
        print("運行時長:%.4f 秒" % (end-start))
        return f


@runtime
def func_a(a):
    print("hello"+a)
    time.sleep(0.5)


@runtime
def func_b(b, c="xx"):
    print("world"+b+c)
    time.sleep(0.8)

if __name__ == '__main__':
    func_a("a")
    func_b("b", c="xxx")

裝飾器帶參數

快到年末了,領導說運行的速度先不要太快了,讓客戶先加錢,而後再以正常的速度顯示,那麼如今的需求是讓每一個函數的運行時間加50%,該如何實現呢?
這就到了裝飾器的高級語法,裝飾器也須要帶上參數了

函數裝飾器

import time


def runtime(slowly=1):
    def wrapper(func):
        def inner_wrapper(*args, **kwargs):
            start = time.time()
            f = func(*args, **kwargs)     # 原函數
            end = time.time()
            t = end-start
            time.sleep((slowly-1)*t)  # 延遲效果
            new_end = time.time()
            print("運行時長:%.4f 秒" % (new_end-start))
            return f
        return inner_wrapper
    return wrapper


@runtime(1.5)
def func_a(a):
    print("hello"+a)
    time.sleep(0.5)


@runtime(1.5)
def func_b(b, c="xx"):
    print("world"+b+c)
    time.sleep(0.8)

if __name__ == '__main__':
    func_a("a")
    func_b("b", c="xxx")

類裝飾器

import time


class runtime(object):
    def __init__(self, slowly=1):
        self.slowly = slowly

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            start = time.time()
            f = func(*args, **kwargs)     # 原函數
            end = time.time()
            t = end-start
            time.sleep((self.slowly-1)*t)  # 延遲效果
            new_end = time.time()
            print("運行時長:%.4f 秒" % (new_end-start))
            return f
        return wrapper


@runtime(1.5)
def func_a(a):
    print("hello"+a)
    time.sleep(0.5)


@runtime(1.5)
def func_b(b, c="xx"):
    print("world"+b+c)
    time.sleep(0.8)

if __name__ == '__main__':
    func_a("a")
    func_b("b", c="xxx")

使用場景

用哪些地方須要使用裝飾器呢?

  • 若是你用過locust,設置權重會用到@task(1),
  • 若是你用過pytest框架,使用fixture功能的時候常常會用到@pytest.fixture(scope="function")
  • allure裏面能夠添加測試步驟 @allure.step('修改購物車')
  • 被大量使用於Flask和Django web框架中,檢查是否被受權去使用一個web應用的端點(endpoint)。如 @login_required
  • 也能夠本身寫個裝飾器添加日誌

python自動化交流 QQ羣:779429633

相關文章
相關標籤/搜索