Python装饰器的应用场景代码总结


Posted in Python onApril 10, 2020

装饰器的应用场景

  • 附加功能
  • 数据的清理或添加:
    • 函数参数类型验证 @require_ints 类似请求前拦截
    • 数据格式转换 将函数返回字典改为 JSON/YAML 类似响应后篡改
    • 为函数提供额外的数据 mock.patch
  • 函数注册
    • 在任务中心注册一个任务
    • 注册一个带信号处理器的函数

不同应用场景下装饰器实现

函数注册表

简单注册表

funcs = []
def register(func):
  funcs.append(func)
  return func    
@register
def a():
  return 3
  
@register
def b():
  return 5  
# 访问结果
result = [func() for func in funcs]

注册表隔离(使用类的不同实例)

class Registry(object):
  def __init__(self):
    self._funcs = []
  
  def register(self, func):
    self._funcs.append(func)
    
  def run_all(self):
    return [func() for func in self._funcs] 
r1 = Registry()
r2 = Registry()

@r1.register
def a():
  return 3
  
@r2.register
def b():
  return 5
  
@r1.register
@r2.register

执行时封装代码

类型检查

from functools import wraps
def require_ints(func):
  @wraps(func) # 将func的信息复制给inner
  def inner(*args, **kwargs):
    for arg list(args) + list(kwargs.values()):
      if not isinstance(arg, int:
        raise TypeError("{} 只接受int类型参数".format(func.__name__)
    return func(*args, **kwargs)
  return inner

用户验证

from functools import wraps

class User(object):
  def __init__(self, username, email):
    self.username = username
    self.email = email
    
class AnonymousUser(object):
  def __init__(self):
    self.username = self.email = None
  def __nonzero__(self): # 将对象转换为bool类型时调用
    return False
def requires_user(func):
  @wraps(func)
  def inner(user, *args, **kwargs): # 由于第一个参数无法支持self, 该装饰器不支持装饰类
    if user and isinstance(user, User):
      return func(use, *args, **kwargs)
    else:
      raise ValueError("非合法用户")
  return inner

输出格式化

import json
from functools import wraps
def json_output(func): # 将原本func返回的字典格式转为返回json字符串格式
  @wrap(func)
  def inner(*args, **kwargs):
    return json.dumps(func(*args, **kwargs))
  return inner

异常捕获

import json
from functools import wraps

class Error1(Exception):
  def __init__(self, msg):
    self.msg = msg
  def __str__(self):
    return self.msg
    

def json_output(func):
  @wrap(func)
  def inner(*args, **kwargs):
    try:
      result = func(*args, **kwargs)
    except Error1 as ex:
      result = {"status": "error", "msg": str(ex)}
    return json.dumps(result)
  return inner
# 使用方法
@json_ouput
def error():
  raise Error1("该条异常会被捕获并按JSON格式输出")

日志管理

import time
import logging
from functools import wraps

def logged(func):
  @wraps(func)
  def inner(*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)模块导入时立即执行

装饰类

为类增加可排序功能(而不通过继承子类扩充父类方法,比如多个类需要增加此功能时)

import time
from functools import wraps
def sortable_by_created(cls):
  original_init = cls.__init__
  @wrap(original_init)
  def new_init(self, *args, **kwargs):
    original_init(*args, **kwargs)
    self._created = time.time()
  cls.__init__ = new_init
  
  cls.__lt__ = lambda self, other: self._created < other._created
  cls.__gt__ = lambda self, other: self._created > other._created
  return cls

也可定义一个SortableByCreated()类, 子类使用多重继承其父类和SortableByCreated

类型转换

函数被装饰后有可能变为一个类的实例,此时为了兼容函数调用,应为所返回的类提供__call__方法

class Task(object):
  def __call__(self, *args, **kwargs):
    return self.run(*args, **kwargs)
  def run(self, *args, **kwargs):
    raise NotImplementedError("子类未实现该接口")
def task(func):
  class SubTask(Task):
    def run(self, *args, **kwargs):
      func(*args, **kwargs)
  return SubTask()

第二章 上下文管理器

定义

包装任意代码

确保执行的一致性

语法

with语句

__enter__和__exit__方法

class ContextManager(object):
  def __init__(self):
    self.entered = False    
  def __enter__(self):
    self.entered = True
    return self    
  def __exit__(self, exc_type, exc_instance, traceback):
    self.entered = False

应用场景

资源清理

import pymysql

class DBConnection(object):
  def __init__(self, *args, **kwargs):
    self.args,self.kwargs = args, kwargs
    
  def __enter__(self):
    self.conn = pymysql.connect(*args, **kwargs)
    return self.conn.cursor()
    
  def __exit__(self, exc_type, exc_instance, trackback):
    self.conn.close()

异常处理(避免重复)

传播异常(__exit__中return False)

终止异常(__exit__中return True)

class BubleExceptions(object):
  def __enter__(self):
    return self
  def __exit__(self, exc_type, exc_instance, trackback):
    if exc_instance:
      print("出现异常: {}".format(exc_instance)
    return False  # return True终止异常

处理特定的异常

class HandleValueError(object):
  def __enter__(self):
    return self
  def __exit__(self, exc_type, exc_instance, trackback):
    if not exc_type: return True
    if issubclass(exc_type, ValueError): 
      print("处理ValueError: {}".format(exc_instance)
    return False

if issubclass...语句改为if exec_type == ValueError则不处理ValueType的子类异常

也可以根据异常的属性来判断是否传播或终止

更简单的语法

import contextlib

@contextlib.contextmanager
def acceptable_error_codes(*codes):
  try:
    yield
  except ShellException as exc_instance:
    if exc_instance.code not in codes:
      raise
    pass

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python中argparse模块用法实例详解
Jun 03 Python
python爬虫爬取淘宝商品信息(selenum+phontomjs)
Feb 24 Python
对Python 网络设备巡检脚本的实例讲解
Apr 22 Python
Python开发的十个小贴士和技巧及长常犯错误
Sep 27 Python
python实现播放音频和录音功能示例代码
Dec 30 Python
wxpython+pymysql实现用户登陆功能
Nov 19 Python
python爬虫添加请求头代码实例
Dec 28 Python
Python3.x+迅雷x 自动下载高分电影的实现方法
Jan 12 Python
Pytorch在dataloader类中设置shuffle的随机数种子方式
Jan 14 Python
python利用paramiko实现交换机巡检的示例
Sep 22 Python
windows系统Tensorflow2.x简单安装记录(图文)
Jan 18 Python
一篇文章搞懂python混乱的切换操作与优雅的推导式
Aug 23 Python
在Python中使用K-Means聚类和PCA主成分分析进行图像压缩
Apr 10 #Python
jupyter notebook 增加kernel教程
Apr 10 #Python
Python3操作YAML文件格式方法解析
Apr 10 #Python
Jupyter Notebook远程登录及密码设置操作
Apr 10 #Python
Python 炫技操作之合并字典的七种方法
Apr 10 #Python
python+selenium+chromedriver实现爬虫示例代码
Apr 10 #Python
Python3操作读写CSV文件使用包过程解析
Apr 10 #Python
You might like
Codeigniter(CI)框架分页函数及相关知识
2014/11/03 PHP
JavaScript 类型的包装对象(Typed Wrappers)
2011/10/27 Javascript
javascript object array方法使用详解
2012/12/03 Javascript
Struts2的s:radio标签使用及用jquery添加change事件
2013/04/08 Javascript
jquery检测input checked 控件是否被选中的方法
2014/03/26 Javascript
JavaScript中判断整数的多种方法总结
2014/11/08 Javascript
jQuery插件MixItUp实现动画过滤和排序
2015/04/12 Javascript
js兼容火狐获取图片宽和高的方法
2015/05/21 Javascript
js从外部获取图片的实现方法
2016/08/05 Javascript
JS实现简单易用的手机端浮动窗口显示效果
2016/09/07 Javascript
Ajax异步获取html数据中包含js方法无效的解决方法
2017/02/20 Javascript
AngularJS实现表单验证功能详解
2017/10/12 Javascript
利用原生js实现html5小游戏之打砖块(附源码)
2018/01/03 Javascript
js中getter和setter用法实例分析
2018/08/14 Javascript
angularJs在多个控制器中共享服务数据的方法
2018/09/30 Javascript
手把手教你 CKEDITOR 4 实现Dialog 内嵌 IFrame操作详解
2019/06/18 Javascript
Vue 使用typescript如何优雅的调用swagger API
2020/09/01 Javascript
[06:09]辉夜杯主赛事开幕式
2015/12/25 DOTA
Python实现在线暴力破解邮箱账号密码功能示例【测试可用】
2017/09/06 Python
使用python生成目录树
2018/03/29 Python
python中的turtle库函数简单使用教程
2018/07/23 Python
django model的update时auto_now不被更新的原因及解决方式
2020/04/01 Python
opencv 图像滤波(均值,方框,高斯,中值)
2020/07/08 Python
关于Python 解决Python3.9 pandas.read_excel(‘xxx.xlsx‘)报错的问题
2020/11/28 Python
HTML5 canvas画矩形时出现边框样式不一致的解决方法
2013/10/14 HTML / CSS
Dr. Martens马汀博士官网:马丁靴始祖品牌
2016/10/15 全球购物
捷克钓鱼用品网上商店:Parys.cz
2018/06/15 全球购物
英国最大的自有市场,比亚马逊便宜:Flubit
2019/03/19 全球购物
美国电子产品主要品牌的授权在线零售商:DataVision
2019/03/23 全球购物
智能钱包:Ekster
2019/11/21 全球购物
2015年秋季校长开学典礼致辞
2015/07/29 职场文书
特种设备安全管理制度
2015/08/06 职场文书
2016年乡镇综治宣传月活动总结
2016/03/16 职场文书
企业内部管理控制:银行存款控制制度范本
2020/01/10 职场文书
2022年四月新番
2022/03/15 日漫
基于Apache Hudi在Google云构建数据湖平台的思路详解
2022/04/07 Servers