gpt4 book ai didi

python - 为什么 PySide 的异常处理会延长这个对象的生命周期?

转载 作者:太空狗 更新时间:2023-10-30 00:10:43 30 4
gpt4 key购买 nike

tl;dr -- 在 PySide 应用程序中,即使所有其他引用都已删除,其方法抛出异常的对象仍将保持事件状态。为什么?如果有的话,人们应该怎么做?

在使用带有 PySide GUI 的模型- View -呈现器架构构建简单的 CRUDish 应用程序的过程中,我发现了一些奇怪的行为。就我而言:

  • 界面分为多个 View ——即,每个显示不同方面数据的标签页可能是它自己的 View 类
  • 首先实例化 View ,在初始化时,它们实例化自己的 Presenter,保持对它的正常引用
  • Presenter 收到对其驱动的 View 的引用,但将其存储为弱引用 (weakref.ref) 以避免循环
  • 不存在对 Presenter 的其他强引用。 (演示者可以与 pypubsub 消息传递库间接通信,但这也仅存储对听众的弱引用,并且不是下面 MCVE 的一个因素。)
  • 因此,在正常操作中,当一个 View 被删除时(例如,当一个选项卡关闭时),它的 Presenter 随后被删除,因为它的引用计数变为 0

但是,方法抛出异常的 Presenter 并没有像预期的那样被删除。该应用程序继续运行,因为 PySide 使用了 some magic捕捉异常。有问题的 Presenter 继续接收和响应绑定(bind)到它的任何 View 事件。但是当 View 被删除时,抛出异常的 Presenter 仍然存在,直到整个应用程序关闭。 MCVE ( link for readability ):

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_())

打开和关闭内部 View ,您会看到 View 和演示者都按预期删除了。打开内 View ,点击按钮在presenter上触发异常,然后关闭内 View 。 View 将被删除,但在应用程序退出之前演示者不会。

为什么? 据推测,代表 PySide 捕获所有异常的任何东西都存储了对抛出它的对象的引用。为什么需要这样做?

我应该如何继续(当然,除了编写永远不会导致异常的代码之外)?我有足够的理由不依赖 __del__ 进行资源管理。我知道我无权期望在捕获但未真正处理的异常之后发生任何事情,但这让我觉得不必要地丑陋。一般来说,我应该如何处理这个问题?

最佳答案

问题是 sys.last_tracbacksys.last_value

当以交互方式引发回溯时,这似乎是被模拟的,最后一个异常及其回溯分别存储在 sys.last_valuesys.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

将释放内存。

值得注意的是最多一个异常和回溯对可以被缓存。这意味着,因为您是理智的并且不依赖 del,所以不会造成大量损失。

但是如果你想回收内存,只需删除那些值即可。

关于python - 为什么 PySide 的异常处理会延长这个对象的生命周期?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26086365/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com