Почему обработка сигналов Django использует слабые ссылки для обратных вызовов по умолчанию?

Django docs говорят об этом по этому вопросу:

Обратите внимание, что Django сохраняет сигнал как слабые ссылки default, поэтому, если ваш обработчик является локальным функция, это может быть сбор мусора. Чтобы предотвратить это, пройдите слабый = False, когда вы вызываете сигналы connect().

Я не смог найти обоснования, почему это значение по умолчанию, и я не понимаю, почему вам когда-либо понадобится сигнал, который вы явно зарегистрировали, чтобы неявно исчезнуть. Итак, каков прецедент для слабых ссылок здесь? И почему это значение по умолчанию?

Я понимаю, что это, вероятно, не имеет значения в 99% случаев, но, очевидно, там что-то я здесь не понимаю, и я хочу знать, есть ли какие-либо "пропашные", которые могут укусить меня когда-нибудь.

Ответы

Ответ 1

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

Ответ 2

Связанные методы сохраняют ссылку на объект, к которому они принадлежат (в противном случае они не могут заполнить self, см. Документация по Python), Рассмотрим следующий код:

import gc
class SomeLargeObject(object):
    def on_foo(self): pass

slo = SomeLargeObject()
callbacks = [slo.on_foo]

print [o for o in gc.get_objects() if isinstance(o, SomeLargeObject)]
del slo
print [o for o in gc.get_objects() if isinstance(o, SomeLargeObject)]
callbacks = []
print [o for o in gc.get_objects() if isinstance(o, SomeLargeObject)]

Выход:

[<__main__.SomeLargeObject object at 0x15001d0>]
[<__main__.SomeLargeObject object at 0x15001d0>]
[]

Одна важная вещь, которую следует знать при сохранении weakrefs при обратных вызовах, заключается в том, что вы не можете напрямую ссылаться на связанные методы, потому что они всегда создаются "на лету":

>>> class SomeLargeObject(object):
...  def on_foo(self): pass
>>> import weakref
>>> def report(o):
...  print "about to collect"
>>> slo = SomeLargeObject()
>>> #second argument: function that is called when weakref'ed object is finalized
>>> weakref.proxy(slo.on_foo, report)
about to collect
<weakproxy at 0x7f9abd3be208 to NoneType at 0x72ecc0>