Kiedy używać słabych odniesień w Pythonie?
Czy ktoś może wyjaśnić użycie słabych odniesień?
Dokumentacja nie wyjaśnia tego dokładnie, po prostu mówi, że GC może zniszczyć obiekt, do którego jest podłączony poprzez słabe odniesienie w dowolnym momencie. Więc jaki jest sens posiadania przedmiotu, który może zniknąć w każdej chwili? Co, jeśli będę musiał go użyć zaraz po zniknięciu?
Czy możesz wyjaśnić je dobrymi przykładami?
Thanks
3 answers
Typowym zastosowaniem słabych odniesień jest to, że A ma odniesienie do B, A B ma odniesienie do A. Bez odpowiedniego filtra śmieci wykrywającego cykl, te dwa obiekty nigdy nie dostałyby GC 'D, nawet jeśli nie ma odniesień do żadnego z "zewnątrz". Jeśli jednak jedna z referencji jest "słaba", obiekty zostaną odpowiednio GC ' D.
Jednak Python posiada moduł garbage collector (od wersji 2.0!), więc to się nie liczy:)
Inne zastosowanie dla słabych odniesień jest dla pamięci podręcznej. Jest wymieniony w weakref
dokumentacji:
Podstawowym zastosowaniem słabych odniesień jest implementacja pamięci podręcznej lub mapowania zawierających duże obiekty, gdzie pożądane jest, aby duży obiekt nie był żywy tylko dlatego, że pojawia się w pamięci podręcznej lub mapowaniu.
Jeśli GC zdecyduje się zniszczyć jeden z tych obiektów, a ty tego potrzebujesz, możesz po prostu przeliczyć / ponownie odczytać dane.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-12 22:36:56
Zdarzenia są częstym scenariuszem dla słabych odniesień.
Problem
Rozważmy parę obiektów: emiter i Odbiornik. Odbiornik ma krótszą żywotność niż emiter.
Możesz wypróbować taką implementację:
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
Jednak w tym przypadku emiter zachowuje odniesienie do metody wiązanej callback
, która zachowuje odniesienie do odbiornika. Więc emiter utrzymuje Odbiornik przy życiu:
# ...continued...
del l
e.emit() # Message received: hello
To jest czasami kłopotliwe. Wyobraź sobie, że {[9] } jest częścią pewnego modelu danych, który wskazuje, kiedy DANE się zmieniają i Receiver
został utworzony przez okno dialogowe, które nasłuchuje tych zmian, aby zaktualizować niektóre kontrolki interfejsu użytkownika.
Przez cały okres istnienia aplikacji można wywoływać wiele okien dialogowych i nie chcemy, aby ich odbiorniki były nadal rejestrowane wewnątrz emitera długo po zamknięciu okna. To byłby wyciek pamięci.
Ręczne usuwanie wywołań zwrotnych jest jedną z opcji (równie kłopotliwych), używanie słabych odwołań jest kolejny.
Rozwiązanie
Jest ładna Klasa WeakSet
, która wygląda jak normalny zestaw, ale przechowuje swoje elementy używając słabych referencji i nie przechowuje ich już, gdy są uwolnione.
def __init__(self):
self.listeners = weakref.WeakSet()
I uruchomić ponownie:
e = Emitter()
l = Receiver(e)
e.emit()
del l
e.emit()
Nic się nie dzieje! Dzieje się tak dlatego, że metoda bound (specyficzny odbiornik callback
) jest teraz osierocona - ani emiter, ani odbiornik nie mają do niej silnego odniesienia. Stąd zbierane są śmieci natychmiast.
Sprawmy, aby odbiornik (nie emiter tym razem) zachowywał silne odniesienie do tego wywołania zwrotnego:
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])
Teraz możemy obserwować oczekiwane zachowanie: emiter utrzymuje callback tylko tak długo, jak długo Odbiornik żyje.
e = Emitter()
l = Receiver(e)
assert len(e.listeners) == 1
del l
import gc; gc.collect()
assert len(e.listeners) == 0
Pod maską
Zauważ, że musiałem umieścić gc.collect()
tutaj, aby upewnić się, że odbiornik jest naprawdę wyczyszczone natychmiast. Jest tu potrzebna, bo teraz jest cykl silnych odniesień: metoda związana odnosi się do odbiornika i odwrotnie.
Nie jest to bardzo złe; oznacza to tylko, że czyszczenie odbiornika zostanie odroczone do następnego uruchomienia garbage collector. Cykliczne odniesienia nie mogą być czyszczone przez prosty mechanizm zliczania odniesień.
Jeśli naprawdę chcesz, możesz usunąć silny cykl odniesienia, zastępując metodę bound niestandardowym obiektem funkcji, który zachowałby jej self
jako słabe odniesienie.
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])
Włóżmy tę logikę do helpera funkcja:
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])
Teraz nie ma cyklu silnych referencji, więc kiedy Receiver
zostanie uwolniona, funkcja zwrotna zostanie również uwolniona (i usunięta z emitera WeakSet
) natychmiast, bez potrzeby pełnego cyklu GC.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-21 12:06:04
- 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.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-30 16:58:34