1. 裝飾器的定義
就是給已有函式增加額外功能的函式,它本質上就是一個閉包函式,
裝飾器的功能特點:
- 不修改已有函式的源代碼
- 不修改已有函式的呼叫方式
- 給已有函式增加額外的功能
閉包和裝飾器的區分:
如果閉包函式的引數有且只有一個,并且是函式型別,那么這個閉包函式稱之為裝飾器,
寫代碼要遵循開放封閉原則,它規定已經實作的功能代碼不允許被修改,但可以被擴展,
2. 裝飾器的示例代碼
# 定義裝飾器
def decorator(func):
def inner():
# 在內部函式里面對已有函式進行裝飾
print('已添加登錄認證')
func()
return inner
def comment():
print('發表評論')
# 呼叫裝飾器對已有函式進行裝飾,左邊的comment=inner
comment = decorator(comment)
# 呼叫方式不變
comment()
3. 裝飾器的語法糖寫法
如果有多個函式都需要添加登錄驗證的功能,每次都需要撰寫func = decorator(func)這樣代碼對已有函式進行裝飾,這種做法還是比較麻煩,
Python給提供了一個裝飾函式更加簡單的寫法,那就是語法糖,語法糖的書寫格式是: @裝飾器名字,通過語法糖的方式也可以完成對已有函式的裝飾
# 定義裝飾器
def decorator(func):
def inner():
# 在內部函式里面對已有函式進行裝飾
print('已添加登錄認證')
func()
return inner
@decorator # comment = decorator(comment) 裝飾器語法糖對該代碼進行了封裝 左邊comment=inner
def comment():
print('發表評論')
# 呼叫方式不變
comment()
4. 裝飾器的執行時機
當 當前模塊加載完成以后,裝飾器會立即執行,對已有函式進行裝飾,
# 定義裝飾器
def decorator(func):
print('裝飾器執行了')
def inner():
# 在內部函式里面對已有函式進行裝飾
print('已添加登錄認證')
func()
return inner
@decorator # comment = decorator(comment) 裝飾器語法糖對該代碼進行了封裝 左邊comment=inner
def comment():
print('發表評論')
運行結果:
裝飾器執行了
5. 裝飾器的使用
5.1 裝飾器的使用場景
- 函式執行時間的統計
- 輸出日志資訊
5.2 裝飾器實作已有函式執行時間的統計
import time
def decorator(func):
def inner():
# 獲取時間距離1970-1-1 0:0:1的時間差
begin = time.time()
func()
end = time.time()
result = end - begin
print(f'函式執行完成耗時:{result}')
return inner
@decorator
def work():
for i in range(10000):
print(i)
work()
6. 通用裝飾器的使用
通用裝飾器:可以裝飾任意型別的函式
使用裝飾器裝飾已有函式的時候,內部函式的型別和要裝飾的已有函式的型別保持一致
6.1 裝飾帶有引數的函式
def decorator(func):
def inner(num1, num2):
print('正在努力執行加法計算')
func(num1, num2)
return inner
@decorator
def add_num(num1, num2):
result = num1 + num2
print(f'結果為:{result}')
add_num(1, 2)
6.2 裝飾帶有引數、回傳值的函式
def decorator(func):
def inner(num1, num2):
print('正在努力執行加法計算')
num = func(num1, num2)
return num
return inner
@decorator
def add_num(num1, num2):
result = num1 + num2
return result
result = add_num(1, 2)
print(f'結果為:{result}')
6.3 裝飾帶有不定長引數、回傳值的函式
def decorator(func):
def inner(*args, **kwargs):
print('正在努力執行加法計算')
# *args:把元組里面的每一個元素,按照位置引數的方式進行傳參
# **kwargs:把字典里面的每一個鍵值對,按照關鍵字的方式進行傳參
num = func(*args, **kwargs)
return num
return inner
@decorator
def add_num(*args, **kwargs):
result = 0
for value in args:
result += value
for value in kwargs.values():
result += value
return result
result = add_num(1, 2, a=3)
print(f'結果為:{result}')
7. 多個裝飾器的使用
多個裝飾器的裝飾程序:由內到外的一個裝飾程序,先執行內部的裝飾器,在執行外部的裝飾器,
def make_div(func):
print('make_div裝飾器執行了')
def inner():
result = '<div>' + func() + '</div>'
return result
return inner
def make_p(func):
print('make_p裝飾器執行了')
def inner():
result = '<p>' + func() + '</p>'
return result
return inner
# 原理剖析:content = make_div(make_p(content))
# 分布拆解:content = make_p(content),內部裝飾器完成,content = make_p.inner
# content = make_div(make_p.inner)
@make_div
@make_p
def content():
return '人生苦短,我用python'
c = content()
print(c)
make_p裝飾器執行了
make_div裝飾器執行了
<div><p>人生苦短,我用python</p></div>
8. 帶有引數的裝飾器
帶有引數的裝飾器就是使用裝飾器裝飾函式的時候可以傳入指定引數,語法格式: @裝飾器(引數,…)
使用帶有引數的裝飾器,其實是在裝飾器外面又包裹了一個函式,使用該函式接收引數,回傳是裝飾器,因為 @ 符號需要配合裝飾器實體使用,
def return_decorator(flag):
# 裝飾器只能接收一個引數并且是函式型別
def decorator(func):
def inner(a, b):
if flag == '+':
print('正在努力執行加法計算')
elif flag == '-':
print('正在努力執行減法計算')
func(a, b)
return inner
# 當呼叫函式的時候可以回傳一個裝飾器decorator
return decorator
@return_decorator('+') # decorator = return_decorator('+'), @decorator => add_num = decorator(add_num)
def add_num(a, b):
result = a + b
print(result)
@return_decorator('-')
def sub_num(a, b):
result = a - b
print(result)
add_num(1, 2)
sub_num(1, 2)
正在努力執行加法計算
3
正在努力執行減法計算
-1
9. 類裝飾器的使用
類裝飾器:使用類裝飾已有函式
class MyDecorator(object):
def __init__(self, func):
self.__func = func
# 實作__call__方法,表示物件是一個可呼叫物件,可以像呼叫函式一樣進行呼叫
def __call__(self, *args, **kwargs):
# 對已有函式進行封裝
print('馬上就有下班啦')
self.__func()
@MyDecorator # @MyDecorator => show = MyDecorator(show)
def show():
print('快要下雪啦')
# 執行show,就相當于執行MyDecorator類創建的實體物件,show() => 物件()
show()
馬上就有下班啦
快要下雪啦
擴展:
函式之所以能夠呼叫,是因為函式內部實作了 __call__ 方法
10. 應用場景
收集函式的操作或錯誤日志記錄
驗證函式的使用權限
計算函式的運行時間
在ORM/DB模型操作時,通過屬性方法動態地獲取關聯的資料
函式資料的快取
定制函式的輸入和輸出(序列化和反序列化)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/139228.html
標籤:其他
