如何在异步函数上使用多个装饰器?

时间:2020-07-17 02:35:15

标签: python decorator discord.py discord.py-rewrite

我正在使用discord.py模块尝试设置机器人。在模块中编写命令如下:

@bot.command()
async def test_function(ctx):
    await ctx.send('test')

我想将其他装饰器应用于这些命令功能之一,但是只需执行以下操作即可:

def second_decorator(func):
    def wrapper():
        print('wrapper activated!')
        func()
    return wrapper

@second_decorator
@bot.command()
async def test_function...

似乎不起作用。据我所知,第二个装饰器根本没有生效-“包装器已激活!”永远不会打印。

TypeError: Callback must be a coroutine.中切换两个结果的顺序,从我在多个装饰器上看到的文档来看,这都不是正确的格式。

1 个答案:

答案 0 :(得分:2)

通常在装饰函数时这样做是因为要在函数上调用装饰器,并使用装饰器的结果而不是函数定义。 bot.command可以做到这一点,但更重要的是,它会将功能注册为机器人内部的回调。伪代码版本看起来像

def command(self, callback, **options):
    if not valid(callback):
        raise error
    comm = Command(callback, **options)
    self.commands[comm.name] = comm
    return comm

因此,在调用第二个装饰器时(装饰器从下到上评估),该回调已在该漫游器中注册。 test_function将引用wrapped,但向漫游器注册的回调将不会。

您无法以其他方式订购装饰器,因为command()要求其参数(second_decorator的返回值wrapped)为协程(async def函数) 。这是我写second_decorator

的方法
def second_decorator(coro):
    print("function wrapped")
    @functools.wrap(coro)  # Important to preserve name because `command` uses it
    async def wrapper(*args, **kwargs):
        print('wrapped function called')
        return await coro(*args, **kwargs)
    return wrapper