類別修飾器


所謂 函式修飾器,指的是對函式作修飾,基於原函式的處理結果再作加工。你也可以對類別作修飾,也就是所謂類別修飾器,如果你如下撰寫:
@decorator
class Some:
    pass

其結果相當於:
class Some:
    pass
Some = decorator(Some)

例如,若 函式修飾器 中的主餐改使用類別封裝:
class FriedChicken:
    def getContent(self):
        return "不黑心炸雞"
    def price(self):
        return 49.0

若你想要對FriedChicken類別作修飾,使其加上附餐功能,則可以設計一個函式如下:
def dish1(clz):
    class SideDishOne:
        def __init__(self):
            self.meal = clz()  
        def getContent(self):
            return self.meal.getContent() + " | 可樂 | 薯條"
        def price(self):
            return self.meal.price() + 30.0
           
    return SideDishOne

那麼你可以如下使用,使FriedChicken具有附餐:
def dish1(clz):
class SideDishOne:
def __init__(self):
self.meal = clz()

def getContent(self):
return self.meal.getContent() + " | 可樂 | 薯條"

def price(self):
return self.meal.price() + 30.0

return SideDishOne

@dish1
class FriedChicken:
def getContent(self):
return "不黑心炸雞"
def price(self):
return 49.0

friedchicken = FriedChicken()
print(friedchicken.getContent()) # 不黑心炸雞 | 可樂 | 薯條
print(friedchicken.price()) # 79.0

這是將修飾器使用在類別上的例子。除了使用函式來定義修飾器之外,你也可以使用類別來定義修飾器,在了解如何使用類別定義修飾器之前,你要先了解__call__()方法的作用:
class Some:
def __call__(self, *args):
for arg in args:
print(arg, end=' ')
print()

s = Some()
s(1)
s(1, 2)
s(1, 2, 3)

簡單來說,如果一個物件上具有__call__()方法,則其實例可以使用圓括號來傳入引數,此時會呼叫實例__call__()方法,就如上面的例子所示範的。

所以若要使用類別來定義函式修飾器,其作法為:
class decorator:
    def __init__(self, func):
        self.func = func
    def __call__(self, *args):
        result = self.func()
        # 對 result 作修飾(傳回)

@decorator
def some(arg):
    pass

some(1)

上例其實相當於:
some = decorator(some)
some(1)   # 呼叫 some.__call__(1)

也就是使用decorator建立實例,之後對some所參考實例的呼叫,其實都轉換為對__call__()方法的呼叫。所以,若要使用類別來作對 函式修飾器 中點餐程式的修飾,則可以如下:
class dish1:
def __init__(self, func):
self.func = func
def __call__(self):
return self.func() + 30

@dish1
def friedchicken():
return 49.0

print(friedchicken()) # 79.0

若要使用類別實作對類別的修飾器,則原理是:
class decorator:
    def __init__(self, clz):
        self.clz = clz
    def __call__(self, *args):
        class wrapper:
            def __init__(self, *args):
                self.wrapped = self.clz(*args)
            # 其它的方法
        return wrapper(*args)

@decorator
class Some:
    pass

上面的例子,其實相當於:
Some = decorator(Some)
s = Some(1, 2)  # 其實是呼叫Some.__call__(1, 2)傳回wrapper實例

例如若使用類別來對FriedChicken作修飾,則可以如下實作:
class dish1:
def __init__(self, clz):
self.clz = clz

def __call__(self):
class SideDishOne:
def __init__(self, meal):
self.meal = meal
def getContent(self):
return self.meal.getContent() + " | 可樂 | 薯條"
def price(self):
return self.meal.price() + 30.0

return SideDishOne(self.clz())

@dish1
class FriedChicken:
def getContent(self):
return "不黑心炸雞"
def price(self):
return 49.0

friedchicken = FriedChicken()
print(friedchicken.getContent()) # 不黑心炸雞 | 可樂 | 薯條
print(friedchicken.price()) # 79.0