Design thought for callbacks

Cem Karan cfkaran2 at gmail.com
Sun Feb 22 08:21:35 EST 2015


On Feb 21, 2015, at 3:57 PM, Grant Edwards <invalid at invalid.invalid> wrote:

> On 2015-02-21, Cem Karan <cfkaran2 at gmail.com> wrote:
>> 
>> On Feb 21, 2015, at 12:42 AM, Chris Angelico <rosuav at gmail.com> wrote:
>> 
>>> On Sat, Feb 21, 2015 at 1:44 PM, Cem Karan <cfkaran2 at gmail.com> wrote:
>>>> In order to inform users that certain bits of state have changed, I require them to register a callback with my code.  The problem is that when I store these callbacks, it naturally creates a strong reference to the objects, which means that if they are deleted without unregistering themselves first, my code will keep the callbacks alive.  Since this could lead to really weird and nasty situations, I would like to store all the callbacks in a WeakSet (https://docs.python.org/3/library/weakref.html#weakref.WeakSet).  That way, my code isn't the reason why the objects are kept alive, and if they are no longer alive, they are automatically removed from the WeakSet, preventing me from accidentally calling them when they are dead.  My question is simple; is this a good design?  If not, why not?  Are there any potential 'gotchas' I should be worried about?
>>>> 
>>> 
>>> No, it's not. I would advise using strong references - if the callback
>>> is a closure, for instance, you need to hang onto it, because there
>>> are unlikely to be any other references to it. If I register a
>>> callback with you, I expect it to be called; I expect, in fact, that
>>> that *will* keep my object alive.
>> 
>> OK, so it would violate the principle of least surprise for you.
> 
> And me as well.  I would expect to be able to pass a closure as a
> callback and not have to keep a reference to it.  Perhaps that just a
> leftover from working with other languages (javascript, scheme, etc.).
> It doesn't matter if it's a string, a float, a callback, a graphic or
> whatever: if I pass your function/library an object, I expect _you_ to
> keep track of it until you're done with it.
> 
>> Interesting.  Is this a general pattern in python?  That is,
>> callbacks are owned by what they are registered with?
> 
> I'm not sure what you mean by "owned" or why it matters that it's a
> callback: it's an object that was passed to you: you need to hold onto
> a reference to it until you're done with it, and the polite thing to
> do is to delete references to it when you're done with it.

I tend to structure my code as a tree or DAG of objects.  The owner refers to the owned object, but the owned object has no reference to its owner.  With callbacks, you get cycles, where the owned owns the owner.  As a result, if you forget where your object has been registered, it may be kept alive when you aren't expecting it.  My hope was that with WeakSets I could continue to preserve the DAG or tree while still having the benefits of callbacks.  However, it looks like that is too surprising to most people.

Thanks,
Cem Karan


More information about the Python-list mailing list