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

Author: eLRuLL, 2010-03-13

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.

 24
Author: Nicolás,
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.

Wspaniale! Użyjmy go:
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.

 29
Author: Kos,
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.
 1
Author: Mangu Singh Rajpurohit,
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