裝飾器
有時會修改既有的函式,但不想更改原始程式碼 → 最常見的例子:加入一個除錯的陳述式,來查看有哪些引數被傳入
裝飾器(decorator)是一種函式,它會接收一個函式,並回傳另一個函式
裝飾器(decorator)是一種函式
裝飾器使用的元素:
- *args 與 **kwargs
- 內部函式
- 當成引數的函式
定義裝飾器【使用 document_it() 函式】
document_it() 函式會定義一個裝飾器,它會做以下的工作:
- 印出 → 函式的名稱,與引數的值
- 執行函式 → 用引數
- 印出 → 結果
- 回傳 → 修改後的函式,以供使用
>>> def document_it(func): # document_it() 函式會定義一個裝飾器
def 函式名稱1(*args, **kwargs):
print('文字1:', func.__name__) # 印出 → 函式的名稱
print('文字2:', args) # 印出 → 引數的值
print('文字3:', kwargs) # 印出 → 引數的值
result = func(*args, **kwargs)
print('Result:', result) # 印出 → 結果
return result
return 函式名稱1 # 回傳 → 修改後的函式,以供使用
# 裝飾器不一定要執行任何 func 的程式,但 document_it()會在過程中呼叫 func → 可以取得 func 的結果以及所有額外的東西
>> def document_it(func): # 無論將什麼 func 傳給 document_it,都可以得到一個新的函式,裡面有 document_it() 加入的陳述式
def new_function(*args, **kwargs):
print('Running function:', func.__name__)
print('Positional arguments:', args)
print('Keyword arguments:', kwargs)
result = func(*args, **kwargs)
print('Result:', result)
return result
return new_function
手動指派裝飾器
>>> def 函式名稱2(引數1, 引數2):
引數1 + 引數2
>>> 函式名稱2(引數1, 引數2):
引數1 + 引數2
>>> 函式名稱2 = document_it(函式名稱1)
>>> 函式名稱3(引數1, 引數2)
文字1: 函式名稱1
文字2: (引數1, 引數2)
文字3: {}
result: 引數1 + 引數2
引數1 + 引數2
>>> def add_ints(a, b):
return a + b
>>> add_ints(3, 5)
8
>>> cooler_add_ints = document_it(add_ints)
>>> cooler_add_ints(3, 5)
Running function: add_ints
Positional arguments: (3, 5)
Keyword arguments: {}
Result: 8
8
取代手動指派裝飾器【使用 @decorator_name】
可以在想要裝飾的函式前添加 @decorator_name,來取代上述的手動指派裝飾器
>>> @document_it
def 串列名稱2(引數1, 引數2):
return 引數1 + 引數2
>>> 串列名稱2(引數1, 引數2)
文字1: 函式名稱1
文字2: (引數1, 引數2)
文字3: {}
result: 引數1 + 引數2
引數1 + 引數2
可以在想要裝飾的函式前添加 @decorator_name,來取代上述的手動指派裝飾器
# 手動應用裝飾器
>>> def add_ints(a,b):
return a + b
>>> add_ints(3, 5)
8
>>> cooler_add_ints = document_it(add_ints)
>>> cooler_add_ints(3, 5)
Running function: add_ints
Positional arguments: (3, 5)
Keyword arguments: {}
Result: 8
8
# 可以在想要裝飾的函式前添加 @decorator_name,來取代上述的手動指派裝飾器
>>> @document_it
def add_ints(a,b):
return a + b
>>> add_ints(3, 5)
Running function: add_ints
Positional arguments: (3, 5)
Keyword arguments: {}
Result: 8
8
同一個函式可以有兩個以上的裝飾器
編寫另一個裝飾器,稱為 square_it(),它會將結果平方
>>> def 函式名稱1(func):
def 函式名稱2(*args, **kwargs):
result = func(*args, **kwargs)
eturn result * result
return 函式名稱
>>> def square_it(func):
def new_function(*args, **kwargs):
result = func(*args, **kwargs)
return result * result
return new_function
裝飾器執行順序不同,但最終結果都一樣
完整執行過程
>>> def document_it(func): # document_it() 函式會定義一個裝飾器
def 函式名稱1(*args, **kwargs):
print('文字1:', func.__name__) # 印出 → 函式的名稱
print('文字2:', args) # 印出 → 引數的值
print('文字3:', kwargs) # 印出 → 引數的值
result = func(*args, **kwargs)
print('Result:', result) # 印出 → 結果
return result
return 函式名稱1 # 回傳 → 修改後的函式,以供使用
# 手動應用裝飾器
>>> def 函式名稱2(引數1, 引數2):
引數1 + 引數2
>>> 函式名稱2(引數1, 引數2):
引數1 + 引數2
# 無論將什麼 func 傳給 document_it,都可以得到一個新的函式,裡面有 document_it() 加入的陳述式
>>> 函式名稱3 = document_it(函式名稱2)
# 裝飾器不一定要執行任何 func 的程式
# 但 document_it() 會在過程中呼叫 func,讓你可以取得 func 的結果以及額外的東西
>>> 函式名稱3(引數1, 引數2)
文字1: 函式名稱1
文字2: (引數1, 引數2)
文字3: {}
result: 引數1 + 引數2
引數1 + 引數2
# 可以在想要裝飾的函式前添加 @decorator_name,來取代上述的手動指派裝飾器
>>> @document_it
def 串列名稱2(引數1, 引數2):
return 引數1 + 引數2
>>> 串列名稱2(引數1, 引數2)
文字1: 函式名稱1
文字2: (引數1, 引數2)
文字3: {}
result: 引數1 + 引數2
引數1 + 引數2
# 同一個函式可以有兩個以上的裝飾器。編寫另一個裝飾器,稱為 square_it(),它會將結果平方
>>> def 函式名稱4(func):
def 函式名稱1(*args, **kwargs):
result = func(*args, **kwargs)
return result * result
return 函式名稱1
# 最靠近函式的裝飾器(在 def 正上方的)會先執行 → 接著是它上面的那個
# 無論順序為何,最終的結果都一樣的,但可以看到過程中有不同的步驟
# 方法一
>>> @document_it
@函式名稱4 # @函式名稱4 先執行 → 再執行 @document_it
def 函式名稱2(引數1, 引數2):
return 引數1 + 引數2
>>> 函式名稱2(引數1, 引數2)
文字1: 函式名稱1
文字2: (引數1, 引數2)
文字3: {}
result: (引數1 + 引數2)*(引數1 + 引數2)
(引數1 + 引數2)*(引數1 + 引數2)
# 方法二
>>> @函式名稱4
@document_it # @document_it 先執行 → 再執行 @函式名稱4
def 函式名稱2(引數1, 引數2):
return 引數1 + 引數2
>>> 函式名稱2(引數1, 引數2)
文字1: 函式名稱2
文字2: (引數1, 引數2)
文字3: {}
result: 引數1 + 引數2
(引數1 + 引數2)*(引數1 + 引數2)
# document_it()函式,定義一個裝飾器 @document_it
>>> def document_it(func):
def new_function(*args, **kwargs):
print('Running function:', func.__name__)
print('Positional arguments:', args)
print('Keyword arguments:', kwargs)
result = func(*args, **kwargs)
print('Result:', result)
return result
return new_function
# 手動應用裝飾器
>>> def add_ints(a, b):
return a + b
>>> add_ints(3, 5)
8
>>> cooler_add_ints = document_it(add_ints)
>>> cooler_add_ints(3, 5)
Running function: add_ints
Positional arguments: (3, 5)
Keyword arguments: {}
Result: 8
8
# 在想要裝飾的函式前添加 @decorator_name,來取代上述的「手動指派裝飾器」
>>> def add_ints(a,b):
return a + b
>>> add_ints(3, 5)
8
>>> cooler_add_ints = document_it(add_ints)
>>> cooler_add_ints(3, 5)
Running function: add_ints
Positional arguments: (3, 5)
Keyword arguments: {}
Result: 8
8
# 同一個函式可以有兩個以上的裝飾器
# 編寫另一個裝飾器,稱為 square_it(),它會將結果平方
>>> def square_it(func):
def new_function(*args, **kwargs):
result = func(*args, **kwargs)
return result * result
return new_function
# 最靠近函式的裝飾器(在 def 正上方的)會最先執行 → 接著是它上面的那一個
# 無論裝飾器的順序為何,最終的結果都一樣的,但可以看到過程中有不同的步驟
# 先執行 @aquare_it → @document_it
>>> @document_it
@square_it
def add_ints(a, b):
return a + b
>>> add_ints(3, 5)
Running function: new_function
Positional arguments: (3, 5)
Keyword arguments: {}
Result: 64 # 先執行 @aquare_it → @document_it
64
# 先執行 @document_it → @aquare_it
>> @square_it
@document_it
def add_ints(a,b):
return a + b
>>> add_ints(3, 5)
Running function: add_ints
Positional arguments: (3, 5)
Keyword arguments: {}
Result: 8 # 先執行 @document_it → @aquare_it
64
64