一、本次实验环境:
在腾讯云虚拟主机centos7上配置pyenv多版本python管理器,并安装交互式web编辑器jupyter,python版本为3.5.2。
二、装饰器:
装饰器的本质是一个函数,接收一个函数作为参数,并且返回一个函数 带参数的装饰器是一个函数,返回一个装饰器 带参数的装饰器最多允许一层,timeit()()(不允许)
在python中,一个函数可以作为参数传递给另外一个函数,还可返回一个函数(不了解此请看高阶函数)因此,我们可以把一个函数传递给另一个函数后,在这个被传递的函数的外部补充一些操作(装饰),而后把这个额外添加了补充装饰的函数得新return回来,装饰器也是高阶函数的一种。
1、不带参数的装饰器:
#装饰器的本质是一个函数,接收一个函数作为参数,并且返回一个函数 #带参数的装饰器是一个函数,返回一个装饰器 #带参数的装饰器最多允许一层@timeit()()(不允许) #@abs #@all #@callable #def func(): # pass #abs(all(callable(func))) def fun(fn):#接收一函数作为参数 print('numner1') def wrap(*args,**kwargs):#接收函数的参数 print('number2') ret = fn(*args,**kwargs)#被装饰的函数 print('number3') return ret print('number4') return wrap @fun def func(*args,**kwargs): print(args) print('run') return args z = func(1,2,3,4,5,6) print(z) #等价于 def func(*args,**kwargs): print(args) print('run') return args z = fun(func) f = z(1,2,3,4,5,6) print(f) #等价于 def func(*args,**kwargs): print(args) print('run') return args z = fun(func)(1,2,3,4,5,6) print(z) print('############################################') #func被多个装饰器装饰 @fun @fun @fun def func(*args,**kwargs): print(args) print('run') return args z = func(1,2,3,4,5,6) print(z) #等价于 print('############################################') def func(*args,**kwargs): print(args) print('run') return args z = fun(fun(fun(func)))(1,2,3,4,5,6) print(z) #根据结果,先执行最外层函数与次内层函数的语句,然后执行次内层与次次内层的语句 #以此类推到最内层函数时,即为被装饰的函数func
2、带参数的装饰器:
#(1)、判断一个用户是否在允许列表中,若在,则执行fn功能函数 from functools import wraps def check(allows): def dec(fn): @wraps(fn)#这里的@wraps(fn)意思是装饰wrap函数,将fn作为参数, def wrap(username,*args,**kwargs):#此时函数wrap函数的方法(wrap.__name__等)即为fn.__name__ if username in allows: return fn(username,*args,**kwargs) return 'not allow'#抛出异常 return wrap return dec @check(['i','you','he']) def test(username): print('congratulations') result = test('he') print(result) #等价于 def test(username): print('congratulations') result = check(['i','you','he'])(test)('she')#高阶函数理解了,这里也就多了一层函数,而这个函数接收了一个参数 print(result)
#(2)、default_user from functools import wraps def inject_user(default_user): def inject(fn): @wraps(fn) def wrap(*args,**kwargs): if 'user' not in kwargs.keys(): kwargs['user'] = default_user ret = fn(*args,**kwargs)#拿到我们想要的结果 return ret return wrap return inject @inject_user('comyn') def do_something(*args,**kwargs): print(kwargs.get('user')) return 23 ret = do_something(user='magedu') print(ret)
3、装饰器的应用:
(1)、装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能 (2)、装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景 (3)、装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用 (4)、概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
#(1)、cache缓存 import time from functools import wraps from functools import lru_cache#python内置的cache装饰器 def cache(instance):#instance为cache接口的封装 def dec(fn): @wraps(fn) def wrap(*args,**kwargs): #key =>fn_name::params pos = ','.join((str(x) for x in args)) kw = ','.join('{}={}'.format(k,v) for k,v in sorted(kwargs.items())) key = '{}::{}::{}'.format(fn.__name__,pos,kw)#生成key ret = instance.get(key)#判断key是否在cache中 print(key) print(ret) if ret is not None:#从cache中得到 return ret ret = fn(*args,**kwargs)#这里我们也可以装饰一个到腾讯云mysql数据库取数据的函数 instance.set(key,ret)#放进缓存(这里指DictCache) return ret return wrap return dec class DictCache:#这里用一个字典来作为缓存,腾讯云缓存也可以 def __init__(self): self.cache = dict() def get(self,key): return self.cache.get(key) def set(self,key,value): self.cache[key] = value def __str__(self): return str(self.cache) def __repr__(self): return repr(self.cache) if __name__ == '__main__': cache_instance = DictCache()#缓存实例可以是腾讯云memcached,redis,mongodb @cache(cache_instance) def long_time_fun(x): time.sleep(x) return x x = long_time_fun(3) print(x) y = long_time_fun(3) print(y) #拿标准库来实现 @lru_cache()#具有换出策略lru def time_fun(x): time.sleep(x) return x x = time_fun(3) print(x) y = time_fun(3) print(y)
#(2)、监控 import logging import time from functools import wraps def mertic(prefix,instance):#instance为各种不同类型监控平台的对象, def timeit(fn):# prefix为监控对象的前缀,属于哪个APP、主机的一个标记 @wraps(fn) def wrap(*args,**kwargs): start = time.time() ret = fn(*args,**kwargs)#取数据 key = '{}.{}.{}'.format(prefix,fn.__module__,fn.__name__) instance.send(key,time.time()-start)#发送到监控处理的地方,比如statsd上 return ret return wrap return timeit #influxdb,grafana展示 class LoggingMetric: def send(self,key,value): logging.warning('{}=>{}'.format(key,value)) @mertic(prefix='mysql',instance=LoggingMetric()) def long_time_fun(x): time.sleep(x) return x print(long_time_fun(1))
(3)、身份验证(4)、路由
未完待续!!!请看python3.5.2之装饰器(2)