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中的defaultdict模块和namedtuple模块的简单入门指南
Apr 01 Python
详解在Python中处理异常的教程
May 24 Python
Python列表和元组的定义与使用操作示例
Jul 26 Python
python 随机数使用方法,推导以及字符串,双色球小程序实例
Sep 12 Python
django.db.utils.ProgrammingError: (1146, u“Table‘’ doesn’t exist”)问题的解决
Jul 13 Python
实例详解Matlab 与 Python 的区别
Apr 26 Python
网易2016研发工程师编程题 奖学金(python)
Jun 19 Python
python3实现猜数字游戏
Dec 07 Python
python循环定时中断执行某一段程序的实例
Jun 29 Python
python写日志文件操作类与应用示例
Jul 01 Python
Anconda环境下Vscode安装Python的方法详解
Mar 29 Python
python有几个版本
Jun 17 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
星际实力自我测试
2020/03/04 星际争霸
DOMXML函数笔记
2006/10/09 PHP
PHP函数utf8转gb2312编码
2006/12/21 PHP
PHP之数组学习
2011/05/29 PHP
通过curl模拟post和get方式提交的表单类
2014/04/23 PHP
php实现仿写CodeIgniter的购物车类
2015/07/29 PHP
PHP+MySQL之Insert Into数据插入用法分析
2015/09/27 PHP
javascript suggest效果 自动完成实现代码分享
2012/02/17 Javascript
JS脚本实现动态给标签控件添加事件的方法
2016/06/02 Javascript
javascript中sort排序实例详解
2016/07/24 Javascript
js仿iphone秒表功能 计算平均数
2017/01/11 Javascript
JavaScript数据结构之链表的实现
2017/03/19 Javascript
bootstrap table使用入门基本用法
2017/05/24 Javascript
EasyUI在Panel上动态添加LinkButton按钮
2017/08/11 Javascript
bootstrap modal+gridview实现弹出框效果
2017/08/15 Javascript
使用vue-cli创建项目的图文教程(新手入门篇)
2018/05/02 Javascript
webpack开发环境和生产环境的深入理解
2018/11/08 Javascript
9102年webpack4搭建vue项目的方法步骤
2019/02/20 Javascript
javascript自定义右键菜单插件
2019/12/16 Javascript
Python cookbook(数据结构与算法)实现对不原生支持比较操作的对象排序算法示例
2018/03/15 Python
pandas.dataframe中根据条件获取元素所在的位置方法(索引)
2018/06/07 Python
Python实现的建造者模式示例
2018/08/06 Python
Python使用Tkinter实现转盘抽奖器的步骤详解
2020/01/06 Python
Python for循环通过序列索引迭代过程解析
2020/02/07 Python
Python任务自动化工具tox使用教程
2020/03/17 Python
python 图像增强算法实现详解
2021/01/24 Python
CSS3实现千变万化的文字阴影text-shadow效果设计
2016/04/26 HTML / CSS
英国计算机商店:Technextday
2019/12/28 全球购物
C#软件工程师英语面试题
2015/06/07 面试题
实体的生命周期
2013/08/31 面试题
2014年小学元旦活动方案
2014/02/12 职场文书
酒店总经理岗位职责
2015/04/01 职场文书
公司晚会主持词
2019/04/17 职场文书
php7中停止php-fpm服务的方法详解
2021/05/09 PHP
python获取对象信息的实例详解
2021/07/07 Python
mysql5.5中文乱码问题解决的有用方法
2022/05/30 MySQL