tl; dr - एक पायसाइड अनुप्रयोग में, एक ऑब्जेक्ट जिसकी विधि अपवाद फेंकता है तब भी जीवित रहेगा जब अन्य सभी संदर्भ हटा दिए जाएंगे। क्यूं कर? और क्या, अगर कुछ भी है, तो क्या इस बारे में कोई करना चाहिए?इस वस्तु के जीवनकाल को विस्तारित करने के लिए पायसाइड का अपवाद हैंडलिंग क्यों है?
एक पाइसाइड जीयूआई के साथ मॉडल-व्यू-प्रेजेंटर आर्किटेक्चर का उपयोग करके एक सरल CRUDish ऐप बनाने के दौरान, मैंने कुछ उत्सुक व्यवहार की खोज की। मेरे मामले में:
- इंटरफेस उन्हें कई दृश्यों में बांटा गया है - यानी, प्रत्येक टैब डेटा का एक अलग पहलू को प्रदर्शित पेज देखें
- दृश्य पहले instantiated कर रहे हैं, और उनके प्रारंभ में की अपने ही वर्ग हो सकता है, वे अपने स्वयं के प्रस्तुतकर्ता का दृष्टांत,
- एक प्रस्तुतकर्ता देखें यह ड्राइव के लिए एक संदर्भ प्राप्त करता है यह करने के लिए एक सामान्य संदर्भ में रखते हुए, लेकिन एक कमजोर संदर्भ (
weakref.ref
) के रूप में भंडार इस घेरा - एक प्रस्तुतकर्ता के लिए कोई अन्य मजबूत संदर्भ मौजूद से बचने के लिए । (प्रस्तुतकर्ता अप्रत्यक्ष रूप से
pypubsub
मैसेजिंग लाइब्रेरी के साथ संवाद कर सकते हैं, लेकिन यह श्रोताओं के लिए केवल कमजोर संदर्भ भी संग्रहीत करता है, और नीचे एमसीवीई में एक कारक नहीं है।) - इस प्रकार, सामान्य ऑपरेशन में, जब कोई दृश्य हटा दिया जाता है (उदाहरण के लिए, कब एक टैब) बंद है, इसकी प्रस्तुतकर्ता बाद में नष्ट कर दिया के रूप में अपनी संदर्भ गिनती 0
हालांकि, एक प्रस्तुतकर्ता जो की एक विधि एक अपवाद फेंका गया है की उम्मीद के रूप में नष्ट नहीं प्राप्त करता है हो जाता है। एप्लिकेशन काम करना जारी रखता है, क्योंकि अपवादों को पकड़ने के लिए पायसाइड some magic को नियोजित करता है। प्रश्न में प्रस्तुतकर्ता इसे किसी भी दृश्य घटनाओं को प्राप्त करने और प्रतिक्रिया देने के लिए जारी है। लेकिन जब दृश्य हटा दिया जाता है, तब तक अपवाद-फेंकने वाला प्रेजेंटर तब तक जीवित रहता है जब तक कि पूरा एप्लिकेशन बंद न हो जाए। एक 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_())
ओपन और भीतरी दृश्य बंद, और आप देखेंगे दोनों दृश्य और प्रस्तोता के रूप में उम्मीद नष्ट हो जाती हैं। आंतरिक दृश्य खोलें, प्रस्तुतकर्ता पर अपवाद ट्रिगर करने के लिए बटन क्लिक करें, फिर आंतरिक दृश्य को बंद करें। दृश्य हटा दिया जाएगा, लेकिन प्रस्तुतकर्ता तब तक नहीं होगा जब तक एप्लिकेशन बाहर निकल न जाए।
क्यों? संभवतः जो कुछ भी है वह पाइसाइड की ओर से सभी अपवादों को पकड़ता है, उस वस्तु को संदर्भित करता है जो इसे फेंक देता है। ऐसा करने की आवश्यकता क्यों होगी?
कैसे मुझे आगे बढ़ना चाहिए (कोड लिखने के अलावा जो कभी भी अपवाद का कारण नहीं बनता)? मुझे संसाधन प्रबंधन के लिए __del__
पर भरोसा नहीं करना पर्याप्त ज्ञान है। मुझे लगता है कि मुझे आदर्श रूप से जाने के लिए पकड़े गए लेकिन वास्तव में-नियंत्रित अपवाद के बाद कुछ भी उम्मीद करने का कोई अधिकार नहीं है, लेकिन यह मुझे अनावश्यक बदसूरत के रूप में मारता है। मैं इसे सामान्य रूप से कैसे पहुंचा सकता हूं?
इस तरह के एक संपूर्ण प्रश्न लिखने के लिए कुडोस। :) – Veedrac
मैं वास्तव में कुछ भी नहीं देख रहा हूं जब मैं खोलता हूं और फिर आंतरिक दृश्य को बंद करता हूं, चाहे मैं 'अपवाद उठाएं' बटन पर क्लिक करता हूं या नहीं। – dano
@ डैनो: अजीब। मेरे सिस्टम पर, यह विंडोज़ में पाइथन 2 और 3 दोनों के साथ काम करता है, लेकिन उबंटू (पायथन 3) के तहत मैं वही व्यवहार देख रहा हूं जो आप वर्णन करते हैं - न तो विचार और न ही प्रस्तुतकर्ता हटा दिए जाते हैं, या कम से कम वे हैं इसे लॉगिंग नहीं कर रहा है। इनरव्यू में स्पष्ट रूप से नष्ट() को नष्ट करने से चाल नहीं होती है। – grayshirt