Python Decorator 四種寫法範例 Code

Python Decorator 可以以 functionclass 實作出來,且有無參數的寫法也會因此不一樣,以下記錄了從 Stack Overflow 學習來的 decorator 寫法 {function, class} X {with, without} arguments = 4 種

Decorator Function

沒有參數的 Function 版本

  • Note: 使用 closure 去處理 function
def decorateApple(f):
    def d_f(*args, **kargs):
        print("apple before call")
        result = f(*args, **kargs)
        print("apple after call")
        return result
    return d_f

@decorateApple
def print_hello():
    print("hello first time.")

print_hello()
output
apple before call
hello first time.
apple after call

有參數的 Decorator Function

  • Note: 需要多一層的 outter function 來傳入參數
def decorateFruit(fruit, rotLevel):
    def outer_d_f(f):
        def d_f(*args, **kargs):
            print("%s %s before call" % (rotLevel, fruit))
            result = f(*args, **kargs)
            print("%s %s after call" % (rotLevel, fruit))
            return result
        return d_f
    return outer_d_f

@decorateFruit('banana', 'new')
def print_hello2():
    print("hello 2nd time.")

@decorateFruit('guava', '50% rot')
def print_hello3():
    print("hello 3th time.")

print_hello2()
print('')
print_hello3()
output
new banana before call
hello 2nd time.
new banana after call

50% rot guava before call
hello 3th time.
50% rot guava after call

Decorator Class

沒有參數版本的 Decorator Class

  • Note: Python 會將被修飾的 function 傳入 init,這時就將它存入到 decorator 的 data member self.f 。另外再直接覆寫 __call__ ,然後在呼叫 self.f 的前後作額外的修飾動作。
  • 這裡的 __call__ 的 signature ,就是跟 被修飾的 function 一樣,所以就加上動態的 *args, 跟 **kargs 就可以完整 delegate了。
class decorateAppleClass(object):
    def __init__(self, f):
        self.f = f
    
    def __call__(self, *args, **kargs):
        print("apple before call")
        result = self.f(*args, **kargs)
        print("apple after call")
        return result


@decorateAppleClass
def print_hello4():
    print("hello 4th time.")

print_hello4()
output
apple before call
hello 4th time.
apple after call

帶有參數版本的 Decorator Class

  • Note: 當有參數版本的 decorator class 在修飾 function 時,function 就不會像沒有參數版本的 decorator class 一樣,會將 function 傳入 init,而是會傳入 decorator 的參數。function 本體則是會在 call中被傳入,這是一個比較不一樣的地方。
  • 這裡的 __call__ 的 signature 則是 def __call__(self, f)
class decorateFruitClass(object):
    def __init__(self, fruit, rotLevel):
        self.fruit = fruit
        self.rotLevel = rotLevel

    def __call__(self, f):
        def d_f(*args, **kargs):
            print("%s %s before call" % (self.rotLevel, self.fruit))
            result = f(*args, **kargs)
            print("%s %s after call" % (self.rotLevel, self.fruit))
            return result
        return d_f

@decorateFruitClass('guava', '80% rot')
def print_hello5():
    print("hello 5th times.")

@decorateFruitClass('banana', '30% rot')
def print_hello6():
    print("hello 6th times.")

print_hello5()
print('')
print_hello6()
output
80% rot guava before call
hello 5nd time.
80% rot guava after call

30% rot banana before call
hello 6th time.
30% rot banana after call
comments powered by Disqus