raising an exception when multiple inheritance involves same baseThank

Scott David Daniels Scott.Daniels at Acm.Org
Sun May 25 16:56:01 EDT 2008


Here are some tweaks on both bits of code:

Paul McGuire wrote:
> On May 25, 8:37 am, Michael Hines <michael.hi... at yale.edu> wrote:
...
>>     m = False
>>     for b in bases :
>>       if hasattr(b, '__mro__'):
>>         for bb in b.__mro__ :
>>           if bb == MetaHocObject.ho :
>>             if m == True:
>>               raise Exception("Inheritance of multiple HocObject not
>> allowed")
>>             m = True
     m = []
     for b in bases:
         if hasattr(b, '__mro__'):
             if MetaHocObject.ho in b.__mro__:
                 m.append(b)
     if m:
         raise TypeError('Multiple Inheritance of HocObject by %s '
                 'not allowed.' % ', '.join(b.__name__ for b in m))

Rationale:
   (1) "if m == True" is always a flag for trouble.
   (2) When you detect a problem and raise an exception, provide
       information about what was wrong.
   (3) There is no need to report error conditions early.  Accumulate
       information and complain (if it can be done without much extra
       code) exhaustively because a failure case should not be in the
       performance-limiting section anyway.

> Here's a more general version of your testing code, to detect *any*
> diamond multiple inheritance (using your sample classes).
> 
> for cls in (A,B,C,D):
>     seen = set()
>     try:
>         bases = cls.__bases__
>         for b in bases:
>             if hasattr(b,"__mro__"):
>                 for m in b.__mro__:
>                     if m in seen:
>                         raise Exception("diamond multiple
> inheritance")
>                     seen.add(m)
>     except Exception, e:
>         print cls,"has diamond MI"
>     else:
>         print cls,"is ok"

Warning: any two "new-style" classes as parents are necessarily involved
in diamond inheritance (because object will be a common superclass).
I'd make sure you produce (or can produce) all common super-classes,
so you can filter the list to what you want.  Maybe something like this:
     def diamond_points(class_):
         seen = dict((b, set([class_])) for b in class_.__bases__)
         for b in class_.__bases__:
             if hasattr(b, "__mro__"):
                 for m in b.__mro__:
		    if b != m:
                         seen.setdefault(m, set()).add(b)
         for subclass, sources in seen.items():
             if len(sources) == 1:
                 seen.pop(subclass)
         return seen

Then you can do a test like:
     trouble = diamond_points(class_)
     if trouble and (len(trouble) > 1 or object not in trouble):
         # you know you have a problem
         # If you only have forbidden multi-roots, you could
         #  test if set(trouble) & forbidden_multi_parents: ...
         if object in trouble:
             trouble.pop(object)  # Avoid simply new-style classes
         for common_ancestor, progeny in trouble.items():
             print common_ancestor.__name__, 'used by:', ' & '.join(
                                     child.__name__ for child in progeny)


--Scott David Daniels
Scott.Daniels at Acm.Org



More information about the Python-list mailing list