使用APScheduler装饰单例类方法报错:未提供self参数
问题原因
当你在类的实例方法上直接使用 @scheduler.scheduled_job 装饰器时,调度器会将该方法作为独立函数注册。由于装饰器在类定义阶段执行,此时还未创建类的实例,方法并未绑定到任何实例上。当调度器触发任务时,无法自动传入 self 参数,从而抛出 ValueError。
而手动调用 scheduler.add_job 时,你传入的是绑定后的实例方法(比如 scanner_instance.method1),这种方法已包含实例引用,调度器调用时无需额外传入 self。
解决方案
方案1:通过包装函数调用单例实例的方法
先创建单例实例,再定义包装函数,用装饰器修饰这些包装函数,在包装函数内部调用单例的实例方法。调度器触发的是不需要 self 的包装函数,间接执行目标方法。
示例代码:
from apscheduler.schedulers.background import BackgroundScheduler scheduler = BackgroundScheduler() class Scanner: _instance = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance def method1(self): print("执行method1") def method2(self): print("执行method2") # 初始化单例实例 scanner = Scanner() # 定义包装函数并使用装饰器 @scheduler.scheduled_job('interval', seconds=10) def run_method1(): scanner.method1() @scheduler.scheduled_job('interval', minutes=5) def run_method2(): scanner.method2() # 启动调度器 scheduler.start()
方案2:在单例初始化时注册任务(类内封装)
如果希望任务注册逻辑封装在类内部,可以在单例的 __init__ 方法中使用 scheduler.add_job 绑定实例方法。虽然不是直接用装饰器,但能达到类似效果,且保持代码内聚。
示例代码:
from apscheduler.schedulers.background import BackgroundScheduler scheduler = BackgroundScheduler() class Scanner: _instance = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) # 初始化时注册任务 cls._instance._register_jobs() return cls._instance def _register_jobs(self): scheduler.add_job(self.method1, 'interval', seconds=10) scheduler.add_job(self.method2, 'interval', minutes=5) def method1(self): print("执行method1") def method2(self): print("执行method2") # 初始化单例时自动注册任务 scanner = Scanner() # 启动调度器 scheduler.start()
方案3:自定义装饰器绑定实例(进阶)
自定义一个装饰器,延迟绑定单例实例到方法上,这样就能直接在类内使用类似原装饰器的语法。
示例代码:
from apscheduler.schedulers.background import BackgroundScheduler import functools scheduler = BackgroundScheduler() def singleton_scheduled_job(*args, **kwargs): def decorator(func): @functools.wraps(func) def wrapper(): # 获取单例实例并调用方法 return func(Scanner()) # 用原装饰器注册包装后的函数 scheduler.scheduled_job(*args, **kwargs)(wrapper) return func return decorator class Scanner: _instance = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance @singleton_scheduled_job('interval', seconds=10) def method1(self): print("执行method1") @singleton_scheduled_job('interval', minutes=5) def method2(self): print("执行method2") # 启动调度器 scheduler.start()
这个自定义装饰器会创建包装函数,每次调度器触发时自动获取单例实例并传入 self 参数,让你能在类内直接使用装饰器语法。
内容的提问来源于stack exchange,提问作者Yoni Melki
相关产品推荐
相关产品推荐

