Python装饰器的应用场景代码总结
装饰器的应用场景
- 附加功能
- 数据的清理或添加:
- 函数参数类型验证@require_ints类似请求前拦截
- 数据格式转换将函数返回字典改为JSON/YAML类似响应后篡改
- 为函数提供额外的数据mock.patch
- 函数注册
- 在任务中心注册一个任务
- 注册一个带信号处理器的函数
不同应用场景下装饰器实现
函数注册表
简单注册表
funcs=[] defregister(func): funcs.append(func) returnfunc @register defa(): return3 @register defb(): return5 #访问结果 result=[func()forfuncinfuncs]
注册表隔离(使用类的不同实例)
classRegistry(object): def__init__(self): self._funcs=[] defregister(self,func): self._funcs.append(func) defrun_all(self): return[func()forfuncinself._funcs] r1=Registry() r2=Registry() @r1.register defa(): return3 @r2.register defb(): return5 @r1.register @r2.register
执行时封装代码
类型检查
fromfunctoolsimportwraps
defrequire_ints(func):
@wraps(func)#将func的信息复制给inner
definner(*args,**kwargs):
forarglist(args)+list(kwargs.values()):
ifnotisinstance(arg,int:
raiseTypeError("{}只接受int类型参数".format(func.__name__)
returnfunc(*args,**kwargs)
returninner
用户验证
fromfunctoolsimportwraps
classUser(object):
def__init__(self,username,email):
self.username=username
self.email=email
classAnonymousUser(object):
def__init__(self):
self.username=self.email=None
def__nonzero__(self):#将对象转换为bool类型时调用
returnFalse
defrequires_user(func):
@wraps(func)
definner(user,*args,**kwargs):#由于第一个参数无法支持self,该装饰器不支持装饰类
ifuserandisinstance(user,User):
returnfunc(use,*args,**kwargs)
else:
raiseValueError("非合法用户")
returninner
输出格式化
importjson fromfunctoolsimportwraps defjson_output(func):#将原本func返回的字典格式转为返回json字符串格式 @wrap(func) definner(*args,**kwargs): returnjson.dumps(func(*args,**kwargs)) returninner
异常捕获
importjson
fromfunctoolsimportwraps
classError1(Exception):
def__init__(self,msg):
self.msg=msg
def__str__(self):
returnself.msg
defjson_output(func):
@wrap(func)
definner(*args,**kwargs):
try:
result=func(*args,**kwargs)
exceptError1asex:
result={"status":"error","msg":str(ex)}
returnjson.dumps(result)
returninner
#使用方法
@json_ouput
deferror():
raiseError1("该条异常会被捕获并按JSON格式输出")
日志管理
importtime
importlogging
fromfunctoolsimportwraps
deflogged(func):
@wraps(func)
definner(*args,**kwargs):#*args可以装饰函数也可以装饰类
start=time.time()
result=func(*args,**kwargs)
exec_time=time.time()-start
logger=logging.getLoger("func.logged")
logger.warning("{}调用时间:{:.2}执行时间:{:.2}s结果:{}".format(func.__name__,start,exec_time,result)
带参数的装饰器
带参数的装饰器相当于一个返回装饰器的函数,@deco(a=1)在调用@之前会首先执行deco(a=1)得到一个实际的装饰器,带参数的装饰器deco(a=1)模块导入时立即执行
装饰类
为类增加可排序功能(而不通过继承子类扩充父类方法,比如多个类需要增加此功能时)
importtime fromfunctoolsimportwraps defsortable_by_created(cls): original_init=cls.__init__ @wrap(original_init) defnew_init(self,*args,**kwargs): original_init(*args,**kwargs) self._created=time.time() cls.__init__=new_init cls.__lt__=lambdaself,other:self._createdother._created returncls
也可定义一个SortableByCreated()类,子类使用多重继承其父类和SortableByCreated
类型转换
函数被装饰后有可能变为一个类的实例,此时为了兼容函数调用,应为所返回的类提供__call__方法
classTask(object):
def__call__(self,*args,**kwargs):
returnself.run(*args,**kwargs)
defrun(self,*args,**kwargs):
raiseNotImplementedError("子类未实现该接口")
deftask(func):
classSubTask(Task):
defrun(self,*args,**kwargs):
func(*args,**kwargs)
returnSubTask()
第二章上下文管理器
定义
包装任意代码
确保执行的一致性
语法
with语句
__enter__和__exit__方法
classContextManager(object): def__init__(self): self.entered=False def__enter__(self): self.entered=True returnself def__exit__(self,exc_type,exc_instance,traceback): self.entered=False
应用场景
资源清理
importpymysql classDBConnection(object): def__init__(self,*args,**kwargs): self.args,self.kwargs=args,kwargs def__enter__(self): self.conn=pymysql.connect(*args,**kwargs) returnself.conn.cursor() def__exit__(self,exc_type,exc_instance,trackback): self.conn.close()
异常处理(避免重复)
传播异常(__exit__中returnFalse)
终止异常(__exit__中returnTrue)
classBubleExceptions(object):
def__enter__(self):
returnself
def__exit__(self,exc_type,exc_instance,trackback):
ifexc_instance:
print("出现异常:{}".format(exc_instance)
returnFalse#returnTrue终止异常
处理特定的异常
classHandleValueError(object):
def__enter__(self):
returnself
def__exit__(self,exc_type,exc_instance,trackback):
ifnotexc_type:returnTrue
ifissubclass(exc_type,ValueError):
print("处理ValueError:{}".format(exc_instance)
returnFalse
ifissubclass...语句改为ifexec_type==ValueError则不处理ValueType的子类异常
也可以根据异常的属性来判断是否传播或终止
更简单的语法
importcontextlib @contextlib.contextmanager defacceptable_error_codes(*codes): try: yield exceptShellExceptionasexc_instance: ifexc_instance.codenotincodes: raise pass
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。