[Python-Dev] PEP 246, redux

Alex Martelli aleax at aleax.it
Tue Jan 11 10:59:07 CET 2005


On 2005 Jan 10, at 19:34, Phillip J. Eby wrote:
    ...
> IMO it's more desirable to support abstract base classes than to allow 
> classes to "opt out" of inheritance when testing conformance to a base 
> class.  If you don't have an "is-a" relationship to your base class, 
> you should be using delegation, not inheritance.  (E.g. 'set' has-a 
> 'dict', not 'set' is-a 'dict', so 'adapt(set,dict)' should fail, at 
> least on the basis of isinstance checking.)

C++'s private inheritance explicitly acknowledges how HANDY subclassing 
can be for pure implementation purposes; we don't have private 
inheritance but that doesn't mean subclassing becomes any less handy;-)

> The other problem with a Liskov opt-out is that you have to explicitly 
> do a fair amount of work to create a LiskovViolation-raising subclass;

A TINY amount of work.  Specifically, starting from:

class X(Abstract):
     """ most of X omitted """
     def hook1(self):
         """ implement hook1 so as to get tm1 from Abstract """
         if self.foo() > self.bar():
             self.baz()
         else:
             self.fie = self.flup - self.flum
         return self.zap()

all you have to do is ADD
     def __conform__(self, protocol):
         if issubclass(protocol, Abstract):
             raise LiskovViolation

that's all.

(See my big post about what Abstract is, with template methods tm1 and 
tm2 respectively using hook methods hook1 and hook2: X doesn't 
implement hook2).

>  that work would be better spent migrating to use delegation instead 
> of inheritance, which would also be cleaner and more comprehensible 
> code than writing a __conform__ hack to announce your bad style in 
> having chosen to use inheritance where delegation is more appropriate. 
>  ;)

The amount of effort is MUCH vaster.  Essentially RECODE everything so 
s/thing like:

class X(object):
     """ most of X omitted """
     class PrivateAuxiliaryClass(Abstract):
         def __init__(self, x):
             self.x = x
         def hook1(self):
             return self.x.hook1()
     def __init__(self):
         self.pac = self.PrivateAuxiliaryClass(self)
         # rest of X.__init__ omitted
     def tm1(self):
         return self.pac.tm1()

this isn't just a tiny band-aid to say "I really wish the language had 
private inheritance because I'm using Abstract as a base just for code 
reuse" -- it's a rich and complex restructuring, and in fact it's just 
the beginning; now you have a deuced reference loop between each 
instance x of X, and its x.pac, so you'll probably want to pull in 
weakref, too, to avoid giving too much work to the cyclical garbage 
collector.

Basically, rephrasing private inheritance with containment and 
delegation is a lot of messy work, and results in far more complicated 
structures.  And instead of paying the tiny price of a __conform__ call 
at adaptation time, you pay the price of delegating calls over and over 
at each x.tm1() call, so it's unlikely performance will improve.

By pushing Liskov conformance without supporting "private inheritance" 
or its equivalent, you're really pushing people to use much more 
complicated and sophisticated structures of objects than "private 
inheritance" affords when properly used... and the LAST thing OO 
programmers need is any encouragement towards more complicated 
structures!-)


Alex



More information about the Python-Dev mailing list