在JavaScript中,我习惯于能够调用稍后要执行的函数,比如
function foo() {
alert('bar');
}
setTimeout(foo, 1000);
这不会阻止其他代码的执行。
我不知道如何在Python中实现类似的功能。我可以使用睡眠
import time
def foo():
print('bar')
time.sleep(1)
foo()
但这会阻止其他代码的执行。 (实际上在我的情况下,阻止Python本身并不是问题,但我无法对该方法进行单元测试。)
我知道线程是为不同步执行而设计的,但我想知道是否存在更容易的事情,类似于setTimeout
或setInterval
。
答案 0 :(得分:14)
要在延迟后执行函数或使用事件循环(无线程)在给定的秒数内重复某个函数,您可以:
#!/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
#!/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
#!/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
Python 3.4为异步IO引入了新的provisional 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
注意:这些方法之间的界面和行为略有不同。
答案 1 :(得分:8)
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)
这不会做无限循环,因为这可能导致一个不会死的线程和其他不愉快的行为,如果做得不对。更复杂的方法可能使用基于Event
的方法like this one。
答案 2 :(得分:4)
Javascript的setTimeout
之类的异步回调需要一个事件驱动的架构。
Python的异步框架(如流行的twisted具有CallLater
可以满足您的需求,但这意味着在您的应用程序中采用事件驱动的体系结构。
另一种选择是使用线程并在线程中休眠。 Python提供程序timer使等待部分变得容易。但是,当你的线程唤醒并且你的函数执行时,它在一个单独的线程中,并且必须以线程安全的方式做它做的任何事情。
答案 3 :(得分:2)
抱歉,我发不到2个以上的链接,所以如需了解更多信息,请查看 PEP 380 ,最重要的是 asyncio
asyncio是此类问题的首选解决方案,除非您坚持进行线程或多处理。它由GvR以“Tulip”的名义设计和实施。它已由GvR在PyCon 2013上引入,旨在成为一个事件循环来规则(和标准化)所有事件循环(如twisted,gevent等中的那些)并使它们与每个事件循环兼容其他。之前已经提到过asyncio,但asyncio的真正威力来自来自的收益。
# 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
>>>
答案 4 :(得分:1)
JavaScript可以做到这一点,因为它在事件循环中运行。这可以通过使用事件循环(如Twisted)或通过工具包(如GLib或Qt)在Python中完成。
答案 5 :(得分:0)
问题是你的普通python脚本不能在框架中运行。脚本被调用并控制主循环。使用JavaScript,页面上运行的所有脚本都在框架中运行,并且它是在超时过去时调用方法的框架。
我自己没有使用过pyQt(只有C ++ Qt),但你可以使用startTimer()在任何QObject上设置一个计时器。当计时器过去时,将调用方法的回调。您还可以使用QTimer并将超时信号连接到任意插槽。这是可能的,因为Qt运行一个事件循环,可以在稍后阶段调用您的方法。