python 中的延迟函数

发布于 2024-10-19 19:16:03 字数 448 浏览 0 评论 0原文

在JavaScript中,我习惯于能够调用稍后执行的函数,就像这样,

function foo() {
    alert('bar');
}

setTimeout(foo, 1000);

这不会阻止其他代码的执行。

我不知道如何在Python中实现类似的东西。我可以使用 sleep

import time
def foo():
    print('bar')

time.sleep(1)
foo()

,但这会阻止其他代码的执行。 (实际上,在我的例子中,阻塞 Python 本身并不是问题,但我无法对该方法进行单元测试。)

我知道线程是为不同步执行而设计的,但我想知道是否有更简单的、类似的东西setTimeoutsetInterval 存在。

In JavaScript I am used to being able to call functions to be executed at a later time, like this

function foo() {
    alert('bar');
}

setTimeout(foo, 1000);

This does not block the execution of other code.

I do not know how to achieve something similar in Python. I can use sleep

import time
def foo():
    print('bar')

time.sleep(1)
foo()

but this will block the execution of other code. (Actually in my case blocking Python would not be a problem in itself, but I would not be able to unit test the method.)

I know threads are designed for out-of-sync execution, but I was wondering whether something easier, similar to setTimeout or setInterval exists.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

晚雾 2024-10-26 19:16:03

要在延迟后执行函数或使用事件循环(无线程)在给定秒数内重复函数,您可以:

Tkinter

#!/usr/bin/env python
from Tkinter import Tk

def foo():
    print("timer went off!")

def countdown(n, bps, root):
    if n == 0:
        root.destroy() # exit mainloop
    else:
        print(n)
        root.after(1000 / bps, countdown, n - 1, bps, root)  # repeat the call

root = Tk()
root.withdraw() # don't show the GUI window
root.after(4000, foo) # call foo() in 4 seconds
root.after(0, countdown, 10, 2, root)  # show that we are alive
root.mainloop()
print("done")

输出

10
9
8
7
6
5
4
3
timer went off!
2
1
done

Gtk

#!/usr/bin/env python
from gi.repository import GObject, Gtk

def foo():
    print("timer went off!")

def countdown(n): # note: a closure could have been used here instead
    if n[0] == 0:
        Gtk.main_quit() # exit mainloop
    else:
        print(n[0])
        n[0] -= 1
        return True # repeat the call

GObject.timeout_add(4000, foo) # call foo() in 4 seconds
GObject.timeout_add(500, countdown, [10])
Gtk.main()
print("done")

输出

10
9
8
7
6
5
4
timer went off!
3
2
1
done

Twisted

#!/usr/bin/env python
from twisted.internet import reactor
from twisted.internet.task import LoopingCall

def foo():
    print("timer went off!")

def countdown(n):
    if n[0] == 0:
        reactor.stop() # exit mainloop
    else:
        print(n[0])
        n[0] -= 1

reactor.callLater(4, foo) # call foo() in 4 seconds
LoopingCall(countdown, [10]).start(.5)  # repeat the call in .5 seconds
reactor.run()
print("done")

输出

10
9
8
7
6
5
4
3
timer went off!
2
1
done

Asyncio

Python 3.4 引入了新的 用于异步 IO 的临时 API -- asyncio module

#!/usr/bin/env python3.4
import asyncio

def foo():
    print("timer went off!")

def countdown(n):
    if n[0] == 0:
        loop.stop() # end loop.run_forever()
    else:
        print(n[0])
        n[0] -= 1

def frange(start=0, stop=None, step=1):
    while stop is None or start < stop:
        yield start
        start += step #NOTE: loss of precision over time

def call_every(loop, seconds, func, *args, now=True):
    def repeat(now=True, times=frange(loop.time() + seconds, None, seconds)):
        if now:
            func(*args)
        loop.call_at(next(times), repeat)
    repeat(now=now)

loop = asyncio.get_event_loop()
loop.call_later(4, foo) # call foo() in 4 seconds
call_every(loop, 0.5, countdown, [10]) # repeat the call every .5 seconds
loop.run_forever()
loop.close()
print("done")

输出

10
9
8
7
6
5
4
3
timer went off!
2
1
done

注意:这些方法之间的界面和行为略有不同。

To execute a function after a delay or to repeat a function in given number of seconds using an event-loop (no threads), you could:

Tkinter

#!/usr/bin/env python
from Tkinter import Tk

def foo():
    print("timer went off!")

def countdown(n, bps, root):
    if n == 0:
        root.destroy() # exit mainloop
    else:
        print(n)
        root.after(1000 / bps, countdown, n - 1, bps, root)  # repeat the call

