Hooks (Re: What does Python fix?)

Hung Jung Lu hungjunglu at yahoo.com
Mon Jan 21 17:01:41 EST 2002


Steven Majewski <sdm7g at Virginia.EDU> wrote in message news:<mailman.1011219213.17167.python-list at python.org>...
> In Paul Graham's article  "What Languages Fix" :
> ... 
> Any suggestions?

This is from a recent experience: dynamic attributes in Python makes
code easier to maintain. Especially for hook methods in class
hierarchy trees.

Case example: you have class A with subclass B, and B with subclass C,
etc. You have a method A.f() that performs some fairly complicated
operations. Now, you realize that B.f() pretty much is similar to
A.f(), except for a few extra intermediate steps. Similarly, C.f() is
similar to B.f(), except for another few intermediate steps. What do
you do?

Possible implementations in C++ are (sorry for skipping some details):

(1) Use virtual methods for A::f(), B::f(), C::f(): you cut and paste
a large chunk of code, but you later often have to make same changes
in each of the virtual methods A::f(), B::f(), C::f(). Not good.

(2) Use one single A::f(), but implement helper hook methods
(virtually). That is, A::f() calls helper hook methods h1(), h2(),
etc, and subclass B, C implement the right hook methods. Disadvantage:
Just because B::h1() and C::h2() are needed, you have to declare
A::h1(), A::h2() and B::h2(). Not good. (One comment: the hook methods
usually access data members of the subclasses, that is why you cannot
simply use "if" statement blocks inside A::f() to accomodate the needs
of the subclasses.)

(3) Use function pointers. Kind of messy. Not good.

Anyway, when using Python, the headache is gone. To add a new hook is
a piece of cake, and you can always do:

    if hasattr(object, 'f'):
        x = object.f()

You modify the code only at two points, exactly where you need to
modify, and there is no ripple effects to other places of your code.
Moreover, in Python the argument list of methods is flexible, so
actually the hooks can be implemented with different argument lists in
different subclasses.

    if object.__class__ == B:
        x = object.f(y)
    elif object.__class__ == C:
        x = object.f(y, z)

------------------------------

Related to the above: Accessor methods.

In C++/Java world, the common wisdom is to implement accessor methods
to access data members of an object (known also as the "getters" and
"setters"). For the longest time I thought there was a universal
programming reason behind this strategy. But only recently did I
realize that this in part is because C++ often needed the accessor
methods for superclasses to get information from the subclasses! In
other words: there are virtual method members in C++, but there are no
virtual data members: a superclass just cannot access subclass data
members, without some extra tweaking. It is not surprise to me that in
my Python codes I often can directly access object attributes without
getters and setters, and that I am forced to use getters and setters
quite often in Java/C++.

(Of course, Python is now introducing customerizable getters and
setters in 2.2, which is fine, because it is making the interception
of storage/retrievel operations easier, and this feature is of course,
optional.)

-----------------------------

Object orientation is all about code re-use. And as far as I can see,
code reuse in Java/C++ is hard, when it comes to complicated base
class methods that contains hook calls to subclasses.

-----------------------------

What doesn't Python fix? Hmm... as far as hooks are concerned, I often
wish there were a simpler way of inserting hooks into pre-existing
functions/methods more easily. That is, given a function/method f(),
it would be nice to be able to insert pre-functions and
post-functions. Mathematically, this is replacing a function f() with
a function composition g(f(h())) or g*f*h(). Function composition is
not easy in Python. It can be done, it's just not easy. Why do we need
function composition? Again, the matter is hooks and code-reuse. In
all the languages I know, dynamic function composition is not easy,
and frankly, in Java/C++ it seems totally imposibble. (Security of
course is a huge concern, but let us not get into there.) Yet function
composition is a powerful tool. For instance, a typical situation
would be something like the Apache mod_rewrite module: given a URL, it
originally was supposed to retrieve a certain web object. However, if
you can insert hooks, you can modify the URL just in time, and
retrieve a new object somewhere else. Function composition also allows
people to do thing like profiling: how many times a function has been
called, how much time is spent in a function, etc. Function
composition allows the implementation of security: when a function is
called, you can check the authorization at that very last microsecond,
hence protecting at the individual object/method level. Function
composition of course also allows easier implementation of patches: a
third-party package can be more easily patched, without the need of
modifying the third-party's source code. Dynamically changing the code
of a function (in a general way) is not easy nor do I think it is too
widely useful, BUT, dynamically pre-inserting and post-inserting hooks
does seem to me to be a personally-often-wished feature, in my
experience.

Sorry for not being too detailed. But I hope the general idea did get
through. :)

regards,

Hung Jung



More information about the Python-list mailing list