相当于python中的setInterval

2024-06-10 00:37:59 发布

您现在位置:Python中文网/ 问答频道 /正文

我最近发表了一篇question关于如何在Python中推迟函数的执行(相当于Javascript setTimeout)的文章,结果发现这是一个使用threading.Timer的简单任务(好吧,只要函数不与其他代码共享状态,但这会在任何事件驱动环境中产生问题)。

现在我正努力做得更好,并模仿setInterval。对于那些不熟悉Javascript的人,setInterval允许每隔x秒重复调用一个函数,而不阻塞其他代码的执行。我已经创建了这个示例decorator:

import time, threading

def setInterval(interval, times = -1):
    # This will be the actual decorator,
    # with fixed interval and times parameter
    def outer_wrap(function):
        # This will be the function to be
        # called
        def wrap(*args, **kwargs):
            # This is another function to be executed
            # in a different thread to simulate setInterval
            def inner_wrap():
                i = 0
                while i != times:
                    time.sleep(interval)
                    function(*args, **kwargs)
                    i += 1
            threading.Timer(0, inner_wrap).start()
        return wrap
    return outer_wrap

使用如下

@setInterval(1, 3)
def foo(a):
    print(a)

foo('bar')
# Will print 'bar' 3 times with 1 second delays

在我看来,一切都很顺利。我的问题是

  • 它看起来太复杂了,我担心我可能错过了一个更简单/更好的机制
  • 可以在不使用第二个参数的情况下调用decorator,在这种情况下,它将永远运行下去。当我说foreover时,我的意思是永远——即使从主线程调用sys.exit()也不会停止它,也不会命中Ctrl+c。阻止它的唯一方法是从外部终止python进程。我希望能够从主线程发送一个停止回调的信号。但我是一个线程初学者-我如何在它们之间进行通信?

编辑如果有人想知道,这是decorator的最终版本,感谢jd的帮助

import threading

def setInterval(interval, times = -1):
    # This will be the actual decorator,
    # with fixed interval and times parameter
    def outer_wrap(function):
        # This will be the function to be
        # called
        def wrap(*args, **kwargs):
            stop = threading.Event()

            # This is another function to be executed
            # in a different thread to simulate setInterval
            def inner_wrap():
                i = 0
                while i != times and not stop.isSet():
                    stop.wait(interval)
                    function(*args, **kwargs)
                    i += 1

            t = threading.Timer(0, inner_wrap)
            t.daemon = True
            t.start()
            return stop
        return wrap
    return outer_wrap

它可以用于固定数量的重复,如上所述

@setInterval(1, 3)
def foo(a):
    print(a)

foo('bar')
# Will print 'bar' 3 times with 1 second delays

或者可以让它一直运行直到收到停止信号

import time

@setInterval(1)
def foo(a):
    print(a)

stopper = foo('bar')

time.sleep(5)
stopper.set()
# It will stop here, after printing 'bar' 5 times.

Tags: toreturnfoodefbarfunctiondecoratorbe
3条回答

也许这是python中最简单的setInterval等价物:

 import threading

 def set_interval(func, sec):
    def func_wrapper():
        set_interval(func, sec) 
        func()  
    t = threading.Timer(sec, func_wrapper)
    t.start()
    return t

我看你的解决办法不错。

有几种与线程通信的方法。要命令线程停止,可以使用threading.Event(),它有一个wait()方法,您可以使用该方法而不是time.sleep()

stop_event = threading.Event()
...
stop_event.wait(1.)
if stop_event.isSet():
    return
...

要使线程在程序终止时退出,请在调用start()之前将其daemon属性设置为True。这也适用于Timer()对象,因为它们是threading.Thread的子类。见http://docs.python.org/library/threading.html#threading.Thread.daemon

也许更简单的是使用递归调用计时器:

from threading import Timer
import atexit

class Repeat(object):

    count = 0
    @staticmethod
    def repeat(rep, delay, func):
        "repeat func rep times with a delay given in seconds"

        if Repeat.count < rep:
            # call func, you might want to add args here
            func()
            Repeat.count += 1
            # setup a timer which calls repeat recursively
            # again, if you need args for func, you have to add them here
            timer = Timer(delay, Repeat.repeat, (rep, delay, func))
            # register timer.cancel to stop the timer when you exit the interpreter
            atexit.register(timer.cancel)
            timer.start()

def foo():
    print "bar"

Repeat.repeat(3,2,foo)

atexit允许使用CTRL-C发出停止信号

相关问题 更多 >