gra*_*irt 6 python garbage-collection exception-handling pyside
tl; dr - 在PySide应用程序中,即使已删除所有其他引用,其方法抛出异常的对象仍将保持活动状态.为什么?什么,如果有的话,应该做些什么呢?
在使用带有PySide GUI的Model-View-Presenter架构构建简单CRUDish应用程序的过程中,我发现了一些奇怪的行为.就我而言:
weakref.ref)以避免循环pypubsub消息传递库进行通信,但这也仅存储对侦听器的弱引用,并且不是下面MCVE中的一个因素.)但是,方法抛出异常的Presenter不会按预期删除.应用程序继续运行,因为PySide使用一些魔法来捕获异常.有问题的演示者继续接收并响应与其绑定的任何View事件.但是当删除视图时,抛出异常的Presenter将保持活动状态,直到整个应用程序关闭.MCVE(可读性链接):
import logging
import sys
import weakref
from PySide import QtGui
class InnerPresenter:
def __init__(self, view):
self._view = weakref.ref(view)
self.logger = logging.getLogger('InnerPresenter')
self.logger.debug('Initializing InnerPresenter (id:%s)' % id(self))
def __del__(self):
self.logger.debug('Deleting InnerPresenter (id:%s)' % id(self))
@property
def view(self):
return self._view()
def on_alert(self):
self.view.show_alert()
def on_raise_exception(self):
raise Exception('From InnerPresenter (id:%s)' % id(self))
class OuterView(QtGui.QMainWindow):
def __init__(self, *args, **kwargs):
super(OuterView, self).__init__(*args, **kwargs)
self.logger = logging.getLogger('OuterView')
# Menus
menu_bar = self.menuBar()
test_menu = menu_bar.addMenu('&Test')
self.open_action = QtGui.QAction('&Open inner', self, triggered=self.on_open, enabled=True)
test_menu.addAction(self.open_action)
self.close_action = QtGui.QAction('&Close inner', self, triggered=self.on_close, enabled=False)
test_menu.addAction(self.close_action)
def closeEvent(self, event, *args, **kwargs):
self.logger.debug('Exiting application')
event.accept()
def on_open(self):
self.setCentralWidget(InnerView(self))
self.open_action.setEnabled(False)
self.close_action.setEnabled(True)
def on_close(self):
self.setCentralWidget(None)
self.open_action.setEnabled(True)
self.close_action.setEnabled(False)
class InnerView(QtGui.QWidget):
def __init__(self, *args, **kwargs):
super(InnerView, self).__init__(*args, **kwargs)
self.logger = logging.getLogger('InnerView')
self.logger.debug('Initializing InnerView (id:%s)' % id(self))
self.presenter = InnerPresenter(self)
# Layout
layout = QtGui.QHBoxLayout(self)
alert_button = QtGui.QPushButton('Alert!', self, clicked=self.presenter.on_alert)
layout.addWidget(alert_button)
raise_button = QtGui.QPushButton('Raise exception!', self, clicked=self.presenter.on_raise_exception)
layout.addWidget(raise_button)
self.setLayout(layout)
def __del__(self):
super(InnerView, self).__del__()
self.logger.debug('Deleting InnerView (id:%s)' % id(self))
def show_alert(self):
QtGui.QMessageBox(text='Here is an alert').exec_()
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
app = QtGui.QApplication(sys.argv)
view = OuterView()
view.show()
sys.exit(app.exec_())
Run Code Online (Sandbox Code Playgroud)
打开并关闭内部视图,您将看到视图和演示者都按预期删除.打开内部视图,单击按钮以在演示者上触发异常,然后关闭内部视图.视图将被删除,但演示者将不会在应用程序退出之前删除.
为什么?据推测,代表PySide捕获所有异常的是存储对抛出它的对象的引用.为什么需要这样做?
我应该如何处理(除了编写永远不会导致例外的代码)?我有足够的理智不依赖于__del__资源管理.我知道我没有权利期待一个被抓住但没有真正处理过的异常之后的任何事情理想地发生,但这只会让我感到不必要的丑陋.一般来说我该如何处理?
问题是sys.last_tracback和sys.last_value。
当以交互方式引发回溯时,这似乎是模拟的,最后一个异常及其回溯分别存储在sys.last_value和中sys.last_traceback。
正在做
del sys.last_value
del sys.last_traceback
# for consistency, see
# https://docs.python.org/3/library/sys.html#sys.last_type
del sys.last_type
Run Code Online (Sandbox Code Playgroud)
将释放内存。
值得注意的是,最多可以缓存一个异常和回溯对。这意味着,因为您神智正常并且不依赖del,所以不会造成大量损害。
但如果你想回收内存,只需删除这些值即可。
| 归档时间: |
|
| 查看次数: |
437 次 |
| 最近记录: |