Python 使用類寫(xiě)裝飾器的小技巧
最近學(xué)到了一個(gè)有趣的裝飾器寫(xiě)法,就記錄一下。
裝飾器是一個(gè)返回函數(shù)的函數(shù)。寫(xiě)一個(gè)裝飾器,除了最常見(jiàn)的在函數(shù)中定義函數(shù)以外,Python還允許使用類來(lái)定義一個(gè)裝飾器。
1、用類寫(xiě)裝飾器
下面用常見(jiàn)的寫(xiě)法實(shí)現(xiàn)了一個(gè)緩存裝飾器。
def cache(func): data = {} def wrapper(*args, **kwargs): key = f'{func.__name__}-{str(args)}-{str(kwargs)})' if key in data: result = data.get(key) print('cached') else: result = func(*args, **kwargs) data[key] = result print('calculated') return result return wrapper
看看緩存的效果。
@cache def rectangle_area(length, width): return length * width rectangle_area(2, 3) # calculated # 6 rectangle_area(2, 3) # cached # 6
裝飾器的@cache是一個(gè)語(yǔ)法糖,相當(dāng)于func = cache(func)
,如果這里的cache不是一個(gè)函數(shù),而是一個(gè)類又會(huì)怎樣呢?定義一個(gè)類class Cache, 那么調(diào)用func = Cache(func)
會(huì)得到一個(gè)對(duì)象,這時(shí)返回的func其實(shí)是Cache的對(duì)象。定義__call__方法可以將類的實(shí)例變成可調(diào)用對(duì)象,可以像調(diào)用函數(shù)一樣調(diào)用對(duì)象。然后在__call__方法里調(diào)用原本的func函數(shù)就能實(shí)現(xiàn)裝飾器了。所以Cache類也能當(dāng)作裝飾器使用,并且能以@Cache的形式使用。
接下來(lái)把cache函數(shù)改寫(xiě)為Cache類:
class Cache: def __init__(self, func): self.func = func self.data = {} def __call__(self, *args, **kwargs): func = self.func data = self.data key = f'{func.__name__}-{str(args)}-{str(kwargs)})' if key in data: result = data.get(key) print('cached') else: result = func(*args, **kwargs) data[key] = result print('calculated') return result
再看看緩存結(jié)果,效果一樣。
@Cache def rectangle_area(length, width): return length * width rectangle_area(2, 3) # calculated # 6 rectangle_area(2, 3) # cached # 6
2、裝飾類的方法
裝飾器不止能裝飾函數(shù),也經(jīng)常用來(lái)裝飾類的方法,但是我發(fā)現(xiàn)用類寫(xiě)的裝飾器不能直接用在裝飾類的方法上。(有點(diǎn)繞…)
先看看函數(shù)寫(xiě)的裝飾器如何裝飾類的方法。
class Rectangle: def __init__(self, length, width): self.length = length self.width = width @cache def area(self): return self.length * self.width r = Rectangle(2, 3) r.area() # calculated # 6 r.area() # cached # 6
但是如果直接換成Cache類會(huì)報(bào)錯(cuò),這個(gè)錯(cuò)誤的原因是area被裝飾后變成了類的一個(gè)屬性,而不是方法。
class Rectangle: def __init__(self, length, width): self.length = length self.width = width @Cache def area(self): return self.length * self.width r = Rectangle(2, 3) r.area() # TypeError: area() missing 1 required positional argument: 'self' Rectangle.area # <__main__.Cache object at 0x0000012D8E7A6D30> r.area # <__main__.Cache object at 0x0000012D8E7A6D30>
回頭再來(lái)看看沒(méi)有裝飾器的情況,Python在實(shí)例化對(duì)象后把函數(shù)變成了方法。
class Rectangle: def __init__(self, length, width): self.length = length self.width = width def area(self): return self.length * self.width Rectangle.area # <function Rectangle.area at 0x0000012D8E7B28C8> r = Rectangle(2, 3) r.area # <bound method Rectangle.area of <__main__.Rectangle object
因此解決辦法很簡(jiǎn)單,要用類寫(xiě)的裝飾器來(lái)裝飾類的方法,只需要把可調(diào)用對(duì)象包裝成函數(shù)就行。
# 定義一個(gè)簡(jiǎn)單的裝飾器,什么也不做,僅僅是把可調(diào)用對(duì)象包裝成函數(shù) def method(call): def wrapper(*args, **kwargs): return call(*args, **kwargs) return wrapper class Rectangle: def __init__(self, length, width): self.length = length self.width = width @method @Cache def area(self): return self.length * self.width r = Rectangle(2, 3) r.area() # calculated # 6 r.area() # cached # 6
或者用@property還能直接把方法變成屬性。
class Rectangle: def __init__(self, length, width): self.length = length self.width = width @property @Cache def area(self): return self.length * self.width r = Rectangle(2, 3) r.area # calculated # 6 r.area # cached # 6
總結(jié)
用類寫(xiě)裝飾器并非什么特別的技巧,一般情況下確實(shí)沒(méi)必要這么寫(xiě),不過(guò)這樣就可以用一些類的特性來(lái)寫(xiě)裝飾器,比如類的繼承,也算是提供了另一種思路吧。
- python 提高開(kāi)發(fā)效率的5個(gè)小技巧
- 使用Python操作MySQL的小技巧
- 常用的10個(gè)Python實(shí)用小技巧
- Python中使用filter過(guò)濾列表的一個(gè)小技巧分享
- 使用Python解決Windows文件名非用反斜杠問(wèn)題(python 小技巧)
- 詳解Python中正則匹配TAB及空格的小技巧
- 詳解10個(gè)可以快速用Python進(jìn)行數(shù)據(jù)分析的小技巧
- 10個(gè)Python小技巧你值得擁有
- Python爬蟲(chóng)小技巧之偽造隨機(jī)的User-Agent
- Python爬蟲(chóng)常用小技巧之設(shè)置代理IP
- 使用python將大量數(shù)據(jù)導(dǎo)出到Excel中的小技巧分享
- python小技巧——將變量保存在本地及讀取
相關(guān)文章
詳解pycharm的python包opencv(cv2)無(wú)代碼提示問(wèn)題的解決
這篇文章主要介紹了詳解pycharm的python包opencv(cv2)無(wú)代碼提示問(wèn)題的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01Perl中著名的Schwartzian轉(zhuǎn)換問(wèn)題解決實(shí)現(xiàn)
這篇文章主要介紹了Perl中著名的Schwartzian轉(zhuǎn)換問(wèn)題解決實(shí)現(xiàn),本文詳解講解了Schwartzian轉(zhuǎn)換涉及的排序問(wèn)題,并同時(shí)給出實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-06-06DataFrame:通過(guò)SparkSql將scala類轉(zhuǎn)為DataFrame的方法
今天小編就為大家分享一篇DataFrame:通過(guò)SparkSql將scala類轉(zhuǎn)為DataFrame的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01Python進(jìn)行數(shù)組的排序、倒序、截取方式
這篇文章主要介紹了Python進(jìn)行數(shù)組的排序、倒序、截取方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02python打開(kāi)網(wǎng)頁(yè)和暫停實(shí)例
這篇文章主要介紹了python打開(kāi)網(wǎng)頁(yè)和暫停實(shí)例,涉及webbrowser模塊與os模塊的使用,是非常具有實(shí)用價(jià)值的一個(gè)技巧,需要的朋友可以參考下2014-09-09Python 中 Virtualenv 和 pip 的簡(jiǎn)單用法詳解
本篇文章主要介紹了Python 中 Virtualenv 和 pip 的簡(jiǎn)單用法詳解,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5復(fù)選框控件QCheckBox詳細(xì)使用方法與實(shí)例
這篇文章主要介紹了python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5復(fù)選框控件QCheckBox詳細(xì)使用方法與實(shí)例,需要的朋友可以參考下2020-02-02Python字典操作詳細(xì)介紹及字典內(nèi)建方法分享
這篇文章主要介紹了Python字典操作詳細(xì)介紹及字典內(nèi)建方法分享,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01