2010-03-12 9 views
31

कोई भी कमजोर संदर्भों के उपयोग की व्याख्या कर सकता है?पायथन में कमजोर संदर्भों का उपयोग कब करें?

documentation यह स्पष्ट रूप से समझा नहींता है, यह केवल इतना कहता है कि जीसी किसी भी समय कमजोर संदर्भ के माध्यम से ऑब्जेक्ट को नष्ट कर सकता है। तो किसी ऑब्जेक्ट को रखने का क्या मतलब है जो किसी भी समय गायब हो सकता है? अगर गायब होने के बाद मुझे इसे इस्तेमाल करने की ज़रूरत है तो क्या होगा?

क्या आप उन्हें कुछ अच्छे उदाहरणों के साथ समझा सकते हैं?

धन्यवाद

उत्तर

24

कमजोर संदर्भ के लिए विशिष्ट प्रयोग करता है, तो एक बी के लिए एक संदर्भ है और बी एक उचित चक्र का पता लगाने के कचरा कलेक्टर के बिना ए के लिए एक संदर्भ है, उन दो वस्तुओं GC'd भी कभी नहीं मिलेगा है अगर "बाहर" से कोई संदर्भ नहीं है। हालांकि अगर संदर्भों में से एक "कमजोर" है, तो वस्तुओं को ठीक से जीसी'd मिल जाएगा।

हालांकि, अजगर एक चक्र-पता लगाने कचरा कलेक्टर राशि (2.0 के बाद से!), ताकि गणना नहीं करता :)

एक अन्य प्रयोग कमजोर संदर्भ के लिए कैश के लिए है नहीं करता है। यह weakref दस्तावेज में उल्लेख किया गया है:

एक प्राथमिक उपयोग कमजोर संदर्भ के लिए कैश या मैपिंग बड़ी वस्तुओं पकड़े, जहां यह वांछित है, क्योंकि यह एक कैश या मानचित्रण में प्रकट होता है कि एक बड़ी वस्तु नहीं केवल जिंदा रखा जा लागू करने के लिए है।

यदि जीसी उन वस्तुओं में से किसी एक को नष्ट करने का फैसला करता है, और आपको इसकी आवश्यकता है, तो आप डेटा को फिर से गणना/रीफ्रैच कर सकते हैं।

+1

हम्म ... मुझे एहसास नहीं हुआ कि आपने कैश का उल्लेख किया है ... मुझे लगता है कि मुझे अपना जवाब हटा देना चाहिए और अगली बार अधिक ध्यान से पढ़ना चाहिए :-)। – Tom

+1

ठीक है, यह केवल कैशों का एक लाइनर उल्लेख था। मैंने जवाब का विस्तार किया, * * के बाद * आपने अपना पोस्ट किया था :) –

+0

ठीक है, कम से कम मैं पागल नहीं हूं :-)। यह नहीं कहता कि आपकी पोस्ट संपादित की गई थी। किसी भी तरह से .. मुझे इसे फिर से पढ़ने के बाद आपका जवाब पसंद आया ... और मैं प्रतिष्ठा-भूख के रूप में नहीं आना चाहता :-)। – Tom

25

घटनाक्रम कमजोर संदर्भों के लिए एक आम परिदृश्य हैं। 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) तुरंत, पूर्ण जीसी चक्र की आवश्यकता के बिना।

+0

मैं Listener._callbacks सेट के पीछे तर्क को समझ नहीं पा रहा हूं। क्या कॉलबैक को श्रोता ऑब्जेक्ट के पूरे जीवनकाल में जीवित नहीं रखा जाएगा? यह उस वस्तु के लिए एक विधि है, आखिरकार। – Xion

+0

बाहर निकलता है यह नहीं है। 'Listener.callback' एक फ़ंक्शन है, लेकिन' l.callback' कॉल 'yourmethod।__get __ (एल, श्रोता) 'जो एक' instancemethod' कॉल करने योग्य देता है जो इसके 'स्वयं' को याद करता है। जब विधि को देखा जाता है तो यह हमेशा पुनर्निर्मित होता है। [यहां अच्छा पढ़ना] (http://users.rcn.com/python/download/Descriptor.htm#functions-and-methods) – Kos

+0

शायद आप इस क्षणिक 'instancemetiond' ऑब्जेक्ट के संदर्भों को संदर्भित नहीं करना चाहते हैं, लेकिन 'श्रोता' ऑब्जेक्ट के लिए (जावा-स्टाइल)। – Xion

1
- Weak references is an important concept in python, which is missing 
    in languages likes Java(java 1.5). 
- In Observer design pattern, generally Observable Object must maintain 
    weak references to the Observer object. 

    eg. A emits an event done() and B registers with A that, it want to 
    listen to event done(). Thus, whenever done() is emitted, B is 
    notified. But If B isn't required in application, then A must not 
    become an hinderance in the garbage collection in A(since A hold the 
    reference to B). Thus, if A has hold weak reference to B, and when 
    all the references to A are away, then B will be garbage collected. 
- It's also very useful in implementing caches.