日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

Python 装饰器初探

發(fā)布時(shí)間:2023/12/10 python 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python 装饰器初探 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Python 裝飾器初探

在談及Python的時(shí)候,裝飾器一直就是道繞不過(guò)去的坎。面試的時(shí)候,也經(jīng)常會(huì)被問(wèn)及裝飾器的相關(guān)知識(shí)。總感覺(jué)自己的理解很淺顯,不夠深刻。是時(shí)候做出改變,對(duì)Python的裝飾器做個(gè)全面的了解了。

1. 函數(shù)裝飾器

直接上代碼,看看裝飾器到底干了些什么?

from functools import wraps import timedef time_cost(func):@wraps(func)def f(*args, **kwargs):start_time = time.time()func(*args, **kwargs)end_time = time.time()print(end_time - start_time)return f@time_cost def test(*args, **kwargs):time.sleep(1.0)if __name__ == "__main__":test()

上面的Python代碼,運(yùn)行后,會(huì)給出test函數(shù)的執(zhí)行時(shí)間。代碼的執(zhí)行順序大概如下,首先是將test作為值傳遞給time_cost函數(shù),返回函數(shù)f,然后再調(diào)用f,這是帶有time_cost裝飾器的test函數(shù)的大致執(zhí)行過(guò)程。

從中,不難看出,即使不使用裝飾器符號(hào),我們利用Python的語(yǔ)言特性,也能達(dá)成上述目的。用裝飾器符號(hào)的好處是簡(jiǎn)化了代碼,增加了代碼的可讀性。

這是一段非常簡(jiǎn)單的對(duì)函數(shù)使用裝飾器的Python代碼。等等,@wraps(func)是什么鬼?悄悄干了什么哇?

我們稍微修改下上述代碼,結(jié)果如下:

from functools import wraps import timedef time_cost(func):def f(*args, **kwargs):start_time = time.time()func(*args, **kwargs)end_time = time.time()print(end_time - start_time)print('hello world')return f@time_cost def test(*args, **kwargs):time.sleep(1.0)if __name__ == "__main__":print(test.__name__)

發(fā)現(xiàn)輸出了hello world,同時(shí)輸出test.__name__,居然變成了f,并不是我們預(yù)期的test。根據(jù)這樣的輸出結(jié)果,我們不難得出,其實(shí)被裝飾器time_cost修飾過(guò)的函數(shù)test本質(zhì)上已經(jīng)等同于time_cost(test),此時(shí)訪問(wèn)test.__name__實(shí)際上訪問(wèn)的是time_cost(test).__name__,得到的當(dāng)然就是f啦。當(dāng)我們加上@wraps(func),此時(shí)test.__name__變成了test。

下面介紹帶參數(shù)的裝飾器,更加難了。在談?wù)搸?shù)的裝飾器之間,首先得引入一個(gè)概念,那就”閉包“。如果你以前用過(guò)腳本語(yǔ)言,比如JavaScript,那么一定會(huì)很熟悉閉包這個(gè)概念。下面是一個(gè)閉包樣例

def add(a):def wrapper(c):return a + creturn wrapperif __name__ == "__main__":add3 = add(3)add9 = add(9)print(add3(4) == 7)print(add9(1) == 10)

從中可以看出,在調(diào)用add3的時(shí)候,wrapper內(nèi)部還可以訪問(wèn)到a的值,這就是閉包的作用。理解了閉包,理解帶參數(shù)的裝飾器就容易多了。

from functools import wrapsdef logging(level):def outer_wrapper(func):@wraps(func)def inner_wrapper(*args, **kwargs):print("[{level}]: enter function {func}()".format(level=level,func=func.__name__))return func(*args, **kwargs)return inner_wrapperreturn outer_wrapper@logging(level='WARN') def show(msg):print('message:{}'.format(msg))if __name__ == "__main__":show('hello world!')

上面給出了一個(gè)帶參數(shù)裝飾器的示例。根據(jù)我們前面的鋪墊,我們不難分析得出,上面的執(zhí)行過(guò)程是logging(level='WARN')->outer_wrapper(show)->inner_wrapper(),所以我們可以理解,在被logging修飾后的show其實(shí)就是logging(level='WARN')(show),執(zhí)行show('hello world!')其實(shí)就是在執(zhí)行l(wèi)ogging(level='WARN')(show)()。注意與不帶參數(shù)的裝飾器的區(qū)別,帶參數(shù)的裝飾器比不帶參數(shù)的裝飾器多套了一層,對(duì)應(yīng)的裝飾器也有了調(diào)用。因?yàn)樵谑褂醚b飾器的時(shí)候,帶了括號(hào),所以裝飾器本身多套了一層。被裝飾器修飾過(guò)的函數(shù)在被調(diào)用的時(shí)候,實(shí)際上執(zhí)行的是裝飾器最內(nèi)層的函數(shù),其余層的在函數(shù)被修飾時(shí)就已經(jīng)執(zhí)行了。

是不是覺(jué)得非常自然?對(duì)的,我以前對(duì)裝飾器的理解也就停留在不帶參數(shù)的裝飾器這一深度。

2. 基于類實(shí)現(xiàn)的裝飾器

依然先上代碼

from functools import wraps import timeclass time_cost:def __init__(self, func):self.func = funcdef __call__(self, *args, **kwargs):start_time = time.time()result = self.func(*args, **kwargs)end_time = time.time()print(end_time - start_time)return result@time_cost def test(*args, **kwargs):time.sleep(1.0)if __name__ == "__main__":test()

上面的基于類實(shí)現(xiàn)的不帶參數(shù)的裝飾器實(shí)際上利用的是Python中的可調(diào)用對(duì)象特性,凡是實(shí)現(xiàn)了__call__方法的類的實(shí)例是可以被調(diào)用的。因此被time_cost修飾過(guò)的test函數(shù)本質(zhì)上已經(jīng)變成了time_cost類的實(shí)例了。調(diào)用test方法的時(shí)候,實(shí)際上執(zhí)行的是__call__方法。

下面介紹稍微復(fù)雜一點(diǎn)的基于類實(shí)現(xiàn)的帶有參數(shù)的裝飾器。

from functools import wrapsclass logging:def __init__(self, level):self.level = leveldef __call__(self, func):@wraps(func)def wrapper(*args, **kwargs):print("[{level}]: enter function {func}()".format(level=self.level,func=func.__name__))return func(*args, **kwargs)return wrapper@logging(level='WARN') def show(msg):print('message:{}'.format(msg))if __name__ == "__main__":show('hello world!')

不同于基于類實(shí)現(xiàn)的不帶參數(shù)的裝飾器,基于類實(shí)現(xiàn)的帶參數(shù)的裝飾器在__call__里面多了一層wrapper。被裝飾器修飾的show方法本質(zhì)上是logging(level='WARN')(show),此時(shí)調(diào)用show方法,實(shí)際上執(zhí)行的是wrapper方法。

現(xiàn)在看來(lái),其實(shí)裝飾器也沒(méi)有很復(fù)雜,在實(shí)際的項(xiàng)目中用裝飾器可以帶來(lái)很大便利。

轉(zhuǎn)載于:https://www.cnblogs.com/crackpotisback/p/10197698.html

總結(jié)

以上是生活随笔為你收集整理的Python 装饰器初探的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。