Re: MRO problems with diamond inheritance?

2005-05-01 Thread Robert Dick
M.E.Farmer:
> Your answer lies somewhere in this page ;)
> http://www.python.org/2.2.2/descrintro.html
> M.E.Farmer

delegate.py (use PyPI) may also be useful.

-Robert Dick-
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Calling __init__ with multiple inheritance

2005-03-28 Thread Robert Dick
jfj:
> In the case of Parent diamond inheritance, super() can avoid calling
> the __init__ of parent twice?  How?

Guido has a nice description of it: 
http://www.python.org/2.2.3/descrintro.html#cooperation.

It linearizes the graph.  Unfortunately, this means that super delegates to 
siblings.  This makes super incompatible with differences in positional 
arguments within a class hierarchy.  If you want a super that works with 
positional arguments, please see delegate.py at 
http://ziyang.ece.northwestern.edu/~dickrp/python/delegate.html (also in 
PyPI).  If others don't find problems with the approach, I hope it becomes 
standard.  I found it distracting that python's default argument passing 
approach conflicts with its default delegation approach.

Good luck,

-Robert Dick-
-- 
http://mail.python.org/mailman/listinfo/python-list


Potential improvement on delegation via explicit calls and super

2004-12-17 Thread 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 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()
-- 
http://mail.python.org/mailman/listinfo/python-list