1.装饰器
1. 什么是装饰器
器即函数
装饰即修饰,意指为其他函数添加新功能
装饰器定义:本质就是函数,功能是为其他函数添加新功能
2.装饰器需要遵循的原则
1.不修改被装饰函数的源代码(开放封闭原则)
2.为被装饰函数添加新功能后,不修改被修饰函数的调用方式
3.实现装饰器知识储备
装饰器=高阶函数+函数嵌套+闭包
4.高阶函数
高阶函数定义:
1.函数接收的参数是一个函数名
2.函数的返回值是一个函数名
3.满足上述条件任意一个,都可称之为高阶函数
1.高阶函数示范
1 def foo(): 2 print('我的函数名作为参数传给高阶函数') 3 def gao_jie1(func): 4 print('我就是高阶函数1,我接收的参数名是%s' %func) 5 func() 6 7 def gao_jie2(func): 8 print('我就是高阶函数2,我的返回值是%s' %func) 9 return func 10 11 gao_jie1(foo) 12 gao_jie2(foo)
输出:
我就是高阶函数1,我接收的参数名是<function foo at 0x00000191572D1F28>
我的函数名作为参数传给高阶函数
我就是高阶函数2,我的返回值是<function foo at 0x00000191572D1F28>
2.把函数当做参数传给高阶函数
1 #高阶函数应用1:把函数当做参数传给高阶函数 2 import time 3 def foo(): 4 print('from the foo') 5 6 def timmer(func): 7 start_time=time.time() 8 func() 9 stop_time=time.time() 10 print('函数%s 运行时间是%s' %(func,stop_time-start_time)) 11 timmer(foo) 12 #总结:我们确实为函数foo增加了foo运行时间的功能,但是foo原来的执行方式是foo(),现在我们需要调用高阶函数timmer(foo),改变了函数的调用方式
输出:
from the foo 函数<function foo at 0x000001DEFDDC1F28> 运行时间是0.0
3.函数返回值是函数名
1 #高阶函数应用2:把函数名当做参数传给高阶函数,高阶函数直接返回函数名 2 import time 3 def foo(): 4 print('from the foo') 5 6 def timmer(func): 7 start_time=time.time() 8 return func 9 stop_time=time.time() 10 print('函数%s 运行时间是%s' %(func,stop_time-start_time)) 11 foo=timmer(foo) 12 foo() 13 #总结:我们确实没有改变foo的调用方式,但是我们也没有为foo增加任何新功能
输出:
#高阶函数应用2:把函数名当做参数传给高阶函数,高阶函数直接返回函数名 import time def foo(): print('from the foo') def timmer(func): start_time=time.time() return func stop_time=time.time() print('函数%s 运行时间是%s' %(func,stop_time-start_time)) foo=timmer(foo) foo() #总结:我们确实没有改变foo的调用方式,但是我们也没有为foo增加任何新功能
高阶函数总结
1.函数接收的参数是一个函数名
作用:在不修改函数源代码的前提下,为函数添加新功能,
不足:会改变函数的调用方式
2.函数的返回值是一个函数名
作用:不修改函数的调用方式
不足:不能添加新功能
5.函数嵌套
1 def father(name): 2 print('from father %s' %name) 3 def son(): 4 print('from son') 5 def grandson(): 6 print('from grandson') 7 grandson() 8 son() 9 10 father('林海峰')
输出:
from the foo
6.闭包
闭包:闭包(closure)是函数式编程的重要的语法结构。函数式编程是一种编程范式 (而面向过程编程和面向对象编程也都是编程范式)。在面向过程编程中,我们见到过函数(function);在面向对象编程中,我们见过对象(object)。函数和对象的根本目的是以某种逻辑方式组织代码,并提高代码的可重复使用性(reusability)。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。
1 ''' 2 闭包:在一个作用域里放入定义变量,相当于打了一个包 3 ''' 4 def father(name): 5 def son(): 6 # name='alex' 7 print('我爸爸是 [%s]' %name) 8 def grandson(): 9 # name='wupeiqi' 10 print('我爷爷是 [%s]' %name) 11 grandson() 12 son() 13 14 father('caofu')
输出:
我爸爸是 [caofu]
我爷爷是 [caofu]
2.无参装饰器
1.无参装饰器
无参装饰器=高级函数+函数嵌套
基本框架
1 #这就是一个实现一个装饰器最基本的架子 2 def timer(func): 3 def wrapper(): 4 func() 5 return wrapper
加上参数
1 def timer(func): 2 def wrapper(*args,**kwargs): 3 func(*args,**kwargs) 4 return wrapper
加上功能
1 import time 2 def timer(func): 3 def wrapper(*args,**kwargs): 4 start_time=time.time() 5 func(*args,**kwargs) 6 stop_time=time.time() 7 print('函数[%s],运行时间是[%s]' %(func,stop_time-start_time)) 8 return wrapper
加上返回值
1 import time 2 def timer(func): 3 def wrapper(*args,**kwargs): 4 start_time=time.time() 5 res=func(*args,**kwargs) 6 stop_time=time.time() 7 print('函数[%s],运行时间是[%s]' %(func,stop_time-start_time)) 8 return res 9 return wrapper
使用装饰器
1 def cal(array): 2 res=0 3 for i in array: 4 res+=i 5 return res 6 7 cal=timer(cal) 8 cal(range(10))
语法糖@
1 @timer #@timer就等同于cal=timer(cal) 2 def cal(array): 3 res=0 4 for i in array: 5 res+=i 6 return res 7 8 cal(range(10))
2.装饰器应用示例
1 user_list=[ 2 {'name':'alex','passwd':'123'}, 3 {'name':'linhaifeng','passwd':'123'}, 4 {'name':'wupeiqi','passwd':'123'}, 5 {'name':'yuanhao','passwd':'123'}, 6 ] 7 8 current_user={'username':None,'login':False} 9 10 def auth_deco(func): 11 def wrapper(*args,**kwargs): 12 if current_user['username'] and current_user['login']: 13 res=func(*args,**kwargs) 14 return res 15 username=input('用户名: ').strip() 16 passwd=input('密码: ').strip() 17 18 for index,user_dic in enumerate(user_list): 19 if username == user_dic['name'] and passwd == user_dic['passwd']: 20 current_user['username']=username 21 22 current_user['login']=True 23 res=func(*args,**kwargs) 24 return res 25 break 26 else: 27 print('用户名或者密码错误,重新登录') 28 29 return wrapper 30 31 @auth_deco 32 def index(): 33 print('欢迎来到主页面') 34 35 @auth_deco 36 def home(): 37 print('这里是你家') 38 39 def shopping_car(): 40 print('查看购物车啊亲') 41 42 def order(): 43 print('查看订单啊亲') 44 45 print(user_list) 46 # index() 47 print(user_list) 48 home()
输出:
[{'name': 'alex', 'passwd': '123'}, {'name': 'linhaifeng', 'passwd': '123'}, {'name': 'wupeiqi', 'passwd': '123'}, {'name': 'yuanhao', 'passwd': '123'}] [{'name': 'alex', 'passwd': '123'}, {'name': 'linhaifeng', 'passwd': '123'}, {'name': 'wupeiqi', 'passwd': '123'}, {'name': 'yuanhao', 'passwd': '123'}] 用户名:
2.带参装饰器
1 user_list=[ 2 {'name':'alex','passwd':'123'}, 3 {'name':'linhaifeng','passwd':'123'}, 4 {'name':'wupeiqi','passwd':'123'}, 5 {'name':'yuanhao','passwd':'123'}, 6 ] 7 8 current_user={'username':None,'login':False} 9 def auth(auth_type='file'): 10 def auth_deco(func): 11 def wrapper(*args,**kwargs): 12 if auth_type == 'file': 13 if current_user['username'] and current_user['login']: 14 res=func(*args,**kwargs) 15 return res 16 username=input('用户名: ').strip() 17 passwd=input('密码: ').strip() 18 19 for index,user_dic in enumerate(user_list): 20 if username == user_dic['name'] and passwd == user_dic['passwd']: 21 current_user['username']=username 22 current_user['login']=True 23 res=func(*args,**kwargs) 24 return res 25 break 26 else: 27 print('用户名或者密码错误,重新登录') 28 elif auth_type == 'ldap': 29 print('巴拉巴拉小魔仙') 30 res=func(*args,**kwargs) 31 return res 32 return wrapper 33 return auth_deco 34 35 36 #auth(auth_type='file')就是在运行一个函数,然后返回auth_deco,所以@auth(auth_type='file') 37 #就相当于@auth_deco,只不过现在,我们的auth_deco作为一个闭包的应用,外层的包auth给它留了一个auth_type='file'参数 38 @auth(auth_type='ldap') 39 def index(): 40 print('欢迎来到主页面') 41 42 @auth(auth_type='ldap') 43 def home(): 44 print('这里是你家') 45 46 def shopping_car(): 47 print('查看购物车啊亲') 48 49 def order(): 50 print('查看订单啊亲') 51 52 # print(user_list) 53 index() 54 # print(user_list) 55 home()
输出:
巴拉巴拉小魔仙
欢迎来到主页面
巴拉巴拉小魔仙
这里是你家
3.超时装饰器
最近工作有点多,趁周末有空,继续分享我在学习和使用python过程中的一些小tips。
有没有遇到过这样的事情:对数据库执行插入或更新操作,因为数据量大或其他原因,导致此次操作非常耗时,有时甚至等上好几个小时,也无法完成。很郁闷,怎么操作不超时啊?因为数据库配置时超时时间很长,并且有些操作又是需要很长时间的,所以不能修改默认的超时时间。
因为客观条件不允许,我们不能靠数据库超时来终止此次操作,所以必须要在自己的方法逻辑模块里实现超时检测的功能。
在python里有没有可以不用修改原来的方法内部逻辑,就能实现超时检测呢?肯定有啦,就是利用装饰器。装饰器是什么?在博客园找到了一篇介绍文章:函数和方法装饰漫谈(Function decorator)。
废话听完,我现在介绍主角出场:超时装饰器,timeout decorator。
超时检测逻辑:启动新子线程执行指定的方法,主线程等待子线程的运行结果,若在指定时间内子线程还未执行完毕,则判断为超时,抛出超时异常,并杀掉子线程;否则未超时,返回子线程所执行的方法的返回值。
在实现过程中,发现python默认模块里是没有方法可以杀掉线程的,怎么办呢?当然先问问google或百度,果然,keill thread这个关键词很热门,很快就搜索到我想要的东西了:"Kill a thread in Python",就是以下这个KThread类,它继承了threading.Thread,并添加了kill方法,让我们能杀掉它:
1 import sys,threading,time 2 3 4 class KThread(threading.Thread): 5 6 """A subclass of threading.Thread, with a kill() 7 8 method. 9 10 11 12 Come from: 13 14 Kill a thread in Python: 15 16 http://mail.python.org/pipermail/python-list/2004-May/260937.html 17 18 """ 19 20 def __init__(self, *args, **kwargs): 21 22 threading.Thread.__init__(self, *args, **kwargs) 23 24 self.killed = False 25 26 27 28 def start(self): 29 30 """Start the thread.""" 31 32 self.__run_backup = self.run 33 34 self.run = self.__run # Force the Thread to install our trace. 35 36 threading.Thread.start(self) 37 38 39 40 def __run(self): 41 42 """Hacked run function, which installs the 43 44 trace.""" 45 46 sys.settrace(self.globaltrace) 47 48 self.__run_backup() 49 50 self.run = self.__run_backup 51 52 53 54 def globaltrace(self, frame, why, arg): 55 56 if why == 'call': 57 58 return self.localtrace 59 60 else: 61 62 return None 63 64 65 66 def localtrace(self, frame, why, arg): 67 68 if self.killed: 69 70 if why == 'line': 71 72 raise SystemExit() 73 74 return self.localtrace 75 76 77 78 def kill(self): 79 80 self.killed = True 81 82 83 84 class Timeout(Exception): 85 86 """function run timeout""" 87 88 89 90 def timeout(seconds): 91 92 """超时装饰器,指定超时时间 93 94 若被装饰的方法在指定的时间内未返回,则抛出Timeout异常""" 95 96 def timeout_decorator(func): 97 98 """真正的装饰器""" 99 100 101 102 def _new_func(oldfunc, result, oldfunc_args, oldfunc_kwargs): 103 104 result.append(oldfunc(*oldfunc_args, **oldfunc_kwargs)) 105 106 107 108 def _(*args, **kwargs): 109 110 result = [] 111 112 new_kwargs = { # create new args for _new_func, because we want to get the func return val to result list 113 114 'oldfunc': func, 115 116 'result': result, 117 118 'oldfunc_args': args, 119 120 'oldfunc_kwargs': kwargs 121 122 } 123 124 thd = KThread(target=_new_func, args=(), kwargs=new_kwargs) 125 126 thd.start() 127 128 thd.join(seconds) 129 130 alive = thd.isAlive() 131 132 thd.kill() # kill the child thread 133 134 if alive: 135 136 raise Timeout(u'function run too long, timeout %d seconds.' % seconds) 137 138 else: 139 140 return result[0] 141 142 _.__name__ = func.__name__ 143 144 _.__doc__ = func.__doc__ 145 146 return _ 147 148 return timeout_decorator 149 150 151 @timeout(5) 152 153 def method_timeout(seconds, text): 154 155 print('start', seconds, text) 156 157 time.sleep(seconds) 158 159 print('finish', seconds, text) 160 161 return seconds 162 163 164 method_timeout(6,'asdfasdfasdfas')