I've been living off of them for years, but have finally found a use for Python's weak references in my own code.
In Shapely, coordinate sequences, rings of polygons, and members of multi-part geometries provide proxy access to a GEOS geometry structure "owned" by the parent polygon or multi-part geometry. Through their lives they must carry references to the parent like this:
>>> point.coords.__p__ == point
True
so that an anonymous parent object isn't swept up by the Python garbage collector before a hobuism like:
>>> len(polygon.exterior.centroid.coords)
1
executes completely. The implementation is a bit tricky, but a Shapely user doesn't need to think about it: it just works.
Some sub-geometries are expensive enough to create that I've decided to cache them in the parent. Since the "child" already holds a reference to the parent, making a reference to the child from the parent creates a potentially problematic reference cycle. The example below shows cyclic references that prevent Python's automatic garbage collection from freeing objects. The garbage attribute of the gc object below contains references to objects that aren't freed.
>>> import gc
>>>
>>> class ReferencingThing:
... ref = None
...
>>> gc.enable()
>>> gc.set_debug(gc.DEBUG_SAVEALL)
>>>
>>> for i in range(2):
... a = ReferencingThing()
... b = ReferencingThing()
... a.ref = b
... b.ref = a
...
>>> gc.collect()
4
>>> gc.garbage
[{'ref': <__main__.ReferencingThing instance at 0xb7dbec4c>},
<__main__.ReferencingThing instance at 0xb7dbeb8c>,
{'ref': <__main__.ReferencingThing instance at 0xb7dbeb8c>},
<__main__.ReferencingThing instance at 0xb7dbec4c>
]
A Shapely user demonstrated to me such a bug that was causing geometries to pile up in his application. A weak reference, which is not enough to keep an object alive, does not so block Python's automatic garbage collection. Here is the non-leaking weakly-referencing version of the code above:
>>> import gc
>>> import weakref
>>>
>>> class WeaklyReferencingThing:
... _ref = None
... def reference(self, ob):
... self._ref = weakref.ref(ob)
... def dereference(self):
... return self._ref()
...
>>> gc.enable()
>>> gc.set_debug(gc.DEBUG_SAVEALL)
>>>
>>> for i in range(2):
... p = WeaklyReferencingThing()
... q = WeaklyReferencingThing()
... p.reference(q)
... q.reference(p)
...
>>> gc.collect()
0
>>> gc.garbage
[]
I've also come across a pattern for weak attributes that provides a nicer syntax.
Comments
Re: Rocky Mountain Rail Authority Study
Author: Paul Ramsey
Breaking news, feasibility to be studied, more at 11!Another sorry chapter in the North American real odyssey.
Here in Victoria, we're busily allowing a perfectly good rail corridor up the island fall into complete disrepair. It's so bad the tracks have potholes! God forbid government should subsidize a transportation network. The highway system is required, but the rail system is optional. Bah!
Re: Rocky Mountain Rail Authority Study
Author: Sean
Yes, I know: I'm setting myself up for disappointment. The geography is favorable here (flat and linear), but money and politics will decide. High speed rail is way too much like Communism for people outside the liberal ghettos of Fort Collins, Boulder, and Denver. I suspect a high speed "ski" train in the I-70 corridor will be found most feasible.Re: Rocky Mountain Rail Authority Study
Author: James Fee
Hey if they can't even get high speed rail between LA and Las Vegas, those in other areas (we here in Arizona are looking at Phoenix to Tucson high speed rail) don't have a prayer.