سؤال

هل لدى Python وظيفة مشابهة لـ JavaScript 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

فقط حافظ عليه لطيف وبسيط.

import threading

def setInterval(func,time):
    e = threading.Event()
    while not e.wait(time):
        func()

def foo():
    print "hello"

# using
setInterval(foo,5)

# output:
hello
hello
.
.
.

تحرير: هذا الرمز غير محافظ

import threading

class ThreadJob(threading.Thread):
    def __init__(self,callback,event,interval):
        '''runs the callback function after interval seconds

        :param callback:  callback function to invoke
        :param event: external event for controlling the update operation
        :param interval: time in seconds after which are required to fire the callback
        :type callback: function
        :type interval: int
        '''
        self.callback = callback
        self.event = event
        self.interval = interval
        super(ThreadJob,self).__init__()

    def run(self):
        while not self.event.wait(self.interval):
            self.callback()



event = threading.Event()

def foo():
    print "hello"

k = ThreadJob(foo,event,2)
k.start()

print "It is non-blocking"

هذا إصدار يمكنك من خلاله البدء والتوقف. إنه لا يحظر. لا يوجد أيضًا خلل حيث لم تتم إضافة خطأ في وقت التنفيذ (مهم للتنفيذ لفترة طويلة مع فاصل زمني قصير جدًا مثل الصوت على سبيل المثال)

import time, threading

StartTime=time.time()

def action() :
    print('action ! -> time : {:.1f}s'.format(time.time()-StartTime))


class setInterval :
    def __init__(self,interval,action) :
        self.interval=interval
        self.action=action
        self.stopEvent=threading.Event()
        thread=threading.Thread(target=self.__setInterval)
        thread.start()

    def __setInterval(self) :
        nextTime=time.time()+self.interval
        while not self.stopEvent.wait(nextTime-time.time()) :
            nextTime+=self.interval
            self.action()

    def cancel(self) :
        self.stopEvent.set()

# start action every 0.6s
inter=setInterval(0.6,action)
print('just after setInterval -> time : {:.1f}s'.format(time.time()-StartTime))

# will stop interval in 5s
t=threading.Timer(5,inter.cancel)
t.start()

الإخراج هو:

just after setInterval -> time : 0.0s
action ! -> time : 0.6s
action ! -> time : 1.2s
action ! -> time : 1.8s
action ! -> time : 2.4s
action ! -> time : 3.0s
action ! -> time : 3.6s
action ! -> time : 4.2s
action ! -> time : 4.8s

يتغيرون Nailxxإجابة قليلا وحصلت على الجواب!

from threading import Timer

def hello():
    print "hello, world"
    Timer(30.0, hello).start()

Timer(30.0, hello).start() # after 30 seconds, "hello, world" will be printed

ال sched توفر الوحدة هذه القدرات لرمز Python العام. ومع ذلك ، كما تشير وثائقها ، إذا كان الكود الخاص بك متعددًا ، فقد يكون من المنطقي استخدام threading.Timer الفصل بدلا من ذلك.

أعتقد أن هذا ما أنت بعده:

#timertest.py
import sched, time
def dostuff():
  print "stuff is being done!"
  s.enter(3, 1, dostuff, ())

s = sched.scheduler(time.time, time.sleep)
s.enter(3, 1, dostuff, ())
s.run()

إذا أضفت إدخالًا آخر إلى المجدول في نهاية طريقة التكرار ، فسوف يستمر فقط.

setInterval البسيطة

from threading import Timer

def setInterval(timer, task):
    isStop = task()
    if not isStop:
        Timer(timer, setInterval, [timer, task]).start()

def hello():
    print "do something"
    return False # return True if you want to stop

if __name__ == "__main__":
    setInterval(2.0, hello) # every 2 seconds, "do something" will be printed

في الآونة الأخيرة ، لدي نفس المشكلة مثلك. وأجد هذه الحل:

1. يمكنك استخدام المكتبة: Througing.Time(هذه مقدمة أعلاه)

2. يمكنك استخدام المكتبة: الجدولة(هذه مقدمة أعلاه أيضًا)

3. يمكنك استخدام المكتبة: جدولة بيثون المتقدمة(نوصي)

الطريقة أعلاه لم تفعل ذلك تمامًا بالنسبة لي لأنني كنت بحاجة لتكون قادرًا على إلغاء الفاصل الزمني. حولت الوظيفة إلى فصل وتوصلت إلى ما يلي:

