裝飾器概念:
把一個函數當作參數傳遞給一個函數,返回一個替代版的函數
本質上就是一個返回函數的函數
在不改變原函數的基礎上,給函數增加功能
python 中裝飾器做的事情!它們封裝一個函數,并且用這樣或者那樣的方式來修改它的行為
@ 符號,那只是一個簡短的方式來生成一個被裝飾的函數
def outer(func):def inner():print('*****')func()return inner@outer
def func():print('have a nice day!')
@outer
def func2():print('hello world')
func()
func2()運行結果:
*****
have a nice day!
*****
hello world
裝飾器示例:
import time# 裝飾器
def decorator(func):def wrapper():print(time.time())func()return wrapper@decorator # 調用裝飾器
def f1():print('This is a function...')def f2(): # 沒有裝飾器print('This is a function...')f1()
f2()運行結果:
1560391414.8582878 f1
This is a function...
This is a function... f2
裝飾器實現一個函數計時器
import time
import string
import random
import functoolsli = [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 resreturn 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()@timeit
def fun_list(n):"""這是fun_list函數,被timeit裝飾"""return [2 * i for i in range(n)]
@timeit
def fun_map(n):"""這是fun_map函數,被timeit裝飾"""return list(map(lambda x:x*2,range(n)))# fun_list(5000)
# fun_map(5000)
print(fun_list.__doc__)
print(fun_map.__doc__)
print(fun_list.__name__)
print(fun_map.__name__)
創建裝飾器, 要求如下:
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 resreturn wrapper
@add_log
def add(x,y):time.sleep(1)return x+y
add(1,10)
多個裝飾器裝飾函數,從上到下執行
def decorator_a(fun):def inner_a(*args,**kwargs):print('Get in inner_a')return fun(*args,**kwargs)return inner_adef 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*2f(1)
多個裝飾器的應用場景:
會采用多個裝飾器先驗證是否登陸成功,再驗證登陸權限是否足夠
inspect.getcallargs會返回一個字典,
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 tempelse: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 tempelse:print('Error:%s 沒有登陸成功' %(args[0]))return wrapper
@is_login
@is_admin
def add_user(name):print('add user~')
add_user('root')
代參數的裝飾器
import functools
import timedef 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 resreturn wrapperreturn 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(*kinds):def required_int(fun):@functools.wraps(fun)def wrapper(*args, **kwargs):for i in args:if not isinstance(i, kinds):# print('TypeError:參數必須為',kinds)# breakraise TypeError('參數必須為%s,%s' % kinds)else:res = fun(*args, **kwargs)return resreturn wrapperreturn required_int# @required_types(float, float)
# def add(a, b):
# return a + b
#
# print(add(1.1, 2.0))
運行結果為:3.1# @required_types(list)
# def add(a, b):
# return a + b
#
# print(add(1.1, 2.0))
運行結果為:
Traceback (most recent call last):File "/home/kiosk/PycharmProjects/20190523/練習.py", line 65, in <module>print(add(1.1, 2.0))File "/home/kiosk/PycharmProjects/20190523/練習.py", line 42, in wrapperraise TypeError('參數必須為%s,%s' % kinds)
TypeError: not enough arguments for format string