# Decorator ### 用途: 靈活度高 易讀性高 協助封裝效果好 程式碼重複率低/簡潔度高 ### Decorator種類 不帶參數的function decorator 帶參數的function decorator 不帶參數的class decorator 帶參數的class decorator ### 不帶參數的function decorator ```python= def print_func_name(func): print('Now use print_func_name') def wrap_1(): print(f"Now use function: {func.__name__}") func() return wrap_1 def dog(): print("dog bark !") print_func_name(dog)() # Now use print_func_name # Now use function: dog # dog Bark ! @print_func_name def cat(): print("cat meow !") cat() # Now use print_func_name # Now use function: cat # cat meow ! ```` * 多層decorator * 多層decorator是採 ”recursive” 方式處理,若一個 function 有兩個以上的 decorators ,先合併「最靠近」的 decorator 傳出新的 function 給上面一個 decorator ! ```python= def print_func_2(func): # func = wrap_1 print('Now use print_func_2') #2 Now use print_func_2 def wrap_2(): #3 Now use function: cat # 若沒@wraps(func)-> func.__name__是wrap_1 print(f"Now use function: {func.__name__}") #4 Words for doc # 若沒@wraps(func)-> func.__doc__是None print(func.__doc__) func() #5 = wrap_1() return wrap_2 def print_func_1(func): print('Now use print_func_1') #1 Now use print_func_1 from functools import wraps @wraps(func) # copy attr of cat to wrap_1 def wrap_1(): # closure func #5 Now use function: cat print(f"Now use function: {func.__name__}") #6 Words for doc print(func.__doc__) func() #7 = cat() = print("cat meow !") return wrap_1 @print_func_2 @print_func_1 def cat(): # 先執行 print_func_1 再執行 print_func_2 'Words for doc' print("cat meow !") cat() # Now use print_func_1 # Now use print_func_2 # Now use function: cat # 沒@wraps(func)-> name會是 wrap_1 # Words for doc # 沒@wraps(func)-> doc會是 None # Now use function: cat # Words for doc # cat meow ! ``` * 裝飾器產生的問題 裝飾後返回的閉包,查看其屬性__doc__, __name__等,獲取到的是裝飾函數本身的屬性。 裝飾函數在實現前加入 **@functools.wraps(func)**,可將閉包的屬性更新為**被裝飾函數**之屬性。 ### 帶參數的function decorator 如果裝飾器本身帶有參數,則需要再包一層function。 ```python= def check_sex(sex: str): # 第一層 def 接收傳入的參數 def decorator(func): # 第二層 def 接收要被裝飾的func def wrapper(): if sex == 'm': print('I am male') if sex == 'f': print('I am female') func() return wrapper return decorator @check_sex('m') def cat(): print("cat meow !") cat() # I am male # cat meow ! ``` ### 不帶參數的class decorator 被裝飾的function 傳入 class 裡的 __init__。然後執行動作。在這例子裡,傳入的 function cat 以 self.talent 的方式宣告為 class 的 instance method。 也是將 function cat 「封裝」到 class Cat 的一種寫法。 ```python= class Cat: def __init__(self, func): self.age = 10 self.talent = func def meow(self): print("Meow !") @Cat def cat(): print("I can do a back flip") cat1 = cat print(cat1.age) # 10 cat1.meow() # Meow ! cat1.talent() # I can do a back flip ``` ### 帶參數的class decorator 比較不同的是,傳入的**function** 放在__call__裡,傳入的**參數**放在__init__裡。 __call__裡面再多包一層 wrapper 接收被裝飾function的參數(即num, *args,**kwargs) ```python= class Cat(object): def __init__(self, sex): self.sex = sex def __call__(self, func): # 被裝飾的func放call裡 def wrapper(num, *args, **kwargs): # 接收func參數 (num) # 調用傳入decorator class的參數 (sex) print(f"calling function {func.__name__} with sex: {self.sex}") # 調用func參數 (num) print(f"calling function {func.__name__} with num : {num}") func(2) # 執行function: cat(2) return wrapper @Cat('male') def cat(num, *arg,**kwarg): print(f"I can do {num} back flip") cat1 = cat cat1(1) # calling function cat with sex: male # calling function cat with parameter : 1 # I can do 2 back flip ```