discord.py cog内用监听器调用同cog任务函数的报错问题问询
问题排查与修复方案
各阶段报错的根因定位
- 初始代码的「变量未定义」错误:Cog是基于类的扩展结构,所有内部方法、属性都属于类实例成员,必须通过
self.前缀访问。直接在监听器里写repeater.start(),会在当前方法的局部作用域查找同名变量,找不到对应引用就会触发未定义异常。 - 修改为
self.repeater()后触发的Cog.listener类型错误: - 核心是混淆了「普通async函数」和「
tasks.Loop实例」的逻辑边界。self.repeater不是可直接调用的普通函数,是被@tasks.loop装饰器包装生成的后台循环调度对象; - 这个报错的直接触发场景,通常是误将业务代码作为参数传入
@commands.Cog.listener装饰器(比如错写为@commands.Cog.listener(self.repeater()))——该装饰器要求传入的参数是事件名字符串,收到函数类型的实参就会抛出类型校验失败。 - 修改为
await self.repeater()后循环只执行一次的问题:直接await循环调度对象,本质是执行它内部包裹的原始协程,完全绕过了tasks.Loop自带的间隔计时、后台调度、状态管理逻辑,自然无法实现每10秒重复运行的效果。
可正常运行的实现代码
修正两个核心逻辑:所有类内成员访问加self.前缀,启动循环任务必须调用循环对象自带的.start()方法。
完整代码如下:
import discord from discord.ext import commands, tasks class Repeater(commands.Cog): def __init__(self, client): self.client = client @tasks.loop(seconds=10) async def repeater(self): # 增加空值校验,避免频道未加载完成时发送消息报错 channel = self.client.get_channel(834554679265329172) if channel: await channel.send('test') @commands.Cog.listener() async def on_ready(self): # 避免on_ready事件重复触发导致的任务重复启动 if not self.repeater.is_running(): self.repeater.start() def setup(client): client.add_cog(Repeater(client))
优化提示
on_ready事件会在机器人网关重连、断线恢复时反复触发,必须搭配is_running()状态判断,否则会出现循环任务重复启动的报错。- 如果循环逻辑依赖机器人缓存的频道、成员数据,可以给repeater任务加前置等待逻辑,确保机器人完全加载后再启动循环:
@repeater.before_loop async def before_repeater(self): await self.client.wait_until_ready()
内容的提问来源于stack exchange,提问作者Joseph Vandiver
相关产品推荐
相关产品推荐