root = Tk()
root.withdraw() # don't show the GUI window
root.after(4000, foo) # call foo() in 4 seconds
root.after(0, countdown, 10, 2, root)  # show that we are alive
root.mainloop()
print("done")

Output

10
9
8
7
6
5
4
3
timer went off!
2
1
done

Gtk

#!/usr/bin/env python
from gi.repository import GObject, Gtk

def foo():
    print("timer went off!")

def countdown(n): # note: a closure could have been used here instead
    if n[0] == 0:
        Gtk.main_quit() # exit mainloop
    else:
        print(n[0])
        n[0] -= 1
        return True # repeat the call

GObject.timeout_add(4000, foo) # call foo() in 4 seconds
GObject.timeout_add(500, countdown, [10])
Gtk.main()
print("done")

Output

10
9
8
7
6
5
4
timer went off!
3
2
1
done

Twisted

#!/usr/bin/env python
from twisted.internet import reactor
from twisted.internet.task import LoopingCall

def foo():
    print("timer went off!")

def countdown(n):
    if n[0] == 0:
        reactor.stop() # exit mainloop
    else:
        print(n[0])
        n[0] -= 1

reactor.callLater(4, foo) # call foo() in 4 seconds
LoopingCall(countdown, [10]).start(.5)  # repeat the call in .5 seconds
reactor.run()
print("done")

Output

10
9
8
7
6
5
4
3
timer went off!
2
1
done

Asyncio

Python 3.4 introduces new provisional API for asynchronous IO -- asyncio module:

#!/usr/bin/env python3.4
import asyncio

def foo():
    print("timer went off!")

def countdown(n):
    if n[0] == 0:
        loop.stop() # end loop.run_forever()
    else:
        print(n[0])
        n[0] -= 1

def frange(start=0, stop=None, step=1):
    while stop is None or start < stop:
        yield start
        start += step #NOTE: loss of precision over time

def call_every(loop, seconds, func, *args, now=True):
    def repeat(now=True, times=frange(loop.time() + seconds, None, seconds)):
        if now:
            func(*args)
        loop.call_at(next(times), repeat)
    repeat(now=now)

loop = asyncio.get_event_loop()
loop.call_later(4, foo) # call foo() in 4 seconds
call_every(loop, 0.5, countdown, [10]) # repeat the call every .5 seconds
loop.run_forever()
loop.close()
print("done")

Output

10
9
8
7
6
5
4
3
timer went off!
2
1
done

Note: there is a slight difference in the interface and behavior between these approaches.

满意归宿 2024-10-26 19:16:03

您需要一个 Timer 对象线程模块。

from threading import Timer
from time import sleep

def foo():
    print "timer went off!"
t = Timer(4, foo)
t.start()
for i in range(11):
    print i
    sleep(.5)

如果您想重复,这里有一个简单的解决方案:不使用 Timer,只需使用 Thread 但向其传递一个工作原理如下的函数:

def call_delay(delay, repetitions, func, *args, **kwargs):             
    for i in range(repetitions):    
        sleep(delay)
        func(*args, *kwargs)

这​​不会无限执行循环,因为如果处理不当,可能会导致线程无法终止以及其他令人不快的行为。更复杂的方法可能使用基于事件的方法,就像这样

You want a Timer object from the threading module.

from threading import Timer
from time import sleep

def foo():
    print "timer went off!"
t = Timer(4, foo)
t.start()
for i in range(11):
    print i
    sleep(.5)

If you want to repeat, here's a simple solution: instead of using Timer, just use Thread but pass it a function that works somewhat like this:

def call_delay(delay, repetitions, func, *args, **kwargs):             
    for i in range(repetitions):    
        sleep(delay)
        func(*args, *kwargs)

This won't do infinite loops because that could result in a thread that won't die and other unpleasant behavior if not done right. A more sophisticated approach might use an Event-based approach, like this one.

丢了幸福的猪 2024-10-26 19:16:03

像 Javascript 的 setTimeout 这样的异步回调需要事件驱动的架构。

Python 的异步框架(例如流行的 twisted)具有 CallLater 可以满足您的要求,但这意味着采用事件驱动您的应用程序中的架构。

另一种选择是使用线程并在线程中休眠。 Python 提供了一个计时器来让等待部分很容易。然而,当你的线程唤醒并且你的函数执行时,它位于一个单独的线程中,并且必须以线程安全的方式执行它所做的任何事情。

Asynchronous callbacks like Javascript's setTimeout require an event-driven architecture.

Asynchronous frameworks for Python like the popular twisted have CallLater which does what you want, but it means adopting the event-driven architecture in your application.

