装饰器及例题分析
知識(shí)點(diǎn):
裝飾器的定義:
- 裝飾器的實(shí)現(xiàn)是函數(shù)里面嵌套函數(shù);
- 裝飾器的本質(zhì)是一個(gè)函數(shù), 它可以讓其他函數(shù)在不需要做任何代碼改動(dòng)的前提下增加額外的功能;
- 裝飾器需要傳遞一個(gè)函數(shù), 返回值也是一個(gè)函數(shù)對(duì)象.
1、map函數(shù)
def f(x):return x * x"""map()傳入的第一個(gè)參數(shù)是一個(gè)函數(shù),第二個(gè)參數(shù)是一個(gè)序列,第二個(gè)序列作為第一個(gè)參數(shù)的輸入值
也就是說(shuō)第二個(gè)參數(shù)調(diào)用了第一個(gè)函數(shù)
"""print map(f, [1, 2, 3, 4])
2、匿名函數(shù)
#上面的函數(shù)也可以用一條語(yǔ)句代替,即匿名函數(shù)lambdax:
print map(lambda x: x * x, [1, 2, 3, 4])
3、帶有參數(shù)的裝飾器:
import functools
import time# 打印當(dāng)前的時(shí)間
print time.ctime()def log(name):def add_log(fun):@functools.wraps(fun)def wrapper(*args, **kwargs):start_time = time.time()res = fun(*args, **kwargs)end_time = time.time()print '<%s> [%s] 函數(shù)名:%s,運(yùn)行時(shí)間:%.5f,運(yùn)行返回值結(jié)果:%d' %(name, time.ctime(), fun.__name__, end_time - start_time, res)return resreturn wrapperreturn add_log@log('lily')
def add(x,y):time.sleep(1)return x+yprint add(1,2)
4、多個(gè)裝飾器的執(zhí)行順序
#定義兩個(gè)裝飾器,將兩個(gè)裝飾器給函數(shù),探究其中調(diào)用的先后順序
def decorator_a(func):print 'Get in decorator_a'def inner_a(*args, **kwargs):print 'Get in inner_a'res = func(*args, **kwargs)return resreturn inner_adef decorator_b(func):print 'Get in decorator_b'def inner_b(*args, **kwargs):print 'Get in inner_b'res = func(*args, **kwargs)return resreturn inner_b# 當(dāng)有多個(gè)裝飾器時(shí),從下到上調(diào)用裝飾器
@decorator_a
@decorator_b
def f(x):print 'Get in f'return x * 2f(1)
?
總結(jié):
從運(yùn)行結(jié)果可以看出,若將decorator_a和decorator_b按順序給函數(shù),則裝飾器外部為從下島上調(diào)用裝飾器,內(nèi)部仍按順序調(diào)用。
例題分析:
1、多個(gè)裝飾器的應(yīng)用
要求:查看若要實(shí)現(xiàn)相同的功能python內(nèi)置函數(shù)和自己編寫(xiě)的函數(shù)哪個(gè)運(yùn)行時(shí)間更少
裝飾器需求:獲取每個(gè)函數(shù)的執(zhí)行時(shí)間
1.函數(shù)執(zhí)行之前計(jì)算時(shí)間
2.函數(shù)執(zhí)行之后計(jì)算時(shí)間
3.運(yùn)行時(shí)間=執(zhí)行后時(shí)間-執(zhí)行前時(shí)間
問(wèn)題1:被裝飾的函數(shù)有返回值的時(shí)候怎么辦?
問(wèn)題2:如何保留被裝飾函數(shù)的函數(shù)名和幫助文檔信息
import functools import random import string import time"""導(dǎo)入模塊的快捷鍵為Alt+Enter并選擇""" li = [random.choice(string.ascii_letters) for i in range(100)]def timeit(fun):"""裝飾器"""@functools.wraps(fun)def wrapper(*args, **kwargs): # 接收可變參數(shù)和關(guān)鍵字參數(shù)"""這是一個(gè)wrapper函數(shù)"""# args:元組 kwargs:字典# 函數(shù)執(zhí)行之前start_time = time.time()# 執(zhí)行函數(shù)# 接收被裝飾函數(shù)的返回值res = fun(*args, **kwargs)# 函數(shù)執(zhí)行之后end_time = time.time()print '運(yùn)行的時(shí)間為:%.6f' % (end_time - start_time)return resreturn wrapper@timeit def con_add():s = ''for i in li:s += (i + ',')print s@timeit def join_add():print ','.join(li)@timeit def fun_list(n):"""這是fun_list函數(shù),被timeit裝飾"""return [2 * i for i in range(n)]@timeit def fun_map(n):"""這是fun_map函數(shù)"""return list(map(lambda x: x * 2, range(n)))# 匿名函數(shù) # map傳入的第一個(gè)參數(shù)為一個(gè)函數(shù),第二個(gè)為一個(gè)序列con_add() join_add() print fun_list(50000) print fun_map(50000)#保留被裝飾函數(shù)的函數(shù)名和幫助文檔信息 print fun_list.__name__ print fun_list.__doc__
總結(jié):從上面的運(yùn)行時(shí)間可以看出:
用python的內(nèi)置函數(shù)比自己編寫(xiě)的函數(shù)運(yùn)行時(shí)間少,所以我們?cè)谡{(diào)用函數(shù)的時(shí)候盡量使用python的內(nèi)置函數(shù)。
?
2、
在我們實(shí)際的應(yīng)用中,會(huì)應(yīng)用到多個(gè)裝飾器,例如: 先驗(yàn)證是否登陸成功再驗(yàn)證權(quán)限的方法
要求:用戶登陸驗(yàn)證的裝飾器 is_login
?????????? 1.如果用戶登陸成功,則執(zhí)行被裝飾的函數(shù)
?????????? 2.如果用戶登陸不成功,則執(zhí)行登陸函數(shù)
import functools
# 讓函數(shù)統(tǒng)一
import inspectdef is_root(fun):@functools.wraps(fun)def wrapper(*args, **kwargs):inspect_res = inspect.getcallargs(fun, *args, **kwargs)print 'inspect_res的返回值:%s' % inspect_resif inspect_res.get('name') == 'root':res = fun(*args, **kwargs)return reselse:print 'Error:no perminssion add student'return wrapperdef is_login(fun):@functools.wraps(fun)def warrper(*args, **kwargs):if args[0] in login_session:res = fun(*args, **kwargs)return reselse:print 'Error:%s未登陸'return warrper@is_login
@is_root
def add_student(name):print '添加學(xué)生信息'login_session = ['root', 'admin', 'redhat']
# 調(diào)用函數(shù)
add_student('root')
3、
要求:用戶登陸驗(yàn)證的裝飾器 is_login
?????????? 1.如果用戶登陸成功,則執(zhí)行被裝飾的函數(shù)
?????????? 2.如果用戶登陸不成功,則執(zhí)行登陸函數(shù)
import functoolslogin_users = ['admin', 'root']def is_login(fun):@functools.wraps(fun)def wrapper(*args, **kwargs):# 判斷寫(xiě)博客這個(gè)用戶是否登陸成功if kwargs.get('name') in login_users:res = fun(*args, **kwargs)return reselse:res = login()return resreturn wrapperdef login():return '登陸。。。'@is_login
def writeBlog(name):return '編寫(xiě)博客。。'print writeBlog(name='admin')
若最后調(diào)用函數(shù)時(shí)為:
print? writeBlog(name='admin')
4、
要求:
創(chuàng)建裝飾器, 要求如下:
1. 創(chuàng)建add_log裝飾器, 被裝飾的函數(shù)打印日志信息;
2. 日志格式為: [字符串時(shí)間] 函數(shù)名: xxx, 運(yùn)行時(shí)間:xxx, 運(yùn)行返回值結(jié)果:xxx
import time import functools #print time.ctime()def add_log(fun):@functools.wraps(fun)def wrapper(*args,**kwargs):start_time = time.time()res = fun(*args,**kwargs)end_time = time.time()print '[%s] 函數(shù)名:%s,運(yùn)行時(shí)間:%.5f,運(yùn)行返回值結(jié)果:%d' %(time.ctime(),fun.__name__,end_time-start_time,res)return resreturn wrapper@add_log def add(x,y):time.sleep(1)return x+yprint add(1,2)
5、
編寫(xiě)裝飾器required_ints, 條件如下:1). 確保函數(shù)接收到的每一個(gè)參數(shù)都是整數(shù);2). 如果參數(shù)不是整形數(shù), 打印 TypeError:參數(shù)必須為整形import functoolsdef required_ints(fun):@functools.wraps(fun)def wrapper(*args, **kwargs):for i in args:if not isinstance(i, int):print 'TypeError:參數(shù)必須為整形'breakelse:res = fun(*args, **kwargs)return resreturn wrapper@required_ints def add(a, b):return a + b@required_ints def myMax(a,b,c,d):return max(a,b,c,d)print add(1,2) print myMax(1,2,3,4)
6、
要求:編寫(xiě)裝飾器required_types, 條件如下:
1). 當(dāng)裝飾器為@required_types(int,float)確保函數(shù)接收到的每一個(gè)參數(shù)都是int或者float類型;
2). 當(dāng)裝飾器為@required_types(list)確保函數(shù)接收到的每一個(gè)參數(shù)都是list類型;
3). 當(dāng)裝飾器為@required_types(str,int)確保函數(shù)接收到的每一個(gè)參數(shù)都是str或者int類型;
4). 如果參數(shù)不滿足條件, 打印 TypeError:參數(shù)必須為xxxx類型
import functoolsdef required_types(*kinds):def required_ints(fun):@functools.wraps(fun)def wrapper(*args, **kwargs):for i in args:if not isinstance(i, kinds):print 'TypeError:參數(shù)必須為',kindsbreakelse:res = fun(*args, **kwargs)return resreturn wrapperreturn required_ints@required_types(float,float) def add(a, b):return a + bprint add(1.1,3)
總結(jié)
- 上一篇: 虚拟机的基本操作
- 下一篇: 面向对象概念及三大特点