Hooks (Re: What does Python fix?)

Alex Martelli aleax at aleax.it
Tue Jan 22 03:51:52 EST 2002


"hungjunglu" <hungjunglu at yahoo.com> wrote in message
news:mailman.1011682659.11920.python-list at python.org...
    ...
> class A:
>     def f(self, x):
    ...
> A.f_old = A.f
> A.f = new.instancemethod(g, None, A)
    ...
> (I use Python 2.1. I have not checked the situation in 2.2.) Anyway,

When you subclass A from object or another builtin type, in
2.2 new.instancemethod doesn't work (as discussed in another
thread).  However, you don't even NEED it in the first place:

>>> class A(object):
...   def f(self): print "old f"
...
>>> a=A()
>>> a.f()
old f
>>> def g(self):
...   print "before f"
...   self.old_f()
...   print "after f"
...
>>> A.old_f=A.f
>>> A.f=g
>>> a.f()
before f
old f
after f
>>>

Whether A is a classic-class or a new-style 2.2 class (derived
from object or another builtin), this "just works".

It's interesting, at least in theory, to evolve this into a
full-fledged "Aspect" modifier that systematically wraps
every method-call into a before-and-after wrapper (presumably
what you call "hooks").  Doing it via a metaclass seems the
natural approach, but I don't quite see how to make it work
"retroactively and non-invasively" on already-instantiated
instances of the class being "Aspected".  Doing it via
__getattr__ hits the difficult snag of removing the methods
from the class's namespace (otherwise __getattr__ does not
get called).  So, it seems easier to do in 2.2, given the
new __getattribute__ (which IS called whether a given
attribute is in the class's namespace, or not).

Here's a very-elementary, doesn't-even-consider-most-
important-concerns proof-of-concept for this idea:

>>> def Aspecter(class_, wrapperfun):
...    base_getattribute = class_.__getattribute__
...    def __getattribute__(self, name):
...        attribute = base_getattribute(self, name)
...        if callable(attribute):
...            attribute = wrapperfun(name, class_, attribute)
...        return attribute
...    class_.__getattribute__ = __getattribute__
...
>>> def wrapperfun(name, class_, method):
...    def wrapper(*args):
...       print "before method",name
...       result = method(*args)
...       print "after method",name
...       return result
...    return wrapper
...
>>> Aspecter(A, wrapperfun)
>>> a.f()
before method f
before f
before method old_f
old f
after method old_f
after f
after method f
>>>

Note that Aspecter and wrapperfun also make rather
unabashed use of nested scopes:-).

Unfortunately, this does NOT work with old-style
classes, since they lack __getattribute__.  One of
the several concerns that should somehow be met to
make this into a somewhat-useful component:-).


Alex







More information about the Python-list mailing list