घटनाक्रम कमजोर संदर्भों के लिए एक आम परिदृश्य हैं। Emitter और रिसीवर:
समस्या
वस्तुओं की एक जोड़ी पर विचार करें। रिसीवर उत्सर्जक से कम जीवनकाल है।
आप इस तरह एक कार्यान्वयन की कोशिश कर सकते:
class Emitter(object):
def __init__(self):
self.listeners = set()
def emit(self):
for listener in self.listeners:
# Notify
listener('hello')
class Receiver(object):
def __init__(self, emitter):
emitter.listeners.add(self.callback)
def callback(self, msg):
print 'Message received:', msg
e = Emitter()
l = Receiver(e)
e.emit() # Message received: hello
हालांकि, इस मामले में, Emitter एक बाध्य विधि callback
कि रिसीवर के लिए एक संदर्भ रहता है के लिए एक संदर्भ रहता है। तो एमिटर रिसीवर को जीवित रखता है:
# ...continued...
del l
e.emit() # Message received: hello
यह कभी-कभी परेशानी होती है। कल्पना करें कि Emitter
कुछ डेटा मॉडल का एक हिस्सा है जो इंगित करता है कि डेटा परिवर्तन और Receiver
एक संवाद विंडो द्वारा बनाया गया था जो कुछ UI नियंत्रणों को अद्यतन करने के लिए उन परिवर्तनों को सुनता है।
आवेदन के जीवनकाल के माध्यम से, कई संवाद उत्पन्न किए जा सकते हैं और हम नहीं चाहते हैं कि खिड़की बंद होने के बाद लंबे समय तक उनके रिसीवर एमिटर के अंदर पंजीकृत हों। यह एक स्मृति रिसाव होगा।
कॉलबैक को मैन्युअल रूप से निकालना एक विकल्प है (जैसा कि परेशानी है), कमजोर संदर्भों का उपयोग करके दूसरा है।
समाधान
एक अच्छा वर्ग WeakSet
है कि एक सामान्य सेट की तरह दिखता है, लेकिन उसके सदस्यों कमजोर संदर्भों का उपयोग संग्रहीत करता है और अब उनके संग्रहीत करता है, जब वे मुक्त कर दिया जाता है नहीं है।
बढ़िया! के लिए इसका इस्तेमाल करते हैं:
def __init__(self):
self.listeners = weakref.WeakSet()
और फिर से चलाने:
e = Emitter()
l = Receiver(e)
e.emit()
del l
e.emit()
ओह, कुछ भी नहीं सब पर होता है! ऐसा इसलिए है क्योंकि बाध्य विधि (एक विशिष्ट रिसीवर का callback
) अब अनाथ है - न तो एमिटर और न ही रिसीवर इसके लिए एक मजबूत संदर्भ रखता है। इसलिए यह तुरंत कचरा इकट्ठा किया गया है।
class Receiver(object):
def __init__(self, emitter):
# Create the bound method object
cb = self.callback
# Register it
emitter.listeners.add(cb)
# But also create an own strong reference to keep it alive
self._callbacks = set([cb])
अब हम अपेक्षित व्यवहार का निरीक्षण कर सकते हैं::
के रिसीवर (नहीं Emitter इस समय) इस कॉलबैक के लिए एक मजबूत संदर्भ रख करते हैं Emitter केवल कॉलबैक जब तक रिसीवर जीवन के रूप में रहता है ।
e = Emitter()
l = Receiver(e)
assert len(e.listeners) == 1
del l
import gc; gc.collect()
assert len(e.listeners) == 0
हुड के अंतर्गत
नोट है कि मैं एक gc.collect()
यहाँ डाल करने के लिए मिला है यह सुनिश्चित करें कि रिसीवर वास्तव में तुरंत साफ किया जाता है बनाने के लिए। इसकी आवश्यकता यहां है क्योंकि अब मजबूत संदर्भों का एक चक्र है: बाध्य विधि रिसीवर को संदर्भित करती है और इसके विपरीत।
यह बहुत बुरा नहीं है; इसका मतलब यह है कि अगले कचरा कलेक्टर चलाने तक रिसीवर का सफाई स्थगित कर दिया जाएगा। सरल संदर्भ गिनती तंत्र द्वारा चक्रीय संदर्भों को साफ नहीं किया जा सकता है।
यदि आप वास्तव में चाहते हैं, तो आप एक कस्टम फ़ंक्शन ऑब्जेक्ट के साथ बाध्य विधि को प्रतिस्थापित करके मजबूत संदर्भ चक्र को हटा सकते हैं जो इसके self
को कमजोर संदर्भ के रूप में भी रखेगा।
def weak_bind(instancemethod):
weakref_self = weakref.ref(instancemethod.im_self)
func = instancemethod.im_func
def callback(*args, **kwargs):
self = weakref_self()
bound = func.__get__(self)
return bound(*args, **kwargs)
return callback
class Receiver(object):
def __init__(self, emitter):
cb = weak_bind(self.callback)
# Register it
emitter.listeners.add(cb)
# But also create an own strong reference to keep it alive
self._callbacks = set([cb])
अब मजबूत संदर्भ का कोई चक्र है, इसलिए जब Receiver
मुक्त हो जाता है, कॉलबैक फ़ंक्शन भी मुक्त हो जाएगा (और Emitter के से हटा:
def __init__(self, emitter):
# Create the bound method object
weakself = weakref.ref(self)
def cb(msg):
self = weakself()
self.callback(msg)
# Register it
emitter.listeners.add(cb)
# But also create an own strong reference to keep it alive
self._callbacks = set([cb])
के एक सहायक समारोह में उस तर्क रखते हैं WeakSet
) तुरंत, पूर्ण जीसी चक्र की आवश्यकता के बिना।
हम्म ... मुझे एहसास नहीं हुआ कि आपने कैश का उल्लेख किया है ... मुझे लगता है कि मुझे अपना जवाब हटा देना चाहिए और अगली बार अधिक ध्यान से पढ़ना चाहिए :-)। – Tom
ठीक है, यह केवल कैशों का एक लाइनर उल्लेख था। मैंने जवाब का विस्तार किया, * * के बाद * आपने अपना पोस्ट किया था :) –
ठीक है, कम से कम मैं पागल नहीं हूं :-)। यह नहीं कहता कि आपकी पोस्ट संपादित की गई थी। किसी भी तरह से .. मुझे इसे फिर से पढ़ने के बाद आपका जवाब पसंद आया ... और मैं प्रतिष्ठा-भूख के रूप में नहीं आना चाहता :-)। – Tom