Properties, Methods, Events

Delaney, Timothy tdelaney at avaya.com
Thu May 10 06:12:05 CEST 2001


> "Fredrik Lundh" <fredrik at pythonware.com> wrote in message
> news:BdVJ6.864$Yu6.223845 at newsc.telia.net...
> > John Flynn wrote:
> > > > Do you know of any way to trap an ordinary method call? 
> Eg. if Time-
> > > > Bomb has a method 'tick', can we find a way to know 
> automatically when
> > > > it is called?
> > >
> > > Ahem... On second thoughts, this goes down as the dopiest and most
> > > meaningless question in history.
> >
> > why?  assuming that you meant "... when it is called, 
> without modifying
> > the method", it's a rather interesting question.
> 
> Yep, that is what I meant the first time, and it _was_ a 
> reasonable question
> after all. An hour or so later I was nearly asleep, and I 
> lost sight of what
> I was trying to do. "You want to know when a method is called? Just do
> something inside the method, you dope!". (Which _is_ the best 
> way 99% of the
> time, but not what I wanted here).
> 
> > (hint: let __getattr__ return a proxy object if the member 
> is callable,
> and
> > let that object call the original method from its __call__ method)
> 
> Thanks /F (and Alex). I'll play around with this. I really 
> dig this idea of
> a language that's not just high level but almost completely 
> transparent if
> you know what you're doing. (Not that I do yet).

Here's a bit of code (quickly thrown together, but tested) which
demonstrates all this. If any method is called on the proxy, it is passed
through to the proxied object and there is a corresponding event handler
then the event handler is called *after* the method call.

The evant handler name is formed by prepending 'on' to the method, and
capitalising the first character of the method.

Note: This is essentially the Decorator design pattern.

Note: there are two main problems with this:

1. Magic methods like __str__() do not exist for types such as lists - you
can see how I have got around this for str() and repr().

2. If the proxied object's method then calls one of its own methods, the
event handler will not be fired.

There are a couple of ways you could deal with #2. One I can think of
off-hand only works with single-threading. When a method call is made,
actually replace *all* the methods of the proxied object with ones which
call the event handlers, and restore them once the method call is complete
(would require try...finally). In a multi-threaded situation though this
could be dangerous ... especially if you have multiple EventProxy objects
proxying the same object.

class EventProxy:
    """"""

    class __FunctionWrapper:
        """"""
        
        def __init__(self, name, function, proxy, delegate):
            """"""
            self.__name = name
            self.__function = function
            self.__proxy = proxy
            self.__delegate = delegate

        def __call__(self, *p, **kw):
            """"""
            apply(self.__function, p, kw)

            try:
                name = 'on' + self.__name[:1].upper() + self.__name[1:]
                getattr(self.__proxy, name)()
            except AttributeError:
                pass

    def __init__(self, delegate):
        """"""
        self.__delegate = delegate

    def __getattr__(self, name):
        """"""
        return self.__FunctionWrapper(name, getattr(self.__delegate, name),
self, self.__delegate)

    def __str__(self):
        """"""
        s = str(self.__delegate)

        try:
            self.on__str__()
        except AttributeError:
            pass

        return s

    def __repr__(self):
        """"""
        s = repr(self.__delegate)

        try:
            self.on__repr__()
        except AttributeError:
            pass

        return s

if __name__ == '__main__':

    import sys

    l = []
    ep = EventProxy(l)

    ep.onAppend = lambda: sys.stdout.write('append called\r\n')
    ep.append(1)
    ep.append(2)

    ep.on__str__ = lambda: sys.stdout.write('__str__ called\r\n')
    print ep

Tim Delaney




More information about the Python-list mailing list