21 Stimmen

Äquivalent von setInterval in Python

Ich habe kürzlich eine Frage darüber, wie man die Ausführung einer Funktion in Python verschiebt (entspricht in etwa dem Javascript setTimeout ) und es stellt sich heraus, dass es eine einfache Aufgabe ist, die threading.Timer (naja, einfach, solange die Funktion ihren Zustand nicht mit anderem Code teilt, aber das würde in jeder ereignisgesteuerten Umgebung Probleme verursachen).

Jetzt versuche ich, es besser zu machen und nachzumachen setInterval . Für diejenigen, die mit Javascript nicht vertraut sind, setInterval ermöglicht es, einen Funktionsaufruf alle x Sekunden zu wiederholen, ohne die Ausführung von anderem Code zu blockieren. Ich habe diesen Beispiel-Dekorator erstellt:

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

die wie folgt zu verwenden sind

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

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

und es scheint mir, dass es gut funktioniert. Mein Problem ist, dass

  • es scheint zu kompliziert zu sein, und ich fürchte, ich habe einen einfacheren/besseren Mechanismus übersehen
  • der Dekorator kann ohne den zweiten Parameter aufgerufen werden, in diesem Fall wird er ewig weiterlaufen. Wenn ich foreover sage, meine ich für immer - sogar anrufen sys.exit() aus dem Haupt-Thread wird es nicht anhalten, ebenso wenig wie das Drücken von Ctrl+c . Die einzige Möglichkeit, ihn zu stoppen, besteht darin, den Python-Prozess von außen zu beenden. Ich möchte in der Lage sein, ein Signal aus dem Hauptthread zu senden, das den Rückruf stoppen würde. Aber ich bin ein Anfänger mit Threads - wie kann ich zwischen ihnen kommunizieren?

EDIT Falls sich jemand wundert, dies ist die endgültige Version des Dekorators, dank der Hilfe von 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

Es kann mit einer festen Anzahl von Wiederholungen wie oben verwendet werden

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

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

oder kann bis zum Erhalt eines Stoppsignals laufen gelassen werden

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.

0 Stimmen

Ich habe mir Ihren Code angeschaut und war etwas verwirrt, weil ich nicht gesehen habe, was die Schleife tatsächlich zum Anhalten bringt. Es schien mir, dass dies tatsächlich zu einer Schleife führen würde, die Folgendes aufruft function langsam bis stopper.set() aufgerufen wird, wodurch die Schleife plötzlich beschleunigt würde! Ich habe es überprüft, und in der Tat, wenn Sie eine weitere time.sleep(5) nach stopper.set() im obigen Beispiel ist genau das der Fall. Sie müssen eine Logik einbauen, um sicherzustellen, dass die Schleife anhält, sobald stop.is_set() wahr ist - z.B. while i != times and not stop.is_set():

0 Stimmen

Sie haben Recht, ich habe einen Absatz in der Dokumentation über die wait() Methode, und ich habe das Problem bei meinen Tests nicht erkannt. Ich werde den Code jetzt korrigieren.

0 Stimmen

11voto

jd. Punkte 10318

Ihre Lösung sieht für mich gut aus.

Es gibt mehrere Möglichkeiten, mit Threads zu kommunizieren. Um einen Thread zum Anhalten zu bringen, können Sie threading.Event() die einen wait() Methode, die Sie anstelle von time.sleep() .

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

Damit Ihr Thread beendet wird, wenn das Programm beendet wird, setzen Sie seine daemon Attribut zu True vor dem Aufruf start() . Dies gilt für Timer() Objekte, denn sie sind Unterklassen von threading.Thread . Siehe http://docs.python.org/library/threading.html#threading.Thread.daemon

0 Stimmen

Wunderbar! Es ist mir gelungen, die Abhängigkeit vom Zeitmodul zu beseitigen und die Kommunikation zu erreichen. Jetzt kann der Callback einfach gestoppt werden :-)

3voto

stamat Punkte 1664

Vielleicht sind dies die einfachsten setInterval-Äquivalente in Python:

 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

2voto

Bernhard Punkte 8045

Vielleicht ist es einfacher, rekursive Aufrufe an Timer zu verwenden:

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 ermöglicht es, das Anhalten mit CTRL-C zu signalisieren.

0 Stimmen

Vielen Dank für Ihren Vorschlag, aber ich habe die Antwort von jd bereits akzeptiert. Auf jeden Fall ziehe ich es vor, zu schlafen, anstatt rekursiv neue Timer zu erstellen - würde dies übrigens unendlich viele Threads erzeugen, oder werden die Threads getötet, wenn die Funktion zurückkehrt, obwohl ein von ihnen erstellter Thread noch offen ist?

0 Stimmen

AFAIK wird es rep-many Threads erzeugen, die alle beendet werden, wenn der letzte Timer beendet ist.

1 Stimmen

Dies kann lästig sein, wenn Sie die Funktion tausende Male oder sogar für immer wiederholen wollen. Trotzdem vielen Dank für Ihren Beitrag! :-)

0voto

ali Punkte 1

Diese Klasse Intervall

class ali:
    def __init__(self):
        self.sure = True;

    def aliv(self,func,san):
        print "ali naber";
        self.setInterVal(func, san);

    def setInterVal(self,func, san):

        # istenilen saniye veya dakika aralnda program calr. 
        def func_Calistir():

            func(func,san);  #calcak fonksiyon.
        self.t = threading.Timer(san, func_Calistir)
        self.t.start()
        return self.t

a = ali();
a.setInterVal(a.aliv,5);

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X