国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

Python 使用類寫裝飾器的小技巧

2020-01-04 14:25:54
字體:
供稿:網(wǎng)友

最近學(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

看看緩存的效果。

@cachedef rectangle_area(length, width):  return length * widthrectangle_area(2, 3)# calculated# 6rectangle_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é)果,效果一樣。

@Cachedef rectangle_area(length, width):  return length * widthrectangle_area(2, 3)# calculated# 6rectangle_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.widthr = Rectangle(2, 3)r.area()# calculated# 6r.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.widthr = 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.widthRectangle.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 wrapperclass Rectangle:  def __init__(self, length, width):    self.length = length    self.width = width  @method  @Cache  def area(self):    return self.length * self.widthr = Rectangle(2, 3)r.area()# calculated# 6r.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.widthr = Rectangle(2, 3)r.area# calculated# 6r.area# cached# 6

總結(jié)

用類寫裝飾器并非什么特別的技巧,一般情況下確實沒必要這么寫,不過這樣就可以用一些類的特性來寫裝飾器,比如類的繼承,也算是提供了另一種思路吧。


注:相關(guān)教程知識閱讀請移步到python教程頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 山阳县| 麻江县| 吉木乃县| 五指山市| 扶风县| 雅江县| 航空| 句容市| 新民市| 隆昌县| 南宫市| 手机| 威信县| 含山县| 锡林浩特市| 宣化县| 平江县| 嘉善县| 泸西县| 兰州市| 游戏| 如东县| 封开县| 西贡区| 桐城市| 读书| 兴海县| 哈尔滨市| 周宁县| 唐山市| 长阳| 泸水县| 通州市| 靖远县| 冷水江市| 阿图什市| 黎平县| 合山市| 漳州市| 漾濞| 青阳县|