Design thought for callbacks

Ian Kelly ian.g.kelly at gmail.com
Mon Feb 23 01:57:04 EST 2015


On Sun, Feb 22, 2015 at 7:22 AM, Cem Karan <cfkaran2 at gmail.com> wrote:
>
> On Feb 22, 2015, at 5:15 AM, Gregory Ewing <greg.ewing at canterbury.ac.nz> wrote:
>
>> Frank Millman wrote:
>>> "In order to inform users that certain bits of state have changed, I require them to register a callback with my code."
>>> This sounds to me like a pub/sub scenario. When a 'listener' object comes into existence it is passed a reference to a 'controller' object that holds state. It wants to be informed when the state changes, so it registers a callback function with the controller.
>>
>> Perhaps instead of registering a callback function, you
>> should be registering the listener object together with
>> a method name.
>>
>> You can then keep a weak reference to the listener object,
>> since if it is no longer referenced elsewhere, it presumably
>> no longer needs to be notified of anything.
>
> I see what you're saying, but I don't think it gains us too much.  If I store an object and an unbound method of the object, or if I store the bound method directly, I suspect it will yield approximately the same results.

Well, it ties the weak ref to the lifetime of the object owning the
callback rather than to the lifetime of the potentially unreferenced
callback itself. I'm not fond of the scheme though because it forces
the callback to be a method, and I'd prefer not to make that
assumption.

Also, I just noticed that Python 3.4 adds a weakref.WeakMethod class
that solves the problem for the bound method case. That still leaves
the closure and lambda cases, but here's a thought: add an optional
argument to the callback registration method that specifies what
object to tie the weak ref to. Something like:

class Listenable:
    def __init__(self):
        self._callbacks = weakref.WeakKeyDictionary()

    def listen(self, callback, owner=None):
        if owner is None:
            if isinstance(callback, types.MethodType):
                owner = weakref.WeakMethod(callback)
            else:
                owner = callback
        self._callbacks.setdefault(owner, []).append(callback)

    def do_callbacks(self, message):
        for callbacks in self._callbacks.values():
            for callback in callbacks:
                callback(message)



More information about the Python-list mailing list