在Python 3中安排重复事件
我想在Python 3中安排一个每分钟重复运行的事件。
我见过一个叫 sched.scheduler
的类,但我在想有没有其他的方法可以做到这一点。我听说可以用多个线程来实现这个,我对此也不介意。
我的主要任务是请求一些JSON数据,然后解析这些数据;这些数据的值会随着时间变化。
如果要使用 sched.scheduler
,我需要创建一个循环来请求它安排事件在一个小时内运行:
scheduler = sched.scheduler(time.time, time.sleep)
# Schedule the event. THIS IS UGLY!
for i in range(60):
scheduler.enter(3600 * i, 1, query_rate_limit, ())
scheduler.run()
还有什么其他的方法可以做到这一点呢?
14 个回答
25
我对这个话题的简单看法:
from threading import Timer
class RepeatedTimer(object):
def __init__(self, interval, function, *args, **kwargs):
self._timer = None
self.function = function
self.interval = interval
self.args = args
self.kwargs = kwargs
self.is_running = False
self.start()
def _run(self):
self.is_running = False
self.start()
self.function(*self.args, **self.kwargs)
def start(self):
if not self.is_running:
self._timer = Timer(self.interval, self._run)
self._timer.start()
self.is_running = True
def stop(self):
self._timer.cancel()
self.is_running = False
使用方法:
from time import sleep
def hello(name):
print "Hello %s!" % name
print "starting..."
rt = RepeatedTimer(1, hello, "World") # it auto-starts, no need of rt.start()
try:
sleep(5) # your long-running job goes here...
finally:
rt.stop() # better in a try/finally block to make sure the program ends!
特点:
- 只使用标准库,不需要其他外部依赖
- 采用了Alex Martnelli建议的模式
start()
和stop()
可以安全地多次调用,即使计时器已经开始或停止- 要调用的函数可以接受位置参数和命名参数
- 你可以随时更改
interval
,下次运行时就会生效。args
、kwargs
甚至function
也是一样!
41
你可以使用schedule这个库。它可以在Python 2.7和3.3上运行,而且非常轻量级:
import schedule
import time
def job():
print("I'm working...")
schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
while 1:
schedule.run_pending()
time.sleep(1)
69
你可以使用 threading.Timer
,但这也只是安排一个一次性的事件,就像调度器对象的 .enter
方法一样。
在任何编程语言中,把一次性调度器变成定期调度器的常见做法是让每个事件在指定的时间间隔内重新安排自己。例如,使用 sched
时,我不会像你那样用循环,而是会用类似下面的方式:
def periodic(scheduler, interval, action, actionargs=()):
scheduler.enter(interval, 1, periodic,
(scheduler, interval, action, actionargs))
action(*actionargs)
然后通过调用来启动整个“永远定期调度”的过程
periodic(scheduler, 3600, query_rate_limit)
或者,我可以用 threading.Timer
来代替 scheduler.enter
,但它们的模式其实很相似。
如果你需要更细致的变化(比如在特定时间停止定期调度或在某些条件下停止),这也不难,只需要加几个额外的参数就可以了。