python中函式裝飾器
函式裝飾器
# 裝飾器的概念
- 裝飾器的實現是函式裡面巢狀函式;
- 裝飾器的本質是一個函式, 它可以讓其他函式在不需要做任何程式碼改動的前提下增加額外的功能;
- 裝飾器需要傳遞一個函式, 返回值也是一個函式物件.
# 裝飾器的應用場景
- 計時器
- 記錄日誌
- 使用者登入驗證
- 函式引數驗證
# ATM
# 使用者使用ATM的時候, 執行程式之前祝福語==“國慶節快樂”,
# 執行程式之後打個廣告===="歡迎再次光臨西部開源.....".
# 1. 解決問題: 在函式執行之前和執行之後新增功能, 呼叫函式的方式改變了.
# 2. 不改變原有函式的呼叫方法: 函式裡面潛逃函式, 並且返回巢狀的函式login = desc(login)
def desc(fun): # fun = login #1). 需要傳遞一個函式, 要裝飾的函式
def add_info(): # 2). 裝飾器函式裡面巢狀函式
print("五一快樂")
fun() # login()
print("歡迎再次光臨西部開源.....")
return add_info #3). 返回值是巢狀的函式物件
# 語法糖
@desc # login = desc(login) # 4). 如何呼叫裝飾器(兩種方式)
def login():
# print("國慶節快樂")
print("login......")
# print("歡迎再次光臨西部開源.....")
# login = desc(login) # 返回值是一個函式
# login()
login()
def logout():
print("logout......")
logout = desc(logout)
logout()
def saveMoney():
print("存錢........")
def transferMoney():
print("轉賬.........")
計時器的實現
# 裝飾器需求: 獲取每個函式的執行時間
# 1).函式執行之前計算時間;
# 2).函式執行之後計算時間
# 實驗1:看字串拼接的效率
# 1). “hello” + "world"
# 2). " ".join(['hello', “world”])
# 實驗2: 檢測列表生成式和map的效率高低, n為函式傳入的引數===兩者實力相當
# 1). [n*2 for i in range(n)]
# 2). map(lambda x:x*2, range(n))
import random
import string
import time
li = [ random.choice(string.ascii_letters) for i in range(100)]
def timeit(fun): # fun_list
def wrapper(*args, **kwargs): # 接收可變引數和關鍵字引數
# args: 元組 kwargs: 字典 args=(5,)
# 函式執行之前
start_time = time.time()
# 執行函式
fun(*args, **kwargs) # args解包, 5,| 對於元組解包, 對於字典解包
# 執行函式之後
end_time = time.time()
print("執行時間為:%.6f" % (end_time - start_time))
return wrapper
@timeit
def con_add():
s = ''
for i in li:
s += (i+",")
print(s)
@timeit
def join_add():
print(",".join(li))
@timeit # fun_list=timeit(fun_list) # fun_list = wrapper
def fun_list(n):
print([2*i for i in range(n)])
@timeit
def fun_map(n):
print(list(map(lambda x:x*2, range(n))))
con_add()
join_add()
fun_list(500000) #
fun_map(500000)
如何保留被裝飾函式的函式名和幫助文件
import random
import string
import time
import functools
# 高階函式的一些方法
from functools import reduce
# 問題1: 裝飾的函式有返回值的解決方法:
# 問題2:如何保留被裝飾函式的函式名和幫助文件資訊. @functools.wraps(fun)
def timeit(fun): # fun_list
"""這是一個裝飾器timeit"""
@functools.wraps(fun)
# 可以保留被裝飾函式的函式名和幫助文件資訊.
def wrapper(*args, **kwargs): # 接收可變引數和關鍵字引數 # 100
"""這是一個wrpper函式"""
# args: 元組 kwargs: 字典 args=(100,)
# 函式執行之前
start_time = time.time()
# 執行函式
res = fun(*args, **kwargs) # args解包, 100,| 對於元組解包, 對於字典解包
# fun_list(100)
# 執行函式之後
end_time = time.time()
print("執行時間為:%.6f" % (end_time - start_time))
return res
return wrapper
@timeit # fun_list=timeit(fun_list) # fun_list = wrapper
def fun_list(n):
"""這是fun_list函式, 被timeit裝飾"""
return [2*i for i in range(n)]
@timeit
def fun_map(n):
"""這是fun_map函式"""
return map(lambda x:x*2, range(n))
@timeit
def fun():
print("hello")
# print(fun_list(100)) # wrapper(100)
# print(fun_map(100))
# fun()
print(fun_list.__name__)
print(fun_list.__doc__)
裝飾器實現新增日誌
# 建立裝飾器, 要求如下:
# 1. 建立add_log裝飾器, 被裝飾的函式列印日誌資訊;
# 2. 日誌格式為: [字串時間] 函式名: xxx, 執行時間:xxx, 執行返回值結果:xxx
import functools
import time
# format
def add_log(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
# run_time = time.ctime()
# fun_name = fun.__name__
start_time = time.time()
res = fun(*args, **kwargs)
end_time = time.time()
print("[%s] 函式名: %s, 執行時間:%.5f, 執行返回值結果:%d"
%(time.ctime(), fun.__name__, end_time-start_time, res )
)
return res
return wrapper
@add_log
def add(x,y):
time.sleep(0.1)
return x+y
print(add(1,2))
帶有多個裝飾器的函式
@is_login, @is_admin
需求: 使用者登陸驗證的裝飾器is_login
1). 如果使用者登陸成功, 則執行被裝飾的函式;
2). 如果使用者登陸不成功, 則執行登陸函式
需求: 判斷登陸使用者是否未管理員is_admin(此處管理員只有一個為:admin使用者)
1).如果使用者為管理員, 則執行被裝飾的函式;
2).如果使用者不是管理員, 則報錯;
import functools
login_users = ['admin', 'root']
def is_admin(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
if kwargs.get("name") == 'admin':
res = fun(*args, **kwargs)
return res
else:
return "Error: 您沒有許可權訪問該網站"
return wrapper
def is_login(fun): # fun: writeBlog
@functools.wraps(fun)
def wrapper(*args, **kwargs): # name="admin" # kwargs={"name":"admin"}
# 判斷寫部落格的這個使用者是否登陸成功;
if kwargs.get("name") in login_users:
res = fun(*args, **kwargs)
return res
else:
res=login()
return res
return wrapper
必須登陸成功
@is_login # writeBlog = is_login(writeBlog)
@is_admin # 先判斷是否為admin使用者
def writeBlog(name):
return "編寫部落格"
def login():
return "登陸。。。。"
是否登陸成功都可以執行程式碼
def news():
print("新聞......")
print(writeBlog(name="root"))
def makebold(fun):
print("bold1")
def wrapper1(*args, **kwargs):
print("bold2")
return fun(*args, **kwargs) # wrapper
return wrapper1
def makei(fun): # fun=login
print("i1")
def wrapper(*args, **kwargs):
print("i2")
return fun(*args, **kwargs)
return wrapper
當有多個裝飾器時, 從下到上呼叫裝飾器,
真實wrapper內容執行是從上到下執行.
@makebold # login = makebold(login) # login為wrapper1
@makei # login = makei(login) # login為wrapper
def login():
return "登陸"
print(login())
帶有引數的裝飾器
建立裝飾器, 要求如下:
1. 建立add_log裝飾器, 被裝飾的函式列印日誌資訊;
2. 日誌格式為: [字串時間] 函式名: xxx, 執行時間:xxx, 執行返回值結果:xxx
import functools
import time
format
def log(kind): # kind="debug"
def add_log(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
# run_time = time.ctime()
# fun_name = fun.__name__
start_time = time.time()
res = fun(*args, **kwargs)
end_time = time.time()
print("<%s> [%s] 函式名: %s, 執行時間:%.5f, 執行返回值結果:%d"
%(kind, time.ctime(), fun.__name__, end_time-start_time, res )
)
return res
return wrapper
return add_log
@log("debug")
log("debug")==> 返回值是add_log
add=add_log(add)
def add(x,y):
time.sleep(0.1)
return x+y
print(add(1,2))