溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python中怎么利用類實現(xiàn)一個裝飾器

發(fā)布時間:2021-06-16 16:11:14 來源:億速云 閱讀:210 作者:Leah 欄目:開發(fā)技術(shù)

Python中怎么利用類實現(xiàn)一個裝飾器,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

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是一個語法糖,相當于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類也能當作裝飾器使用,并且能以@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

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業(yè)資訊頻道,感謝您對億速云的支持。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI