Potential improvement on delegation via explicit calls and super

Discussion in 'Python' started by Robert Dick, Dec 17, 2004.

  1. Robert Dick

    Robert Dick Guest

    Derived classes sometimes need to delegate portions of the work in overridden
    methods to methods in their base classes. This was traditionally done with
    explicit calls in python, e.g.,

    class Derived(Left, Right):
    def __init__(self, myarg, larg, rarg):
    Left.__init__(self, larg)
    Right.__init__(self, rarg)
    self.data = myarg
    print 'derived'

    This worked great. It was possible to grab the appropriate arguments and send
    them off to the right point. However, there was a problem.

    class Base:
    def __init__(self):
    print 'base'

    class Left(Base):
    def __init__(self, arg):
    Base.__init__(self)
    print 'left'

    class Right(Base):
    def __init__(self, arg):
    Base.__init__(self)
    print 'right'

    Now, when Derived(Left, Right) is initialized, Base.__init__ is called twice.
    Sometimes that's O.K. Usually, it's a bad thing. Along came new-style
    classes and 'super'. Unfortunately, super-based delegation doesn't play
    nicely with traditional classes.
    http://www.ai.mit.edu/people/jknight/super-harmful/
    Moreover, it undermines any attempts to control which subset of arguments go
    to which base class. This second problem is serious. In real life, base
    classes differ from each other: I need to be able to send the right arguments
    to each.

    What I really want to do is explicitly delegate tasks to base classes,
    choosing the arguments to send to each, but avoid double-calls resulting from
    reconverging paths in the inheritance directed acyclic (pray it's acyclic)
    graph.

    I think the appended code may solve this problem, play nicely with traditional
    classes, and allow the coder to send the right arguments to the right base
    classes.

    However, I'm new to python so I need some help.

    1) Is my thinking reasonable or is there an better way to solve the
    reconvergent path problem in python without undermining control over
    arguments?

    2) What's the proper way to rewrite the appended code. I'm sure it's
    dreadfully inefficient. There are also probably ways to make its use more
    intuitive, but I'm new to the language so I don't know the tricks yet.

    Thanks for any tips,

    -Robert Dick-

    ----
    '''See the example at the bottom.'''

    import inspect

    def flatten_tree(tree):
    '''Flatten a tree represented by nested lists'''
    if isinstance(tree, list):
    return [j for i in tree for j in flatten_tree(i)]
    else:
    return (tree,)

    # Cache for delegation decisions.
    call_cache = set()

    def should_call(self, pos, supr):
    '''Examines the inheritance DAG (might work for DCGs, too... haven't
    checked) for 'self' to determine whether 'pos' is the leftmost derived
    for 'supr'. Returns bool. Caches results for performance.'''
    if (self.__class__, pos, supr) in call_cache: return True
    ct = flatten_tree(inspect.getclasstree(inspect.getmro(self.__class__),
    True))
    # ct is a list of (class, (base classes)) tuples
    # Find the first instance of the supr as a base class
    do_call = pos is [cls for cls, bases in ct if supr in bases][0]
    if do_call: call_cache.add((self.__class__, pos, supr))
    return do_call

    def delegate(self, pos, s_call, *pargs, **kargs):
    '''If 'pos' is the leftmost derived for 's_call' in the 'self' inheritance
    DAG, call s_call with 'pargs' and 'kargs'.'''
    if inspect.ismethoddescriptor(s_call):
    supr = s_call.__objclass__
    else:
    supr = s_call.im_class
    if should_call(self, pos, supr):
    s_call(self, *pargs, **kargs)

    if __name__ == '__main__':
    class Base(object):
    def __init__(self):
    delegate(self, Base, object.__init__)
    print 'base'

    class Left(Base):
    def __init__(self):
    delegate(self, Left, Base.__init__)
    print 'left'

    class Right(Base):
    def __init__(self):
    delegate(self, Right, Base.__init__)
    print 'right'

    class Der(Left, Right):
    def __init__(self):
    delegate(self, Der, Left.__init__)
    delegate(self, Der, Right.__init__)
    print 'der'

    der = Der()
    Robert Dick, Dec 17, 2004
    #1
    1. Advertising

  2. Am Fri, 17 Dec 2004 02:17:38 -0600 schrieb Robert Dick:

    > Derived classes sometimes need to delegate portions of the work in overridden
    > methods to methods in their base classes. This was traditionally done with
    > explicit calls in python, e.g.,
    >
    > class Base:
    > def __init__(self):
    > print 'base'
    >
    > class Left(Base):
    > def __init__(self, arg):
    > Base.__init__(self)
    > print 'left'
    >
    > class Right(Base):
    > def __init__(self, arg):
    > Base.__init__(self)
    > print 'right'


    If you can change the source of Base, I would do it like
    this:

    class Base:
    _init_done=0
    def __init__(self):
    if self._init_done:
    return
    self._init_done=1

    # ... do init


    Thomas

    --
    Thomas Güttler, http://www.thomas-guettler.de/
    Thomas Guettler, Dec 17, 2004
    #2
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Guest

    super.super.super how?

    Guest, Feb 19, 2005, in forum: Java
    Replies:
    24
    Views:
    10,741
    Darryl Pierce
    Feb 24, 2005
  2. Fernando Rodriguez

    Getting the super class via the super() function

    Fernando Rodriguez, Nov 21, 2003, in forum: Python
    Replies:
    2
    Views:
    708
    Bob Willan
    Nov 22, 2003
  3. Sam Roberts
    Replies:
    4
    Views:
    298
    Sam Roberts
    May 7, 2008
  4. cbare
    Replies:
    8
    Views:
    169
    John G Harris
    Oct 31, 2007
  5. cbare
    Replies:
    1
    Views:
    88
    cbare
    Nov 1, 2007
Loading...

Share This Page