Python-裝飾器

一、前言

python裝飾器本質(zhì)上就是一個(gè)函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動(dòng)的前提下增加額外的功能,裝飾器的返回值也是一個(gè)函數(shù)對(duì)象。
Python中一切皆可對(duì)象

二、函數(shù)帶和不帶()的區(qū)別

def test():
    print('test ist running')
test()
print(test)
test_two = test
test_two()
#輸出
test ist running
<function test at 0x0000021ED072C268>
test ist running

如代碼所示,test()和test輸出是不同的,這是因?yàn)楫?dāng)你把一對(duì)小括號(hào)放在后面,這個(gè)函數(shù)就會(huì)執(zhí)行;然而如果你不放括號(hào)在它后面,它是一個(gè)對(duì)象,那它可以被到處傳遞,并且可以賦值給別的變量而不去執(zhí)行它

三、簡(jiǎn)單的裝飾器

def use_logging(func):
    def wrapper():
        """wrapper()"""
        print('%s start running' % func.__name__)
        f = func()
        print('%s stop running' % func.__name__)
        return f
    return wrapper
@use_logging
def foo():
    """foo()"""
    print("i am foo")
foo()
#output
foo start running
i am foo
foo stop running

or(代碼稍微不同,可以控制執(zhí)行順序吧)
2024-05-22改
上面的f = func()會(huì)執(zhí)行函數(shù),所以順序變了

def use_logging(func):
    def wrapper():
        """wrapper()"""
        print('%s start running' % func.__name__)
        #f = func()
        print('%s stop running' % func.__name__)
        return func()
    return wrapper
@use_logging
def foo():
    """foo()"""
    print("i am foo")
foo()
#output
foo start running
foo stop running
i am foo

use_logging 就是一個(gè)裝飾器,它是一個(gè)普通的函數(shù),它把執(zhí)行真正業(yè)務(wù)邏輯的函數(shù) func 包裹在其中,看起來(lái)像 foo 被 use_logging 裝飾了一樣,use_logging 返回的也是一個(gè)函數(shù),這個(gè)函數(shù)的名字叫 wrapper。

如果業(yè)務(wù)函數(shù)帶有參數(shù),且不確定有幾個(gè)參數(shù),這是就需要用*args,**kwargs兩兄弟了。

def use_logging(func):
    def wrapper(*args, **kwargs):
        """wrapper()"""
        print('%s start running' % func.__name__)
        #f = func(*args, **kwargs)
        print('%s stop running' % func.__name__)
        return func(*args, **kwargs)
    return wrapper

六、warps

__name__用于獲取函數(shù)的名稱,__doc__用于獲取函數(shù)的docstring內(nèi)容(函數(shù)的注釋)

在上面章節(jié)的示例代碼加入如下代碼
print(foo.__name__)
print(foo.__doc__)
#output
wrapper
wrapper()

可以發(fā)現(xiàn)foo函數(shù)的名稱和注釋都被裝飾器的wrapper函數(shù)覆蓋了。
Python提供了一個(gè)簡(jiǎn)單函數(shù)解決了這個(gè)問(wèn)題。

from functools import wraps
def use_logging(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """wrapper()"""
        print('%s start running' % func.__name__)
        #f = func(*args, **kwargs)
        print('%s stop running' % func.__name__)
        return func(*args, **kwargs)
    return wrapper
@use_logging
def foo():
    """foo()"""
    print("i am foo")
print(foo.__name__)
print(foo.__doc__)
#output
foo
foo()

@wraps接受一個(gè)函數(shù)來(lái)進(jìn)行裝飾,并加入了復(fù)制函數(shù)名稱、注釋文檔、參數(shù)列表等等的功能。這可以讓我們?cè)谘b飾器里面訪問(wèn)在裝飾之前的函數(shù)的屬性。

五、裝飾器帶參數(shù)

import logging
from functools import wraps
def use_logging(level=‘warn’):
    @wraps(func)
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                logging.warning("%s is running" % func.__name__)
            elif level == "info":
                logging.info("%s is running" % func.__name__)
            return func(*args)
        return wrapper

    return decorator

@use_logging(level="warn")
def foo(name='foo'):
    print("i am %s" % name)
foo()

上面的 use_logging是允許帶參數(shù)的裝飾器。它實(shí)際上是對(duì)原有裝飾器的一個(gè)函數(shù)封裝,并返回一個(gè)裝飾器。我們可以將它理解為一個(gè)含有參數(shù)的閉包。當(dāng)我 們使用@use_logging(level="warn")調(diào)用的時(shí)候,Python 能夠發(fā)現(xiàn)這一層的封裝,并把參數(shù)傳遞到裝飾器的環(huán)境中。
@use_logging(level="warn") 等價(jià)于 @decorator

六、類裝飾器

關(guān)于call方法,不得不先提到一個(gè)概念,就是可調(diào)用對(duì)象(callable),我們平時(shí)自定義的函數(shù)、內(nèi)置函數(shù)和類都屬于可調(diào)用對(duì)象,
但凡是可以把一對(duì)括號(hào)()應(yīng)用到某個(gè)對(duì)象身上都可稱之為可調(diào)用對(duì)象,判斷對(duì)象是否為可調(diào)用對(duì)象可以用函數(shù) callable。
如果在類中實(shí)現(xiàn)了call方法,那么實(shí)例對(duì)象也將成為一個(gè)可調(diào)用對(duì)象

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)     # 原函數(shù)
        end = time.time()
        print("運(yùn)行時(shí)長(zhǎng):%.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")

類裝飾器帶參數(shù)

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)     # 原函數(shù)
            end = time.time()
            t = end-start
            time.sleep((self.slowly-1)*t)  # 延遲效果
            new_end = time.time()
            print("運(yùn)行時(shí)長(zhǎng):%.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")

參考:
菜鳥(niǎo)教程
上海-悠悠

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 每個(gè)人都有的內(nèi)褲主要功能是用來(lái)遮羞,但是到了冬天它沒(méi)法為我們防風(fēng)御寒,咋辦?我們想到的一個(gè)辦法就是把內(nèi)褲改造一下,...
    chen_000閱讀 1,403評(píng)論 0 3
  • 如果看完這一篇文章還不理解裝飾器,這說(shuō)明我寫(xiě)的還不夠清晰、詳細(xì),那請(qǐng)鼓勵(lì)鼓勵(lì)我吧。 講 Python 裝飾器前,我...
    龍皓晨閱讀 756評(píng)論 0 8
  • 談裝飾器前,還要先要明白一件事,Python 中的函數(shù)和 Java、C++不太一樣,Python 中的函數(shù)可以像普...
    明日孤風(fēng)寒閱讀 322評(píng)論 0 0
  • 談到裝飾器,要先要明白一件事,Python 中的函數(shù)和 Java、C++不太一樣,Python 中的函數(shù)可以像普通...
    Guang777閱讀 444評(píng)論 0 2
  • 其實(shí)人和人,到最后的區(qū)別,就是這一個(gè)一個(gè)坎兒,你能不能熬過(guò)去,過(guò)去了你就不一樣了。一生中總會(huì)遇到這樣的時(shí)候,你的內(nèi)...
    強(qiáng)強(qiáng)_62f8閱讀 142評(píng)論 0 1

友情鏈接更多精彩內(nèi)容