Python装饰器的应用场景

╰+攻爆jí腚メ 2023-10-09 05:54 144阅读 0赞

装饰器的应用场景

  • 附加功能
  • 数据的清理或添加:

    • 函数参数类型验证 @require_ints 类似请求前拦截
    • 数据格式转换 将函数返回字典改为 JSON/YAML 类似响应后篡改
    • 为函数提供额外的数据 mock.patch
  • 函数注册

    • 在任务中心注册一个任务
    • 注册一个带信号处理器的函数

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

函数注册表

  1. 简单注册表

    funcs = []
    def register(func):

    1. funcs.append(func)
    2. return func
  1. @register
  2. def a():
  3. return 3
  4. @register
  5. def b():
  6. return 5
  7. # 访问结果
  8. result = [func() for func in funcs]
  1. 注册表隔离(使用类的不同实例)

    class Registry(object):

    1. def __init__(self):
    2. self._funcs = []
    3. def register(self, func):
    4. self._funcs.append(func)
    5. def run_all(self):
    6. return [func() for func in self._funcs]
  1. r1 = Registry()
  2. r2 = Registry()
  3. @r1.register
  4. def a():
  5. return 3
  6. @r2.register
  7. def b():
  8. return 5
  9. @r1.register
  10. @r2.register

执行时封装代码

  1. 类型检查

    from functools import wraps

    def require_ints(func):

    1. @wraps(func) # 将func的信息复制给inner
    2. def inner(*args, **kwargs):
    3. for arg list(args) + list(kwargs.values()):
    4. if not isinstance(arg, int:
    5. raise TypeError("{} 只接受int类型参数".format(func.__name__)
    6. return func(*args, **kwargs)
    7. return inner
  2. 用户验证

    from functools import wraps

    class User(object):

    1. def __init__(self, username, email):
    2. self.username = username
    3. self.email = email

    class AnonymousUser(object):

    1. def __init__(self):
    2. self.username = self.email = None
    3. def __nonzero__(self): # 将对象转换为bool类型时调用
    4. return False

    def requires_user(func):

    1. @wraps(func)
    2. def inner(user, *args, **kwargs): # 由于第一个参数无法支持self, 该装饰器不支持装饰类
    3. if user and isinstance(user, User):
    4. return func(use, *args, **kwargs)
    5. else:
    6. raise ValueError("非合法用户")
    7. return inner
  3. 输出格式化

    import json
    from functools import wraps

    def json_output(func): # 将原本func返回的字典格式转为返回json字符串格式

    1. @wrap(func)
    2. def inner(*args, **kwargs):
    3. return json.dumps(func(*args, **kwargs))
    4. return inner
  4. 异常捕获

    import json
    from functools import wraps

    class Error1(Exception):

    1. def __init__(self, msg):
    2. self.msg = msg
    3. def __str__(self):
    4. return self.msg
  1. def json_output(func):
  2. @wrap(func)
  3. def inner(*args, **kwargs):
  4. try:
  5. result = func(*args, **kwargs)
  6. except Error1 as ex:
  7. result = {"status": "error", "msg": str(ex)}
  8. return json.dumps(result)
  9. return inner
  10. # 使用方法
  11. @json_ouput
  12. def error():
  13. raise Error1("该条异常会被捕获并按JSON格式输出")
  1. 日志管理

    import time
    import logging
    from functools import wraps

    def logged(func):

    1. @wraps(func)
    2. def inner(*args, **kwargs): # *args可以装饰函数也可以装饰类
    3. start = time.time()
    4. result = func(*args, **kwargs)
    5. exec_time = time.time() - start
    6. logger = logging.getLoger("func.logged")
    7. logger.warning("{} 调用时间:{:.2} 执行时间:{:.2}s 结果:{}".format(func.__name__, start, exec_time, result)

带参数的装饰器

带参数的装饰器相当于一个返回装饰器的函数,@deco(a=1)在调用@之前会首先执行deco(a=1)得到一个实际的装饰器, 带参数的装饰器deco(a=1)模块导入时立即执行

装饰类

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

    import time
    from functools import wraps

    def sortable_by_created(cls):

    1. original_init = cls.__init__
    2. @wrap(original_init)
    3. def new_init(self, *args, **kwargs):
    4. original_init(*args, **kwargs)
    5. self._created = time.time()
    6. cls.__init__ = new_init
    7. cls.__lt__ = lambda self, other: self._created < other._created
    8. cls.__gt__ = lambda self, other: self._created > other._created
    9. return cls

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

类型转换

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

  1. class Task(object):
  2. def __call__(self, *args, **kwargs):
  3. return self.run(*args, **kwargs)
  4. def run(self, *args, **kwargs):
  5. raise NotImplementedError("子类未实现该接口")
  6. def task(func):
  7. class SubTask(Task):
  8. def run(self, *args, **kwargs):
  9. func(*args, **kwargs)
  10. return SubTask()

第二章 上下文管理器

定义

  • 包装任意代码
  • 确保执行的一致性

语法

  • with语句
  • __enter__和__exit__方法

    class ContextManager(object):

    1. def __init__(self):
    2. self.entered = False
    3. def __enter__(self):
    4. self.entered = True
    5. return self
    6. def __exit__(self, exc_type, exc_instance, traceback):
    7. self.entered = False

应用场景

资源清理

  1. import pymysql
  2. class DBConnection(object):
  3. def __init__(self, *args, **kwargs):
  4. self.args,self.kwargs = args, kwargs
  5. def __enter__(self):
  6. self.conn = pymysql.connect(*args, **kwargs)
  7. return self.conn.cursor()
  8. def __exit__(self, exc_type, exc_instance, trackback):
  9. self.conn.close()

异常处理(避免重复)

  • 传播异常(__exit__中return False)
  • 终止异常(__exit__中return True)

    class BubleExceptions(object):

    1. def __enter__(self):
    2. return self
    3. def __exit__(self, exc_type, exc_instance, trackback):
    4. if exc_instance:
    5. print("出现异常: {}".format(exc_instance)
    6. return False # return True终止异常
  • 处理特定的异常

    class HandleValueError(object):

    1. def __enter__(self):
    2. return self
    3. def __exit__(self, exc_type, exc_instance, trackback):
    4. if not exc_type: return True
    5. if issubclass(exc_type, ValueError):
    6. print("处理ValueError: {}".format(exc_instance)
    7. return False

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

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

更简单的语法

  1. import contextlib
  2. @contextlib.contextmanager
  3. def acceptable_error_codes(*codes):
  4. try:
  5. yield
  6. except ShellException as exc_instance:
  7. if exc_instance.code not in codes:
  8. raise
  9. pass

转载于:https://www.cnblogs.com/superhin/p/11454823.html

发表评论

表情:
评论列表 (有 0 条评论,144人围观)

还没有评论,来说两句吧...

相关阅读