我正在用Python开发一个项目,并且获得了很多以下样式的函数:
from PyQt5.QtCore import *
import functools
...
def myfunc(self, callback, callbackArg):
'''
This function hasn't finished its job when it hits the
return statement. Provide a callback function and a
callback argument, such that this function will call:
callback(callbackArg)
when it has finally finished its job.
'''
def start():
myIterator = iter(self.myList)
QTimer.singleShot(10, functools.partial(process_next, myIterator))
return
def process_next(itemIterator):
try:
item = next(itemIterator)
except StopIteration:
finish()
# Do something
QTimer.singleShot(10, functools.partial(process_next, myIterator))
return
def finish():
callback(callbackArg)
return
start()
return
此函数运行时间不会很长,因此不会冻结GUI和其他进程。取而代之的是,该函数几乎立即退出,并在随后的短暂时间内完成其工作。最后,当工作完成时,它将调用提供的回调。
但是有一个缺点。这种方法给堆栈带来了很大的压力(我认为),因为您获得了以下链:
start() -> process_next() -> process_next() -> process_next() -> ... -> finish()
尽管我对此不太确定。函数process_next()
调用QTimer.singleShot(...)
,然后退出。那么也许堆栈上的长链根本没有发生?
您知道该方法是否会引起堆栈溢出的风险?我还没有发现其他潜在风险吗?
编辑
谢谢@ygramoel的澄清。因此,实际上,以下行:
QTimer.singleShot(10, functools.partial(process_next, myIterator))
调用函数process_next(myIterator)
,而无需压入另一个堆栈框架。因此,我不会冒长列表导致堆栈溢出的风险。太好了!
我只是想知道:有时我不希望QTimer.singleShot()
函数所提供的几毫秒的延迟。要立即调用下一个函数(而不用推入另一个堆栈框架),我可以这样做:
QTimer.singleShot(0, functools.partial(process_next, myIterator))
但是,每个QTimer.singleShot()
调用都会触发pyqtSignal()
。在短时间内触发太多线程会使主线程达到极限(请记住:主python线程监听传入的pyqt信号)。主线程一个接一个地处理事件队列条目,调用相应的插槽。因此,如果软件将太多事件触发到该队列中,则GUI可能会变得无响应。
是否有另一种优雅的方式来调用process_next(myIterator)
,而不会出现以下任何问题:
答案 0 :(得分:1)
您未包含item.foobar
和self.foo
的代码。假设这些调用不会引起深度递归,则在执行此代码期间的最大堆栈深度不会随列表的长度而增加。
functools.partial
不会立即调用process_next
函数。它只会创建一个类似函数的对象,以后可以调用它。参见https://docs.python.org/3/library/functools.html
QTimer.singleShot
也不立即调用process_next
函数。在返回对functools.partial
的当前调用之后,它计划将从process_next
返回的类似函数的对象稍后执行。
您可以通过在print("enter")
的开头放置一个process_next
语句,并在返回之前放置一个print("leave")
语句来轻松地自己进行验证。
在递归的情况下,您将看到:
enter
enter
enter
...
leave
leave
leave
,堆栈将溢出很长的列表。
如果没有递归,您将看到:
enter
leave
enter
leave
enter
leave
...
,最大堆栈深度与列表的长度无关。