connect 的 weak 参数默认是 True:信号只弱引用 receiver。常见的说法是「局部函数当 receiver 会被回收」。这篇按这个说法去试,结果和预期不一样,顺着查下去找到了原因。
setup() 返回并 gc.collect() 之后 send:LOG = ['local called']
这和我一开始的预期相反,所以我没有直接下结论,而是接着查原因。
缓存现状: CacheInfo(hits=0, misses=47, maxsize=512, currsize=47) 再 connect 600 个别的函数后: CacheInfo(hits=0, misses=647, maxsize=512, currsize=512) send: [] LOG = [] receivers = 0
读 Django 源码:django/utils/inspect.py 里的 _get_func_parameters 被 functools.lru_cache(maxsize=512) 装饰,connect 检查 receiver 签名时会调用它,缓存把函数对象强引用住了。实测把缓存挤满(600 个别的函数)后,那个局部函数才被回收。也就是说 weak=True 的局部 receiver「什么时候消失」取决于缓存里还有多少别的函数——行为不确定,不要依赖它。这一点本文只在 Django 6.1.1 + Python 3.14 上验证,别的版本没有测。
被调用的: ['lambda', 'partial', 'module-level']
对象存在:n = 1 receivers = 1 对象 del 之后 send() 返回: [] receivers = 0
绑定方法用 WeakMethod:缓存里放的是底层函数,不是实例,所以实例本身可以被回收。
weak=True: 删除对象引用后对象是否已被回收: True weak=False: 删除对象引用后对象是否已被回收: False
弱引用的好处:receiver 是对象的方法时,对象不用再手动 disconnect。weak=False 让信号强引用,适合局部函数或 lambda(不然行为不确定),但持有的对象就不会释放。