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执行shell获取硬件参数写入mysql的方法
Dec 29 Python
django admin添加数据自动记录user到表中的实现方法
Jan 05 Python
Python输出由1,2,3,4组成的互不相同且无重复的三位数
Feb 01 Python
Python列表推导式、字典推导式与集合推导式用法实例分析
Feb 07 Python
详解Python做一个名片管理系统
Mar 14 Python
Django基础三之视图函数的使用方法
Jul 18 Python
Python 从attribute到property详解
Mar 05 Python
使用Python文件读写,自定义分隔符(custom delimiter)
Jul 05 Python
一篇文章搞懂python的转义字符及用法
Sep 03 Python
5款实用的python 工具推荐
Oct 13 Python
Python基于template实现字符串替换
Nov 27 Python
详解Python中的for循环
Apr 30 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
木翼下载系统中说明的PHP安全配置方法
2007/06/16 PHP
php获取图片信息的方法详解
2015/12/10 PHP
自己的js工具_Form 封装
2009/08/21 Javascript
javascript 触发事件列表 比较不错
2009/09/03 Javascript
JQuery Dialog的内存泄露问题解决方法
2010/06/18 Javascript
jQuery在vs2008及js文件中的无智能提示的解决方法
2010/12/30 Javascript
原生js ActiveXObject获取execl里面的值
2013/11/01 Javascript
js,jquery滚动/跳转页面到指定位置的实现思路
2014/06/03 Javascript
浅谈几种常用的JS类定义方法
2016/06/08 Javascript
Vue-router路由判断页面未登录跳转到登录页面的实例
2017/10/26 Javascript
vue系列之requireJs中引入vue-router的方法
2018/07/18 Javascript
JavaScript实现JSON合并操作示例【递归深度合并】
2018/09/07 Javascript
Vue核心概念Getter的使用方法
2019/01/18 Javascript
解决layui动态添加的元素click等事件触发不了的问题
2019/09/20 Javascript
《javascript设计模式》学习笔记四:Javascript面向对象程序设计链式调用实例分析
2020/04/07 Javascript
详解Vue.js 响应接口
2020/07/04 Javascript
[14:51]DOTA2 HEROS教学视频教你分分钟做大人-卓尔游侠
2014/06/13 DOTA
修改Python的pyxmpp2中的主循环使其提高性能
2015/04/24 Python
浅析Python中的for 循环
2016/06/09 Python
Python 中pandas.read_excel详细介绍
2017/06/23 Python
Python字典实现简单的三级菜单(实例讲解)
2017/07/31 Python
使用Python制作简单的小程序IP查看器功能
2019/04/16 Python
python matplotlib库绘制条形图练习题
2019/08/10 Python
Python 正则表达式爬虫使用案例解析
2019/09/23 Python
python实现七段数码管和倒计时效果
2019/11/23 Python
python退出循环的方法
2020/06/18 Python
GitHub上值得推荐的8个python 项目
2020/10/30 Python
国际知名军事风格休闲装品牌:Alpha Industries(阿尔法工业)
2017/05/24 全球购物
瑞士图书网站:Weltbild.ch
2019/09/17 全球购物
Kappa英国官方在线商店:服装和运动器材
2020/11/22 全球购物
PPP协议组成及简述协议协商的基本过程
2015/05/28 面试题
家具厂厂长岗位职责
2014/01/01 职场文书
中秋节礼品促销方案
2014/02/02 职场文书
烹饪大赛策划方案
2014/05/26 职场文书
2014年酒店工作总结与计划
2014/11/17 职场文书
安全生产奖惩制度
2015/08/06 职场文书