mzh/blog

Python标准库小窥[1]:weakref

平时工作经常能碰到一部分标准库的代码,但是常常因为琐事没有细细地研究这些标准库,直到最近发觉Python不愧是battery included的语言,因此决定从PyMOTW好好学学。 那么就从最常见,但最容易忽略的weakref开始吧! 先让我们看看weakref想解决什么问题。 PyMOTW是这样说的:

Refer to an “expensive” object, but allow it to be garbage collected if there are no other non-weak references.

引用一个”开销大”的对象,并在只剩下弱引用时允许垃圾回收机制回收这个对象。 PyMOTW中的例子 当obj被显式地删除后(模拟gc回收了),弱引用的proxy和ref的引用对象消失了,不能再取回了。 达到了之前希望的只剩下弱引用时允许回收。 如果还有强引用,这些弱引用仍能正常获取值。 问题就来了,这个蛋疼的东西到底有什么用? 那就是当个智能Cache 比如你有一堆图片文件buffer,你希望通过字典类组成一个cache来存储它们,以获得可观的O(1)读取速度。但是,当这个cache中的图片越来越多时,由于Python自带的gc(垃圾回收机制)没办法收回字典内引用了的项,导致cache越来越大,内存消耗加大,可你又不想用其他方法暂存这些数据到硬盘(因为慢啊!),这时,如果有种方法让这些存储项能自动清除,并能该有多好! 这就是PyMOTW中的Cache例子 可以看到,使用dict的例子中,如果删除了所有引用(all_refs),cache仍然保留着这些”开销大”的对象,而用WeakValueDictionary就完成了正常回收的过程,保证了cache不会过多地占用系统空间。 还有一种用途,就是保证循环引用可回收 比如有以下节点 A B C,他们相互有指向下个节点的引用(->)表示

A->B
B->C
C->A
即A->B->C->A

当这个引用形成了环形时,如果把其中两个节点(B、C)删除掉,这个环仍能正常工作,

A->B->C->A

但是当我们删掉最后的A节点后,gc就不明白该不该回收这些节点,因此,造成了内存泄漏(leaking) 这个情况正如PyMOTW所示:

After 2 references removed:

one->two->three->one

Collecting...

Unreachable objects: 0

Garbage:[]



Removing last reference:

Collecting...

gc: uncollectable

gc: uncollectable

gc: uncollectable

gc: uncollectable

gc: uncollectable

gc: uncollectable

Unreachable objects: 6

Garbage:[Graph(one),

 Graph(two),

 Graph(three),

 {'name': 'one', 'other': Graph(two)},

 {'name': 'two', 'other': Graph(three)},

 {'name': 'three', 'other': Graph(one)}]

如果使用弱引用的dict就没有这个问题啦~ 注意,由于WeakDict是构建于dict之上的,因此,不要遍历(iter)这个对象,因为里面的值随时发生变化