淺談 Python 中的Decorator(下)
前言
前一篇有提到Decorator初階的概念,這篇再來更深入探討Decorator有哪些做法吧!
範例
先來兩個範例,註解的地方是執行順序
Function-based
# Decorator Function Sample
def logged(func):
print('scope of logged') # 2 進入logged scope
print("Entering function name is: {}".format(func.__name__)) # 3
def with_logging(*args, **kwargs):
print('scope of with_logging') # 6
print("Entering function name is: {}".format(func.__name__)) # 7
print("arg:",*args) # 8
print("kwarg:",**kwargs) # 9
result = func(*args, **kwargs)
print("Exited", func.__name__) # 11
return result
print('exit') # 4
return with_logging
print('start using decorator') # 1
@logged # 呼叫logged
def test(k):
print("excute k: {}".format(k)) # 10
return k
print('excute') # 5
print(test(10)) # 12
輸出:
Class-based
# Decorator Class Sample
class decorator(object):
def __init__(self, f):
print('enter init')
self.f = f
print('exit init') def __call__(self, *args, **kwargs):
print("Entering", self.f.__name__)
r = self.f(*args, **kwargs)
print("Exited", self.f.__name__)
return r
print('start decorator ')
@decorator
def hello(k):
print('inside hello')
return("k is: " + k)
print('excute')
print(hello('say hello!'))
印出結果:
執行完上述兩個範例程式碼,大概可以了解使用Class-based
、Function-based
兩者使用Decorator的差別以及執行順序。
接著來細分Decorator種類有哪幾種吧!
Decorator種類
- 不帶參數的function decorator
- 帶有參數的function decorator
- 不帶參數的class decorator
- 帶有參數的class decorator
不帶參數的function decorator
# 不帶參數的 Decorator Function
def decorator(f):
print("excute \"{}\" decorate".format(f.__name__))
def print_df(*args, **kargs):
print("print_df before call")
result = f(*args, **kargs) # 呼叫 hello(),並將回傳值傳遞給result
print("print_df after call")
print("印出回傳值: {}".format(result))
return result
return print_df
@decorator
def hello():
print("say hello.")
return "hello"hello()
印出結果:
帶有參數的function decorator
# 帶有參數的 Decorator Function
def parseDecorator(param1, param2):
print("excute 'parseDecorator'") # 解析parseDecorator內的參數
def decorator(f):
print("excute \"{}\" decorate".format(f.__name__))
def print_df(*args, **kargs):
print("params are: '{}', '{}' ".format(param1, param2))
print("print_df before call")
result = f(*args, **kargs) # 呼叫 hello(),並將回傳值傳遞給result
print("print_df after call")
print("印出回傳值: {}".format(result))
return result
return print_df
return decorator
@parseDecorator("param1", "param2")
def hello():
print("say hello.")
return "hello"hello()
印出結果:
比較有無參數的function decorator
藉由上述兩個例子觀察差異,帶有參數的decorator需在decorator外層再包一層function,而最外層稱作parseDecorator
的function是用來解析decorator所傳遞的參數,parseDecorator
的下一層decorator(f)
是主要要修飾的函式,跟不帶參數的decorator作用發訪相同。
簡單來說就是將無參數的function decorator,外面再包一層function,用來傳遞參數。
不帶參數的class decorator
# 沒有參數的 Decorator Class
class decorator():
def __init__(self, f): # 對參數、函式進行初始化
self.f = f
def __call__(self, *args, **kargs):
print("person1 before call")
result = self.f() # 呼叫 person1()獲得回傳值,並將回傳值assign給result
print("result: {}".format(result)) # 印出回傳值
print("person1 after call")
@decorator
def person1():
print("I'm person1")
return "person1"person1()
輸出結果:
帶有參數的class decorator
# 有參數的 Decorator Class
class decorateFruitClass(object):
def __init__(self, fruit, amount):
self.fruit = fruit
self.amount = amount
def __call__(self, f):
def buy(*args, **kargs):
print("%s %s before call" % (self.amount, self.fruit))
result = f(*args, **kargs)
print("%s %s after call" % (self.amount, self.fruit))
return result
return buy
@decorateFruitClass('guava', 10)
def person1():
print("I'm person1.")@decorateFruitClass('banana', 20)
def person2():
print("I'm person2.")person1()
print('-------')
person2()
輸出結果:
Decorator 的有序性
如果decorators多層的話,執行的順序可能會和一般的想法不太一樣。
def walk(func):
print("walking outside")
def warp_1():
print("walking inside")
print("Now use function '{}'".format(func.__name__))
func() # call warp_2()
return warp_1 # call warp_1()
def jump(func):
print("jumping outside")
def warp_2():
print("jumping inside")
print("Now use function '{}'".format(func.__name__))
print(func()) # call student()
return warp_2 # 傳出warp_2給@walk@walk
@jump
def student():
print("I'm a student")
return "student"student()
執行結果
上圖的執行順序是,先呼叫@jump
後傳出新function(傳出warp_2
)給往上一層的@walk
,再walk(func)
中依序執行warp_1()
,最後呼叫warp_2()
。
遇到多層decorators時,邏輯處理採用 “遞迴(recursive)” 的方式,也就是說,原則上會先合併「最靠近」的decorator,再傳出新的function給往上面一層的decorator
參閱
Python中有不同類型的Decorator,透過實際範例能夠更了解不同類型的Decorator是如何去運行!
如果觀念上有不正確或是文章內容有錯誤之處
還請看過文章的人指教!