在python中,如何简单地使用与正常功能和协程功能兼容的装饰器?

时间:2018-07-15 03:07:13

标签: python

这是我的方法,但是我觉得这不是很简单,还有更好的方法吗?

import asyncio
import time


def timer_all(f):
    if asyncio.iscoroutinefunction(f):
        async def wrapper(*args, **kwargs):
            now = time.time()
            result = await f(*args, **kwargs)
            print('used {}'.format(time.time() - now))
            return result
    else:
        def wrapper(*args, **kwargs):
            now = time.time()
            result = f(*args, **kwargs)
            print('used {}'.format(time.time() - now))
            return result

    return wrapper

有很多装饰器,重试,添加日志等,都将以这种方式编写,有点难看,对吧?

1 个答案:

答案 0 :(得分:1)

尽管在专门的装饰器中重复相同的代码并没有真正的问题。 这就是我将如何进行重构的方法。

我将使用一个类装饰器,该装饰器保持接受预调用功能和后调用功能, 两者都将用装饰器的实例调用。 调用前功能的结果将保存到装饰器的属性中。

这对于需要计算增量的特殊计时情况是必需的。

我猜可能还有其他一些示例,可能需要调用函数执行前的返回值。

我还将保存执行的装饰函数的结果保存到装饰器实例的result属性中。这使post call函数可以读取该值以进行记录。

这是一个示例实现:

import asyncio


class WrapAll(object):
    def __init__(self, pre=lambda _: None, post=lambda _: None):
        self.pre = lambda : pre(self)
        self.pre_val = None
        self.result = None
        self.post = lambda : post(self)

    def __call__(self, fn):
        if asyncio.iscoroutinefunction(fn):
            async def wrap(*args, **kwargs):
                self.pre_val = self.pre()
                self.result = await fn(*args, *kwargs)
                self.post()
                return self.result
        else:
            def wrap(*args, **kwargs):
                self.pre_val = self.pre()
                self.result = fn(*args, *kwargs)
                self.post()
                return self.result
        return wrap

计时器示例

import asyncio
import time

timer = dict(
    pre=lambda self: time.time(),
    post=lambda self: print('used {}'.format(time.time()-self.pre_val))
)

@WrapAll(**timer)
def add(x, y):
    return x + y

@WrapAll(**timer)
async def async_add(x, y):
    future = asyncio.Future()
    future.set_result(x+y)
    await future
    return future.result()
运行同步加法器
>>> add(3, 4)
used 4.76837158203125e-06
7
运行异步加法器
>>> loop = asyncio.get_event_loop()
>>> task = asyncio.ensure_future(async_add(3, 4))
>>> try:
...    loop.run_until_complete(task)
... except RuntimeError:
...     pass
used 2.193450927734375e-05

记录示例

import asyncio
import logging

FORMAT = '%(message)s'
logging.basicConfig(format=FORMAT)

logger = dict(
    post=lambda self: logging.warning('subtracting {}'.format(self.result))
)

@WrapAll(**logger)
def sub(x, y):
    return x - y

@WrapAll(**logger)
async def async_sub(x, y):
    future = asyncio.Future()
    future.set_result(x-y)
    await future
    return future.result()

运行同步减法器:

>>> sub(5, 6)
subtracting -1

运行异步减法器:

>>> loop = asyncio.get_event_loop()
>>> task = asyncio.ensure_future(async_sub(5, 6))
>>> try:
...     loop.run_until_complete(task)
... except RuntimeError:
...     pass
subtracting -1