Tornado PeriodicCallback第二次运行失败:'NoneType'对象不可调用
为什么Tornado的PeriodicCallback仅第一次执行,后续触发报NoneType错误?
这个问题我之前帮不少开发者排查过,大概率是你初始化PeriodicCallback的时候踩了个常见的小坑——把函数调用的结果传给了它,而不是函数本身。
最常见的错误场景
比如你可能写了类似这样的代码:
# 错误示例:不小心加了括号,执行函数并把返回值传给PeriodicCallback def monitor_module_a(): # 监控模块A的逻辑 print("监控模块A") def start_scheduler(): # 错误:这里调用了monitor_module_a(),返回None callback_a = PeriodicCallback(monitor_module_a(), 5000) callback_a.start()
当你启动服务器时,monitor_module_a()会被立即执行一次(这就是你看到的第一次正常执行),但这个函数如果没有显式返回值,默认返回None。这就导致PeriodicCallback内部保存的callback属性变成了None。等到下一次触发间隔到了,IOLoop尝试调用self.callback(),自然就会抛出TypeError: 'NoneType' object is not callable。
正确的写法应该是直接传入函数对象,不要加括号:
# 正确示例:传入函数本身,而不是调用结果 def start_scheduler(): callback_a = PeriodicCallback(monitor_module_a, 5000) callback_a.start()
其他可能的原因
如果上面的情况不是你的问题,那可以排查这两个方向:
- 实例方法被销毁:如果你的回调是某个类的实例方法,比如
my_instance.monitor(),但这个实例my_instance在start_scheduler执行完后被当成局部变量回收了(比如没有全局引用),那后续触发时,方法的引用就会变成无效的None。这种情况要确保实例的生命周期和服务器一致,比如把实例存在全局变量或者服务器对象的属性里。 - 回调引用被意外覆盖:有没有在代码的其他地方,不小心把
PeriodicCallback的callback属性改成了None?比如在回调函数内部错误地修改了自身的引用。
快速验证方法
你可以在start_scheduler里初始化完PeriodicCallback后,打印一下它的callback属性:
def start_scheduler(): callback_a = PeriodicCallback(monitor_module_a, 5000) print(callback_a.callback) # 应该输出<function monitor_module_a at 0x...>,而不是None callback_a.start()
如果打印结果是None,那肯定是初始化时传错了;如果是正常的函数/方法对象,再去排查实例生命周期或者引用覆盖的问题。
内容的提问来源于stack exchange,提问作者Peter Pik
相关产品推荐
相关产品推荐