Another alternative is to use threads and to sleep in a thread. Python providers a timer to make the waiting part easy. However, when your thread awakes and your function executes, it is in a separate thread and must do whatever it does in a thread-safe manner.

不再见 2024-10-26 19:16:03

抱歉,我无法发布超过 2 个链接,因此有关更多信息,请查看 PEP 380 以及最重要的 asyncio

除非您坚持使用线程或多处理,否则 asyncio 是此类问题的首选解决方案。它由 GvR 以“Tulip”的名称设计和实现。 GvR 在 PyCon 2013 上推出了这一活动,旨在成为唯一一项活动-loop 来规则(和标准化)所有事件循环(如twisted、gevent 等中的事件循环)并使它们彼此兼容。之前已经提到过 asyncio,但 asyncio 的真正威力是通过 yield from 来释放的。

# asyncio is in standard lib for latest python releases (since 3.3)
import asyncio

# there's only one event loop, let's fetch that
loop = asyncio.get_event_loop()

# this is a simple reminder that we're dealing with a coro
@asyncio.coroutine
def f():
    for x in range(10):
        print(x)
        # we return with a coroutine-object from the function, 
        # saving the state of the execution to return to this point later
        # in this case it's a special sleep
        yield from asyncio.sleep(3)

# one of a few ways to insert one-off function calls into the event loop
loop.call_later(10, print, "ding!")
# we insert the above function to run until the coro-object from f is exhausted and 
# raises a StopIteration (which happens when the function would return normally)
# this also stops the loop and cleans up - keep in mind, it's not DEAD but can be restarted
loop.run_until_complete(f())
# this closes the loop - now it's DEAD
loop.close()

===============

>>> 
0
1
2
3
ding!
4
5
6
7
8
9
>>>

Sorry, I can't post more than 2 links, so for more information please check PEP 380 and most importantly the documentation of asyncio.

asyncio is the preferred solution to this kind of question unless you insist on threading or multiprocessing. It is designed and implemented by GvR under the name "Tulip". It has been introduced by GvR on PyCon 2013 with the intention to be the one event-loop to rule (and standardize) all event-loops (like the ones in twisted, gevent, etc.) and make them compatible with each other. asyncio has been mentioned before, but the true power of asyncio is unleashed with yield from.

# asyncio is in standard lib for latest python releases (since 3.3)
import asyncio

# there's only one event loop, let's fetch that
loop = asyncio.get_event_loop()

# this is a simple reminder that we're dealing with a coro
@asyncio.coroutine
def f():
    for x in range(10):
        print(x)
        # we return with a coroutine-object from the function, 
        # saving the state of the execution to return to this point later
        # in this case it's a special sleep
        yield from asyncio.sleep(3)

# one of a few ways to insert one-off function calls into the event loop
loop.call_later(10, print, "ding!")
# we insert the above function to run until the coro-object from f is exhausted and 
# raises a StopIteration (which happens when the function would return normally)
# this also stops the loop and cleans up - keep in mind, it's not DEAD but can be restarted
loop.run_until_complete(f())
# this closes the loop - now it's DEAD
loop.close()

================

>>> 
0
1
2
3
ding!
4
5
6
7
8
9
>>>
那一片橙海, 2024-10-26 19:16:03

JavaScript 可以做到这一点,因为它在事件循环中运行事物。这可以在 Python 中通过使用 Twisted 等事件循环或通过 GLib 或 Qt 等工具包来完成。

JavaScript can do this because it runs things in an event loop. This can be done in Python through use of an event loop such as Twisted, or via a toolkit such as GLib or Qt.

我喜欢麦丽素 2024-10-26 19:16:03

问题是你的普通 python 脚本不能在框架中运行。该脚本被调用并控制主循环。使用 JavaScript,页面上运行的所有脚本都在框架中运行,并且框架在超时后调用您的方法。

我自己没有使用过 pyQt(仅使用过 C++ Qt),但是您可以使用 startTimer() 在任何 QObject 上设置计时器。当计时器到期时,将调用您的方法的回调。您还可以使用 QTimer 并将超时信号连接到任意插槽。这是可能的,因为 Qt 运行一个事件循环,可以在稍后阶段调用您的方法。

The problem is that your normal python script doesn't run in a framework. The script gets called and is in control of the main loop. With JavaScript, all the scripts that runs on your page runs in a framework and it is the framework that invokes your method when the timeout elapses.

I haven't used pyQt myself (only C++ Qt), but you can set a timer on any QObject using startTimer(). When the timer elapse, a callback on your method is invoked. You can also use QTimer and connect the timeout signal to an arbitrary slot. This is possible because Qt runs an event loop that can call your method at a later stage.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文