2014-09-28 11 views
6

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__ पर भरोसा नहीं करना पर्याप्त ज्ञान है। मुझे लगता है कि मुझे आदर्श रूप से जाने के लिए पकड़े गए लेकिन वास्तव में-नियंत्रित अपवाद के बाद कुछ भी उम्मीद करने का कोई अधिकार नहीं है, लेकिन यह मुझे अनावश्यक बदसूरत के रूप में मारता है। मैं इसे सामान्य रूप से कैसे पहुंचा सकता हूं?

+0

इस तरह के एक संपूर्ण प्रश्न लिखने के लिए कुडोस। :) – Veedrac

+0

मैं वास्तव में कुछ भी नहीं देख रहा हूं जब मैं खोलता हूं और फिर आंतरिक दृश्य को बंद करता हूं, चाहे मैं 'अपवाद उठाएं' बटन पर क्लिक करता हूं या नहीं। – dano

+0

@ डैनो: अजीब। मेरे सिस्टम पर, यह विंडोज़ में पाइथन 2 और 3 दोनों के साथ काम करता है, लेकिन उबंटू (पायथन 3) के तहत मैं वही व्यवहार देख रहा हूं जो आप वर्णन करते हैं - न तो विचार और न ही प्रस्तुतकर्ता हटा दिए जाते हैं, या कम से कम वे हैं इसे लॉगिंग नहीं कर रहा है। इनरव्यू में स्पष्ट रूप से नष्ट() को नष्ट करने से चाल नहीं होती है। – grayshirt

उत्तर

3

समस्या 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 

कर स्मृति मुक्त होगा।

यह ध्यान देने योग्य है कि अधिकांश एक अपवाद और ट्रेसबैक जोड़ी कैश हो सकती है। इसका मतलब यह है कि, क्योंकि आप सचेत हैं और del पर भरोसा नहीं करते हैं, वहां बड़ी संख्या में नुकसान नहीं किया जा सकता है।

लेकिन यदि आप स्मृति को पुनः प्राप्त करना चाहते हैं, तो बस उन मानों को हटा दें।

+0

प्रबुद्ध - मुझे यह मानना ​​गलत था कि यह पाइसाइड की गलती थी; यह सिर्फ पायथन है। अभी भी मेरे ऐप के लिए एक मुद्दा है। एक अपवाद फेंकने के बाद लंबी कहानी छोटी है, इसके प्रस्तुतकर्ता को बंद और हटा दिए जाने के बाद एक प्रेजेंटर लाइव रहना जारी रख सकता है। प्राकृतिक मौत के बिना, इसे 'पिपब्सब' संदेश प्राप्त करना जारी रहता है जो इसे अपने दृश्य को अपडेट करने के लिए कहता है, जो अब मौजूद नहीं है। सरल समाधान: अगर अद्यतन इसे अद्यतन करने से पहले मौजूद है तो परीक्षण करें। आगे के अपवादों से बचें, लेकिन प्रेजेंटर के लिए यह देखने के लिए मेरे लिए सिर्फ सकल है। मैं ऊपर वर्णित मानों को हटाने के लिए कोई अच्छी जगह नहीं देख सकता हूं। कोई सुझाव? – grayshirt

+0

मुझे सच में यकीन नहीं है। आम तौर पर जिन चीजों का कोई संदर्भ नहीं है उन्हें कुछ भी नहीं करना चाहिए, लेकिन मुझे लगता है कि यह आपके मामले में काफी कठिन है। लेकिन आपको विनाश पर किसी भी आदेश की आवश्यकता नहीं है; पाइथन '__del__' * कभी * कॉल नहीं होने की गारंटी नहीं देता है, न ही ऑब्जेक्ट * कभी * हटा दिया जाता है। – Veedrac

+0

उस पर, जैसा कि हमने उपरोक्त टिप्पणियों में देखा है, उबंटू पर जो भी कारण है, पाइसाइड व्यू ऑब्जेक्ट्स (और उनके प्रस्तुतकर्ता बदले में) को नष्ट नहीं कर रहा है, अपवाद या नहीं। ऐसा लगता है कि विकल्पों को शांति बनाने के लिए या सी ++ सीखना है। आपकी सहायता के लिए एक बार फिर से धन्यवाद। – grayshirt

संबंधित मुद्दे