How about "pure virtual methods"?

Alex Martelli aleaxit at yahoo.com
Sun Dec 19 18:26:26 CET 2004


Fredrik Lundh <fredrik at pythonware.com> wrote:

> Alex Martelli:
> 
> >> what? Early warning, a few microseconds ahead of the invocation of a
> >> method which will cause the stub in the base class to raise an
> >> exception?
> >
> > Exactly.  Microseconds don't count, but stack levels do -- getting the
> > traceback end as close as possible to the real CAUSE of the problem,
> > rather than a few levels further down where the SYMPTOM appears, can
> > easily shave whole minutes off debugging time.
> 
> minutes?
> 
>     Traceback (most recent call last):
>     File "mymodule.py", line 9, in somefunction
>         someobj.bar()
>         ... zero or more lines ...
>     File "somelibrary.py", line 3, in bar
>         raise NotImplementedError("must implement abstract method bar")
>     NotImplementedError: must implement abstract method bar
> 
> you have to look at the last line of a traceback to find the error
> message, and the offending module/method is two lines above that.

The offending module is the one which instantiates a class it shouldn't
instantiate -- a class which isn't made concrete by overriding all
methods which need to be.  What is that module?  What is that class?
The error is generally not the calling of 'bar' on someobj: someobj
arrives as a argument to somefunction from somewhere -- the caller of
somefunction is quite often innocent, in turn -- who BUILT someobj, by
instantiating which class?  I think `minutes' is a very fair (not at all
pessimistic) assessment of the time it will generally take to find out.

With an exception at the time of instantiation, you shave those minutes
off.  As I already explained: if it was acceptable to raise at the time
the class statement is executed, which doesn't make the class fully
concrete yet fails to explicitly assert __abstract__=True in classbody,
the distance between where the error is in the code, and where it's
diagnosed, could be further reduced in many cases; sometimes the error
is to mistakenly instantiate a class which is not yet meant to be fully
concrete, but equally well it could be to fail to override all needed
methods in a class which IS meant to be fully concrete.  However, this
further refinement turns out to be generally of lesser impact wrt the
primary one: as long as the error msg at instantiation time fully
identifies what class is involved and why (because of what still
abstract methods) it's an error to instantiate it, then the productivity
plus of getting the error at class-statement time vs instantiation time
is generally no big deal -- the big deal is not having to wait until,
somewhere, somehow, a still-abstract method finally gets _called_, and
then having to trace back to how and where that class was instantiated.

 
> > So, do _I_ use this?  Nah: I think abstract classes with
> > explicitly-abstract "somebody's gotta implement this!" methods are
> > rarely a good idea
> 
> yet you claim to be an expert on issues related to debugging NotImplemented-
> Error exceptions?

Yes, the body of experience which essentially convinced me to avoid the
above idiom (because, as I say in the snippet I quote here, it's rarely
a good idea -- you snipped my summary of reasons why) essentially
overlaps with the body of experience which suggests to me that IF
somebody's keen to use the idiom widely anyway, they're going to be far
better off with the errors it's meant to catch causing errors to be
raised ASAP, as the custom metaclass so easily allows, rather than as
late as possible, as using 'raise NotImplementedError' leads to.

[[ Protocols (also unfortunately known as interfaces) and adaptation
work much better, essentially by not depending on inheritance. ]]

I really don't understand what's so difficult here, for you, as to make
it apparently so very hard for you to follow.  The idiom (meant to catch
some wrong uses of inheritance) of 'abstract classes with certain
methods that need to get overridden' isn't all that helpful in Python
when all that happens is a NotImplementedError exception.  If the base
class had simply omitted defining the method, as used to be the idiom,
you'd generally get an AttributeError instead -- slightly more generic
but happening basically at the same spot in the code, time difference in
finding the errors usually a few seconds.  With a custom metaclass (and
optionally a decorator, though there are other ways, which we can
discuss in detail if you think such distinctions are oh so crucial) you
can easily make the idiom more helpful for the purpose of diagnosing
errors in inheritance &c -- i.e., the idiom's sole purpose.

Nevertheless, even when one has made the idiom as good as it can be, I
think it's still rarely a good idea.  Protocols and adaptation take a
fresh approach, not as heavily influenced by C++ and Java limitations as
the idiom we're discussing here, and it's my contention that said
approach does a better job.  One can perfectly well still use
inheritance for what inheritance does best (easy, simple, fast sharing
of some implementation) without depending on it for those protocol
compliance issues which were never its forte.


Alex



More information about the Python-list mailing list