裝飾器:
概念:把一個函數當作參數傳遞給一個函數,返回一個替代版的函數本質上就是一個返回函數的函數
“在不改變原函數的基礎上,給函數增加功能”
@ 符號,那只是一個簡短的方式來生成一個被裝飾的函數
def paint(func): 參數是一個函數
def inner():
print('#########') 這個的整個部分就都是裝飾器
func()
return inner
@paint 這個就是函數戴帽子,也就是裝飾器
def func():
print('have a nice day!!')
@paint
def func2():
print('hello world')
func()
func2()
#########
have a nice day!!
#########
hello world
1、 被裝飾的函數存在參數的時候
在構建裝飾器的時候,作為返回值的函數是用來替代作為參數的函數的,所以這兩個函數所需要的參數必須一致。就時說再定義函數裝飾器的時候,指定作為參數的函數可以由于任意個參數以及作為返回值的函數可以右任意個參數,這樣就能保證再實際使用的時候不論被裝飾的函數需要幾個參數,都能使用該裝飾器。
2 、被裝飾的函數有返回值的時候
需要再函數裝飾器中作為返回值的函數中,接收該作為參數的函數的返回值,飯后再返回該值。
def outter(func): 函數作為參數+
def inner(*args):
# if age <=0:
# age = 0
func(*args) 調用函數
return inner
@outter
def say(name,age):
print('%s is %d years old' %(name,age))
say('redhat',10)
3 、被裝飾的函數如何保留自己的函數名和幫助信息文檔
當函數被裝飾之后,裝飾器返回一個新的函數來替代原來的函數進行工作,這時候函數名稱以及函數文檔都會變成裝飾器中定義的函數。
def welcome(func): # 參數是一個函數
def inner_f(*args,**kwargs):
"""這是裝飾器中的welcome函數"""
print('welcome'.center(40,'~'))
res = func(*args,**kwargs)
return res
return inner_f # 返回值是另一個函數
@welcome # 使用welcome進行裝飾
def name(name):
"""這是被裝飾的name函數"""
print('name is %s' %name)
@welcome # 使用welcome進行裝飾
def stuinfo(name,age):
"""這是被裝飾的stuinfo函數"""
return ('name is %s ,age is %d' %(name,age))
print(name.__name__) # 輸出函數名稱
print(name.__doc__) # 輸出函數文檔
print(stuinfo.__name__) # 輸出函數名稱
print(stuinfo.__doc__) # 輸出函數文檔
輸出結果:
inner_f
這是裝飾器中的welcome函數
inner_f
這是裝飾器中的welcome函數 # 任何被裝飾的函數民成以及函數文檔都變成裝飾器中定義的函數
Python提供給我們一個簡單的函數來解決這個問題,那就是functools.wraps。wraps接受一個函數來進行裝飾,并加入了復制函數名稱、注釋文檔、參數列表等等的功能。這可以讓我們在裝飾器里面訪問在裝飾之前的函數的屬性。
import time 模塊
def desc(f): # 需要傳遞的函數(要裝飾的函數)
def add_info():
print('兒童節快樂~~~')
f()
print('歡迎來西部開源~~~')
return desc
@desc
def login():
print('login...')
print(time.time())
裝飾器實現一個函數計時器
import time
import string
import random
import functools
li = [random.choice(string.ascii_letters)
for i in range(1000)]
def timeit(fun):
# 問題1:被裝飾的函數有返回值的時候怎么辦?
# 問題2:被裝飾的函數如何保留自己的函數名和幫助信息文檔?
@functools.wraps(fun)
def wapper(*args, **kwargs):
"""這是一個wapper函數"""
# 在函數的執行之前
start_time = time.time()
# 執行函數
res = fun(*args, **kwargs)
# 在函數執行之后
end_time = time.time()
print('運行的時間為:%.6f' % (end_time - start_time))
return res
return wapper
@timeit
def con_add():
s = ''
for i in li:
s += (i + '+')
print(s)
@timeit
def join_add():
print('+'.join(li))
con_add()
join_add()
創建裝飾器, 要求如下:
#1. 創建add_log裝飾器, 被裝飾的函數打印日志信息;
#2. 日志格式為: [字符串時間] 函數名: xxx,
運行時間:xxx, 運行返回值結果:xxx
import time
import functools
#print(time.ctime())
def add_log(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
start_time = time.time()
res = func(*args,**kwargs)
end_time = time.time()
print('[%s] 函數名:%s,運行時間:%.6f,運行返回值的'
'結果:%d' %(time.ctime(),func.__name__,
end_time-start_time,res))
return res
return wrapper
@add_log
def add(x,y):
time.sleep(1)
return x+y
add(1,10)
實驗效果
[Mon Jun 3 16:01:39 2019] 函數名:add,運行時間:1.000481,運行返回值的結果:11
裝飾器的執行順序,先上到下執行
def decorator_a(fun):
def inner_a(*args,**kwargs):
print('Get in inner_a')
return fun(*args,**kwargs)
return inner_a
def decorator_b(fun):
def inner_b(*args,**kwargs):
print('Get in inner_b')
return fun(*args,**kwargs)
return inner_b
##多個裝飾器裝飾函數,從上到下執行
@decorator_b
@decorator_a
def f(x):
print('Gat in f')
return x*2
f(1)
實驗效果
Get in inner_b
Get in inner_a
Gat in f
練習
[‘root’,‘admin’,‘redhat’]
多個裝飾器的應用場景:
會采用多個裝飾器先驗證是否登陸成功,再驗證登陸權限是否足夠
import inspect
import functools
def is_admin(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
# inspect.getcallargs會返回一個字典,
# key值:形參 value:對應的實參數
inspect_res = inspect.getcallargs(fun,*args,**kwargs)
print('inspect的返回值是:%s' %(inspect_res))
if inspect_res.get('name') == 'root':
temp = fun(*args,**kwargs)
return temp
else:
print('not root user,no permisson add user')
return wrapper
login_session = ['root','admin','redhat']
def is_login(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
if args[0] in login_session:
temp = fun(*args,**kwargs)
return temp
else:
print('Error:%s 沒有登陸成功' %(args[0]))
return wrapper
@is_login
@is_admin
def add_user(name):
print('add user~')
add_user('root')
實驗效果
inspect的返回值是:{'name': 'root'}
add user~
帶參數的裝飾器
import functools
import time
def log(kind):
def add_log(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
start_time = time.time()
res = func(*args,**kwargs)
end_time = time.time()
print('<%s>[%s] 函數名:%s,運行時間:%.6f,運行返回值的'
'結果:%d' %(kind,time.ctime(),func.__name__,
end_time-start_time,res))
return res
return wrapper
return add_log
@log('debug')
def add(x,y):
time.sleep(1)
return x+y
print(add(1,2))
編寫裝飾器required_types, 條件如下:
1). 當裝飾器為@required_types(int,float)確保函數接收到的每一個參數都是int或者float類型;
2). 當裝飾器為@required_types(list)確保函數接收到的每一個參數都是list類型;
3). 當裝飾器為@required_types(str,int)確保函數接收到的每一個參數都是str或者int類型;
4). 如果參數不滿足條件, 打印 TypeError:參數必須為xxxx類型
import functools
def required_types(*k):
def required(func):
@functools.wraps(func)
def wrappers(*args, **kwargs):
for i in args:
if not isinstance(i, k):
print('參數必須為:', *k)
break
else:
res = func(*args, **kwargs)
return res
return wrappers
return required
@required_types(int,str)
def output(*args):
print(*args)
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
