On Mon, Mar 2, 2015 at 4:04 AM, Cem Karan <cfkar...@gmail.com> wrote:
> On Feb 26, 2015, at 2:54 PM, Ian Kelly <ian.g.ke...@gmail.com> wrote:
>> On Feb 26, 2015 4:00 AM, "Cem Karan" <cfkar...@gmail.com> wrote:
>> >
>> >
>> > On Feb 26, 2015, at 12:36 AM, Gregory Ewing <greg.ew...@canterbury.ac.nz> 
>> > wrote:
>> >
>> > > Cem Karan wrote:
>> > >> I think I see what you're talking about now.  Does WeakMethod
>> > >> (https://docs.python.org/3/library/weakref.html#weakref.WeakMethod) 
>> > >> solve
>> > >> this problem?
>> > >
>> > > Yes, that looks like it would work.
>> >
>> >
>> > Cool!
>>
>> Sometimes I wonder whether anybody reads my posts. I suggested a solution 
>> involving WeakMethod four days ago that additionally extends the concept to 
>> non-method callbacks (requiring a small amount of extra effort from the 
>> client in those cases, but I think that is unavoidable. There is no way that 
>> the framework can determine the appropriate lifetime for a closure-based 
>> callback.)
>
> I apologize about taking so long to reply to everyone's posts, but I've been 
> busy at home.
>
> Ian, it took me a while to do some research to understand WHY what you were 
> suggesting was important; you're right about storing the object as well as 
> the method/function separately, but I think that WeakMethod might solve that 
> completely, correct?  Are there any cases where WeakMethod wouldn't work?

WeakMethod only works for bound method objects. If you pass it a
non-method function, you'll get a TypeError:

>>> from weakref import WeakMethod
>>> WeakMethod(lambda: None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.4/weakref.py", line 49, in __new__
    .format(type(meth))) from None
TypeError: argument should be a bound method, not <class 'function'>

This check uses duck typing, so you could perhaps write a method-like
class with __self__ and __func__ attributes and pass that to the
WeakMethod constructor in the case of non-methods. There's a bigger
issue with this however, which is that WeakMethod works by keeping
weak references to *both* the object and the function, meaning that as
soon as the function has no other references, the WeakMethod expires
even if the object is still alive. This isn't a problem for methods
because it's the transience of the method object, not the underlying
function, that WeakMethod seeks to work around. But it doesn't by
itself do anything to solve the problem of closures or lambdas that
may themselves be transient.

Revisiting the implementation I suggested previously, I want to make a
correction. This would be better solved with a WeakValueDictionary:

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

    def listen(self, callback, owner=None):
        if owner is None:
            if isinstance(callback, types.MethodType):
                owner = weakref.WeakMethod(callback)
            else:
                owner = callback
        # TODO: Should anything happen if the callback is already in the dict?
        self._callbacks[callback] = owner

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

This approach has two benefits over the previous one: it simplifies
the callback management a bit, and it avoids making the assumption
that the owner is hashable (it assumes instead that the callback is
hashable, but I think that's reasonable).
-- 
https://mail.python.org/mailman/listinfo/python-list

Reply via email to