class setInterval():
    def __init__(self, func, sec):
        def func_wrapper():
            self.t = threading.Timer(sec, func_wrapper)
            self.t.start()
            func()
        self.t = threading.Timer(sec, func_wrapper)
        self.t.start()

    def cancel(self):
        self.t.cancel()

بعض الإجابات أعلاه تستخدم func_wrapper و threading.Timer في الواقع ، العمل ، باستثناء أنه يولد موضوعًا جديدًا في كل مرة يتم فيها استدعاء الفاصل الزمني ، وهو ما يسبب مشاكل في الذاكرة.

المثال الأساسي أدناه بقسوة نفذت آلية مماثلة عن طريق وضع الفاصل الزمني على موضوع منفصل. ينام في الفاصل الزمني المعطى. قبل القفز إلى الكود ، إليك بعض القيود التي تحتاج إلى أن تكون على دراية بها:

  1. JavaScript واحد مترابط ، لذلك عندما تكون الوظيفة في الداخل setInterval يتم إطلاقه ، لن يعمل أي شيء آخر في نفس الوقت (باستثناء خيط العمال ، ولكن دعنا نتحدث عن حالة الاستخدام العام setInterval. لذلك ، الخيوط آمنة. ولكن هنا في هذا التنفيذ ، قد تواجه ظروف السباق ما لم تكن باستخدام أ threading.rLock.

  2. يستخدم التنفيذ أدناه time.sleep لمحاكاة الفواصل الزمنية ، ولكن إضافة وقت التنفيذ func, ، إجمالي الوقت لهذا فترة قد يكون أكبر مما تتوقعه. لذلك بناءً على حالات الاستخدام ، قد ترغب في "النوم أقل" (ناقص الوقت المستغرق للاتصال func)

  3. انا فقط بقسوة اختبر هذا ، ويجب بالتأكيد عدم استخدام المتغيرات العالمية بالطريقة التي فعلت بها ، لا تتردد في تعديلها بحيث تناسب نظامك.


ما يكفي من الحديث ، ها هو الكود:

# Python 2.7
import threading
import time


class Interval(object):
    def __init__(self):
        self.daemon_alive = True
        self.thread = None # keep a reference to the thread so that we can "join"

    def ticktock(self, interval, func):
        while self.daemon_alive:
            time.sleep(interval)
            func()

num = 0
def print_num():
    global num
    num += 1
    print 'num + 1 = ', num

def print_negative_num():
    global num
    print '-num = ', num * -1

intervals = {} # keep track of intervals
g_id_counter = 0 # roughly generate ids for intervals

def set_interval(interval, func):
    global g_id_counter

    interval_obj = Interval()
    # Put this interval on a new thread
    t = threading.Thread(target=interval_obj.ticktock, args=(interval, func))
    t.setDaemon(True)
    interval_obj.thread = t
    t.start()

    # Register this interval so that we can clear it later
    # using roughly generated id
    interval_id = g_id_counter
    g_id_counter += 1
    intervals[interval_id] = interval_obj

    # return interval id like it does in JavaScript
    return interval_id

def clear_interval(interval_id):
    # terminate this interval's while loop
    intervals[interval_id].daemon_alive = False
    # kill the thread
    intervals[interval_id].thread.join()
    # pop out the interval from registry for reusing
    intervals.pop(interval_id)

if __name__ == '__main__':
    num_interval = set_interval(1, print_num)
    neg_interval = set_interval(3, print_negative_num)

    time.sleep(10) # Sleep 10 seconds on main thread to let interval run
    clear_interval(num_interval)
    clear_interval(neg_interval)
    print "- Are intervals all cleared?"
    time.sleep(3) # check if both intervals are stopped (not printing)
    print "- Yup, time to get beers"

الناتج المتوقع:

num + 1 =  1
num + 1 =  2
-num =  -2
 num + 1 =  3
num + 1 =  4
num + 1 =  5
-num =  -5
num + 1 =  6
num + 1 =  7
num + 1 =  8
-num =  -8
num + 1 =  9
num + 1 =  10
-num =  -10
Are intervals all cleared?
Yup, time to get beers

وحدة بيثون 3 jsinterval.py سيكون مفيدًا! ها هو:

"""
Threaded intervals and timeouts from JavaScript
"""

import threading, sys

__all__ =  ['TIMEOUTS', 'INTERVALS', 'setInterval', 'clearInterval', 'setTimeout', 'clearTimeout']

TIMEOUTS  = {}
INTERVALS = {}

last_timeout_id  = 0
last_interval_id = 0

class Timeout:
    """Class for all timeouts."""
    def __init__(self, func, timeout):
        global last_timeout_id
        last_timeout_id += 1
        self.timeout_id = last_timeout_id
        TIMEOUTS[str(self.timeout_id)] = self
        self.func = func
        self.timeout = timeout
        self.threadname = 'Timeout #%s' %self.timeout_id

    def run(self):
        func = self.func
        delx = self.__del__
        def func_wrapper():
            func()
            delx()
        self.t = threading.Timer(self.timeout/1000, func_wrapper)
        self.t.name = self.threadname
        self.t.start()

    def __repr__(self):
        return '<JS Timeout set for %s seconds, launching function %s on timeout reached>' %(self.timeout, repr(self.func))

    def __del__(self):
        self.t.cancel()

class Interval:
    """Class for all intervals."""
    def __init__(self, func, interval):
        global last_interval_id
        self.interval_id = last_interval_id
        INTERVALS[str(self.interval_id)] = self
        last_interval_id += 1
        self.func = func
        self.interval = interval
        self.threadname = 'Interval #%s' %self.interval_id

    def run(self):
        func = self.func
        interval = self.interval
        def func_wrapper():
            timeout = Timeout(func_wrapper, interval)
            self.timeout = timeout
            timeout.run()
            func()
        self.t = threading.Timer(self.interval/1000, func_wrapper)
        self.t.name = self.threadname
        self.t.run()

    def __repr__(self):
        return '<JS Interval, repeating function %s with interval %s>' %(repr(self.func), self.interval)

    def __del__(self):
        self.timeout.__del__()

def setInterval(func, interval):
    """
    Create a JS Interval: func is the function to repeat, interval is the interval (in ms)
    of executing the function.
    """
    temp = Interval(func, interval)
    temp.run()
    idx = int(temp.interval_id)
    del temp
    return idx


def clearInterval(interval_id):
    try:
        INTERVALS[str(interval_id)].__del__()
        del INTERVALS[str(interval_id)]
    except KeyError:
        sys.stderr.write('No such interval "Interval #%s"\n' %interval_id)

def setTimeout(func, timeout):
    """
    Create a JS Timeout: func is the function to timeout, timeout is the timeout (in ms)
    of executing the function.
    """
    temp = Timeout(func, timeout)
    temp.run()
    idx = int(temp.timeout_id)
    del temp
    return idx


def clearTimeout(timeout_id):
    try:
        TIMEOUTS[str(timeout_id)].__del__()
        del TIMEOUTS[str(timeout_id)]
    except KeyError:
        sys.stderr.write('No such timeout "Timeout #%s"\n' %timeout_id)

تحرير الكود: إصلاح تسرب الذاكرة (رصدها benjaminz). الآن يتم تنظيف جميع الخيوط عند النهاية. لماذا يحدث هذا التسرب؟ يحدث ذلك بسبب المراجع الضمنية (أو حتى الصريحة). في حالتي، TIMEOUTS و INTERVALS. المهلة ذاتية النقطة تلقائيًا (بعد هذا التصحيح) لأنها تستخدم غلاف الوظائف الذي يدعو الوظيفة ثم القوات الذاتية. ولكن كيف يحدث هذا؟ لا يمكن حذف الكائنات من الذاكرة ما لم يتم حذف جميع المراجع أيضًا أو gc يتم استخدام الوحدة النمطية. شرح: لا توجد وسيلة لإنشاء (في الكود) إشارات غير مرغوب فيها إلى المهمات/الفواصل الزمنية. لديهم مرجع واحد فقط: TIMEOUTS/INTERVALS ديكتات. وعندما تتم مقاطعة أو الانتهاء (فقط يمكن أن تنتهي المهلة دون انقطاع) ، فإنهم يحذفون الإشارة الحالية الوحيدة إلى أنفسهم: عنصر التصميم المقابل. يتم تغليف الفصول الدراسية تمامًا باستخدام __all__, ، لذلك لا مساحة لتسرب الذاكرة.

فيما يلي محلول انجراف منخفض الوقت يستخدم مؤشر ترابط للإشارة بشكل دوري إلى كائن حدث. Run () Run () لا يفعل شيئًا تقريبًا أثناء انتظار مهلة ؛ وبالتالي انجراف الوقت المنخفض.

# Example of low drift (time) periodic execution of a function.
import threading
import time

# Thread that sets 'flag' after 'timeout'
class timerThread (threading.Thread):

    def __init__(self , timeout , flag):
        threading.Thread.__init__(self)
        self.timeout = timeout
        self.stopFlag = False
        self.event = threading.Event()
        self.flag = flag

    # Low drift run(); there is only the 'if'
    # and 'set' methods between waits.
    def run(self):
        while not self.event.wait(self.timeout):
            if self.stopFlag:
                break
            self.flag.set()

    def stop(self):
        stopFlag = True
        self.event.set()

# Data.
printCnt = 0

# Flag to print.
printFlag = threading.Event()

# Create and start the timer thread.
printThread = timerThread(3 , printFlag)
printThread.start()

# Loop to wait for flag and print time.
while True:

    global printCnt

    # Wait for flag.
    printFlag.wait()
    # Flag must be manually cleared.
    printFlag.clear()
    print(time.time())
    printCnt += 1
    if printCnt == 3:
        break;

# Stop the thread and exit.
printThread.stop()
printThread.join()
print('Done')

معظم الإجابات أعلاه لا تغلق الخيط بشكل صحيح. أثناء استخدام دفتر Notebook Jupyter ، لاحظت أنه عند إرسال مقاطعة صريحة ، كانت الخيوط لا تزال تعمل والأسوأ من ذلك ، فإنها ستستمر في بدء تشغيل مؤشر ترابط واحد ، 2 ، 4 إلخ. المقابض المقاطعة عن طريق تشغيل حلقة لا حصر لها في الخيط الرئيسي للاستماع إلى أحداث Sigint و Sigterm

  • لا انجراف
  • قابل للإلغاء
  • يتعامل مع sigint و sigterm بشكل جيد للغاية
  • لا يصنع موضوعًا جديدًا لكل تشغيل

لا تتردد في اقتراح تحسينات

import time
import threading
import signal

# Record the time for the purposes of demonstration 
start_time=time.time()

class ProgramKilled(Exception):
    """
    An instance of this custom exception class will be thrown everytime we get an SIGTERM or SIGINT
    """
    pass

# Raise the custom exception whenever SIGINT or SIGTERM is triggered
def signal_handler(signum, frame):
    raise ProgramKilled

# This function serves as the callback triggered on every run of our IntervalThread
def action() :
    print('action ! -> time : {:.1f}s'.format(time.time()-start_time))

# https://stackoverflow.com/questions/2697039/python-equivalent-of-setinterval
class IntervalThread(threading.Thread) :
    def __init__(self,interval,action, *args, **kwargs) :
        super(IntervalThread, self).__init__()
        self.interval=interval
        self.action=action
        self.stopEvent=threading.Event()
        self.start()

    def run(self) :
        nextTime=time.time()+self.interval
        while not self.stopEvent.wait(nextTime-time.time()) :
            nextTime+=self.interval
            self.action()

    def cancel(self) :
        self.stopEvent.set()

def main():

    # Handle SIGINT and SIFTERM with the help of the callback function
    signal.signal(signal.SIGTERM, signal_handler)
    signal.signal(signal.SIGINT, signal_handler)
    # start action every 1s
    inter=IntervalThread(1,action)
    print('just after setInterval -> time : {:.1f}s'.format(time.time()-start_time))

    # will stop interval in 500s
    t=threading.Timer(500,inter.cancel)
    t.start()

    # https://www.g-loaded.eu/2016/11/24/how-to-terminate-running-python-threads-using-signals/
    while True:
        try:
            time.sleep(1)
        except ProgramKilled:
            print("Program killed: running cleanup code")
            inter.cancel()
            break

if __name__ == "__main__":
    main()

الأمور تعمل بشكل مختلف في بيثون: تحتاج إلى أي منهما sleep() (إذا كنت تريد حظر الخيط الحالي) أو بدء تشغيل مؤشر ترابط جديد. نرى http://docs.python.org/library/Threading.html

من وثائق بيثون:

from threading import Timer

def hello():
    print "hello, world"

t = Timer(30.0, hello)
t.start() # after 30 seconds, "hello, world" will be printed
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top