Python 使用類寫裝飾器的小技巧
最近學(xué)到了一個有趣的裝飾器寫法,就記錄一下。
裝飾器是一個返回函數(shù)的函數(shù)。寫一個裝飾器,除了最常見的在函數(shù)中定義函數(shù)以外,Python還允許使用類來定義一個裝飾器。
1、用類寫裝飾器
下面用常見的寫法實現(xiàn)了一個緩存裝飾器。
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是一個語法糖,相當(dāng)于func = cache(func),如果這里的cache不是一個函數(shù),而是一個類又會怎樣呢?定義一個類class Cache, 那么調(diào)用func = Cache(func)會得到一個對象,這時返回的func其實是Cache的對象。定義__call__方法可以將類的實例變成可調(diào)用對象,可以像調(diào)用函數(shù)一樣調(diào)用對象。然后在__call__方法里調(diào)用原本的func函數(shù)就能實現(xiàn)裝飾器了。所以Cache類也能當(dāng)作裝飾器使用,并且能以@Cache的形式使用。
接下來把cache函數(shù)改寫為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)常用來裝飾類的方法,但是我發(fā)現(xiàn)用類寫的裝飾器不能直接用在裝飾類的方法上。(有點繞…)
先看看函數(shù)寫的裝飾器如何裝飾類的方法。
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類會報錯,這個錯誤的原因是area被裝飾后變成了類的一個屬性,而不是方法。
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>
回頭再來看看沒有裝飾器的情況,Python在實例化對象后把函數(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
因此解決辦法很簡單,要用類寫的裝飾器來裝飾類的方法,只需要把可調(diào)用對象包裝成函數(shù)就行。
# 定義一個簡單的裝飾器,什么也不做,僅僅是把可調(diào)用對象包裝成函數(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é)
用類寫裝飾器并非什么特別的技巧,一般情況下確實沒必要這么寫,不過這樣就可以用一些類的特性來寫裝飾器,比如類的繼承,也算是提供了另一種思路吧。
- python 提高開發(fā)效率的5個小技巧
- 使用Python操作MySQL的小技巧
- 常用的10個Python實用小技巧
- Python中使用filter過濾列表的一個小技巧分享
- 使用Python解決Windows文件名非用反斜杠問題(python 小技巧)
- 詳解Python中正則匹配TAB及空格的小技巧
- 詳解10個可以快速用Python進行數(shù)據(jù)分析的小技巧
- 10個Python小技巧你值得擁有
- Python爬蟲小技巧之偽造隨機的User-Agent
- Python爬蟲常用小技巧之設(shè)置代理IP
- 使用python將大量數(shù)據(jù)導(dǎo)出到Excel中的小技巧分享
- python小技巧——將變量保存在本地及讀取
相關(guān)文章
Python爬蟲框架Scrapy實戰(zhàn)之批量抓取招聘信息
網(wǎng)絡(luò)爬蟲又被稱為網(wǎng)頁蜘蛛,網(wǎng)絡(luò)機器人,在FOAF社區(qū)中間,更經(jīng)常的稱為網(wǎng)頁追逐者,是按照一定的規(guī)則,自動抓取萬維網(wǎng)信息的程序或者腳本。這篇文章主要介紹Python爬蟲框架Scrapy實戰(zhàn)之批量抓取招聘信息,有需要的朋友可以參考下2015-08-08
Pandas數(shù)據(jù)分組統(tǒng)計的實現(xiàn)示例
對數(shù)據(jù)進行分組統(tǒng)計,主要適用DataFrame對象的groupby()函數(shù),本文就來詳細的介紹下Pandas數(shù)據(jù)分組統(tǒng)計的實現(xiàn),具有一定的參考價值,感興趣的可以了解下2023-11-11
python實現(xiàn)將文本轉(zhuǎn)換成語音的方法
這篇文章主要介紹了python實現(xiàn)將文本轉(zhuǎn)換成語音的方法,涉及Python中pyTTS模塊的相關(guān)使用技巧,需要的朋友可以參考下2015-05-05

