A problem is that the variant semantics also seem pretty arbitrary ;-), and there's a dearth of compelling use cases to guide a decision.
[Greg Ewing] |> At the time I think I suggested that it would be
reasonable if weakref callbacks were guaranteed to be called as long as they weren't trash themselves and they didn't reference any trash. From what you said in a recent message, it sounds like that's the way it currently works.
Nope, and that was the point of the first message in the thread I referenced. The issues were explained well in that thread, so I don't want to repeat it all here again.
These semantics would be sufficient to be able to use weakrefs to register finalisers, I think. You keep a global list of weakrefs with callbacks that call the finalizer and then remove the weakref from the global list.
I'll put together a prototype some time and see if it works. I actually have a use case at the moment -- a couple of types that need to release external resources, and since they're part of a library I'm distributing, I can't be sure people won't put them in cycles.
Note that it's very easy to do this with __del__. The trick is for your type not to have a __del__ method itself, but to point to a simple "cleanup object" with a __del__ method. Give that "contained" object references to the resources you want to close, and you're done. Because your "real" objects don't have __del__ methods, cycles involving them can't inhibit gc. The cleanup object's only purpose in life is to close resources. Like:
class _RealTypeResourceCleaner: def __init__(self, *resources): self.resources = resources
def __del__(self): if self.resources is not None: for r in self.resources: r.close() self.resources = None
# and typically no other methods are needed, or desirable, in # this helper class
class RealType: def __init__(*args): ... # and then, e.g., self.cleaner = _ResourceCleaner(resource1, resource2)
... tons of stuff, but no __del__ ....
That's the simple, general way to mix cycles and __del__ without problems.