Подтвердить что ты не робот

Когда использовать слабые ссылки в Python?

Может ли кто-нибудь объяснить использование слабых ссылок?

Документация не объясняет это точно, она просто говорит, что GC может уничтожить объект, связанный с помощью слабой ссылки в любое время, Тогда какой смысл иметь объект, который может исчезнуть в любое время? Что делать, если мне нужно использовать его сразу после его исчезновения?

Не могли бы вы объяснить им несколько хороших примеров?

Спасибо

4b9b3361

Ответ 1

Типичное использование слабых ссылок заключается в том, что если A имеет ссылку на B и B имеет ссылку на A. Без надлежащего обнаружения циклов сборщик мусора эти два объекта никогда не получат GC'd, даже если нет ссылок на либо из "снаружи". Однако, если одна из ссылок является "слабой", объекты получат должным образом GC'd.

Однако у Python есть сборщик мусора, обнаруживающий цикл (начиная с 2.0!), так что не учитывается:)

Другое использование для слабых ссылок - для кэшей. Он упоминается в документации weakref:

Первичное использование слабых ссылок заключается в реализации кэшей или сопоставлений, содержащих большие объекты, где требуется, чтобы большой объект не поддерживался только потому, что он отображается в кеше или сопоставлении.

Если GC решает уничтожить один из этих объектов, и вам это нужно, вы можете просто пересчитать/восстановить данные.

Ответ 2

События - общий сценарий для слабых ссылок.


Проблема

Рассмотрим пару объектов: излучатель и приемник. Приемник имеет более короткий срок службы, чем излучатель.

Вы можете попробовать реализацию следующим образом:

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

Однако в этом случае эмиттер сохраняет ссылку на связанный метод callback, который сохраняет ссылку на приемник. Таким образом, излучатель поддерживает приемник:

# ...continued...

del l
e.emit() # Message received: hello

Это иногда хлопотно. Представьте, что Emitter является частью некоторой модели данных, которая указывает, когда изменения данных и Receiver были созданы диалоговым окном, которое слушает об изменениях для обновления некоторых элементов управления пользовательского интерфейса.

В течение срока службы приложения может быть создано несколько диалогов, и мы не хотим, чтобы их приемники все еще были зарегистрированы внутри излучателя, после того как окно было закрыто. Это будет утечка памяти.

Удаление обратных вызовов вручную - это один из параметров (так же, как хлопотно), использование слабых ссылок - это другое.


Решение

Там хороший класс 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])

Теперь мы можем наблюдать ожидаемое поведение: излучатель сохраняет только обратный вызов, пока живет приемник.

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 __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])

Положите эту логику в вспомогательную функцию:

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 WeakSet) немедленно, без необходимости полного цикла GC.

Ответ 3

 - 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 also very useful in implementing caches.