python - 如何对协程进行限速,限速后重新调用协程?

标签 python asynchronous discord discord.py rate-limiting

我一直在阅读这里的回复:What's a good rate limiting algorithm?

Carlos A. Ibarra 的回复在没有异步功能的情况下工作得很好,但有什么方法可以修改它以异步工作吗?

import time

def RateLimited(maxPerSecond):
    minInterval = 1.0 / float(maxPerSecond)
    def decorate(func):
        lastTimeCalled = [0.0]
        def rateLimitedFunction(*args,**kargs):
            elapsed = time.clock() - lastTimeCalled[0]
            leftToWait = minInterval - elapsed
            if leftToWait>0:
                time.sleep(leftToWait)
            ret = func(*args,**kargs)
            lastTimeCalled[0] = time.clock()
            return ret
        return rateLimitedFunction
    return decorate

@RateLimited(2)  # 2 per second at most
def PrintNumber(num):
    print num

if __name__ == "__main__":
    print "This should print 1,2,3... at about 2 per second."
    for i in range(1,100):
        PrintNumber(i)

time.sleep(leftToWait) 更改为 await asyncio.sleep(leftToWait) 并等待 PrintNumber(i) 对第一个实例有效,但此后没有。我真的是 Python 的新手,并且尽我最大的努力遵守 API 的速率限制。

我的实现:

def rate_limited(max_per_second):
    min_interval = 1.0 / float(max_per_second)

    def decorate(func):
        last_time_called = [0.0]

        async def rate_limited_function(*args, **kargs):
            elapsed = time.clock() - last_time_called[0]
            left_to_wait = min_interval - elapsed
            if left_to_wait > 0:
                await asyncio.sleep(left_to_wait)
            ret = func(*args, **kargs)
            last_time_called[0] = time.clock()
            return ret
        return rate_limited_function
    return decorate


class Test:
    def __init__(self, bot):
        self.bot = bot

    @commands.command(hidden=True, pass_context=True)
    @checks.serverowner()
    async def test1(self, ctx):
        await self.print_number()

    @rate_limited(0.1)
    def print_number(self):
        print("TEST")

最佳答案

这是一个简单的 discord.py 解决方案。这使用 on_command_error 事件来保持命令并永远运行它直到冷却时间得到解决,基本上是通过使用 asyncio.sleep 等待冷却时间结束:

bot = commands.Bot('?')

@bot.command(hidden=True, pass_context=True)
@commands.cooldown(1, 5, commands.BucketType.user)  # means "allow to be called 1 time every 5 seconds for this user, anywhere"
async def test(ctx):
    print("TEST")

@bot.event
async def on_command_error(exc, context: commands.Context):
    if isinstance(exc, commands.errors.CommandOnCooldown):
        while True:
            await asyncio.sleep(exc.retry_after)
            try:
                return await context.command.invoke(context)
            except commands.errors.CommandOnCooldown as e:
                exc = e

例子

在 discord 中(假设前缀是 ?):

0s> ?test
1s> ?test
2s> ?test

在控制台中:

0s> TEST
5s> TEST
10s> TEST

关于python - 如何对协程进行限速,限速后重新调用协程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51144059/

相关文章:

node.js - 在 Nodejs 项目中处理 Controller 发送响应的更好方法是什么?

javascript - 具有相同动画 GIF 的多个 <img> 标签的异步播放

python - 是否可以使用我的机器人在 discord.py 中附加大于 150Mb 的文件

mysql - 在 Discord.js 和 MySQL 中注册命令

asynchronous - 如何在获取 Flutter 中的当前位置时显示加载微调器?

python-3.x - 机器人两次执行相同的命令

Python 统计模块返回的标准差与计算值不同

python - 有 CPAN 的 Python 版本吗?

python - 如何正确测试模式中的有效字符,可能使用字符数组或其他一些方法(正则表达式)

python - 如何使用 Python 从 Google Cloud Function 发送具有正确名称的 zip 文件?