[Python-Dev] PEP 246, redux (original) (raw)

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