# 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
```