Может ли кто-нибудь объяснить использование слабых ссылок?
Не могли бы вы объяснить им несколько хороших примеров?
Спасибо
Может ли кто-нибудь объяснить использование слабых ссылок?
Не могли бы вы объяснить им несколько хороших примеров?
Спасибо
Типичное использование слабых ссылок заключается в том, что если A имеет ссылку на B и B имеет ссылку на A. Без надлежащего обнаружения циклов сборщик мусора эти два объекта никогда не получат GC'd, даже если нет ссылок на либо из "снаружи". Однако, если одна из ссылок является "слабой", объекты получат должным образом GC'd.
Однако у Python есть сборщик мусора, обнаруживающий цикл (начиная с 2.0!), так что не учитывается:)
Другое использование для слабых ссылок - для кэшей. Он упоминается в документации weakref
:
Первичное использование слабых ссылок заключается в реализации кэшей или сопоставлений, содержащих большие объекты, где требуется, чтобы большой объект не поддерживался только потому, что он отображается в кеше или сопоставлении.
Если GC решает уничтожить один из этих объектов, и вам это нужно, вы можете просто пересчитать/восстановить данные.
События - общий сценарий для слабых ссылок.
Рассмотрим пару объектов: излучатель и приемник. Приемник имеет более короткий срок службы, чем излучатель.
Вы можете попробовать реализацию следующим образом:
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.
- 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.