[Doc-SIG] suggestions for a PEP
Guido van Rossum
guido@digicool.com
Sun, 18 Mar 2001 12:12:15 -0500
> Thank you Guido.
> That makes sense.
> Not naughty after all ;^)
> > The ExtensionClass module in Zope actually implements class-like
> > objects that behave in such a way that at least the first example
> > (D.f.foo = 2) changes the f.foo value for class D but not for class C.
> > So this is not just theoretical!
> see, I knew it'd be the sort of think Jim Fulton plays with.
> (OK, `think' was a typo: but it deserves to survive ;^)
> Which encourages hope.
>
> First off, I'm going to disparage your second example:
> > class C:
> > def f(self): pass
> > f.foo = 1
> >
> > x = C()
> > x.f.foo = 2 # Would also change value of C.f.foo!
>
> This is strictly another matter: x.f isn't really the same thing as C.f,
> it `should' be currie(C.f, x), if you see what I mean, hence a different
> object from C.f, so setting its .foo shouldn't affect C.f, even with the
> old semantics (even assuming one's allowed to setattr on a bound method,
> which sounds *very* dodgy to me). So I would be rude enough to call
> this example a bug in pre-2.1b1 python and ask that it be left out of
> the discussion ;^>
Sure!
> But the `D.f is C.f' problem is real.
> So, on the one hand, when a method is inherited,
>
> > class C:
> > def f(self): pass
> > f.foo = 1
> >
> > class D(C):
> > pass
> > D.f.foo = 2 # Would change value of C.f.foo!
>
> but, at the same time, if the derived class *does* over-ride the method,
>
> >>> class A:
> ... def f(self): """doc string of A.f"""
> ...
> >>> class B(A):
> ... def f(self): return
> ...
> >>> B.f.__doc__ # B.f wishes it would `inherit' from A.f, but doesn't
> >>>
>
> Now any attempt at getting the latter desideratum, without the former
> naughtiness, is going to be sophisticated: but can it be done ? Clearly
> there *is* a sophisticated munging phase when B's namespace, having been
> built by execution of B's suite, gets transformed and packaged so that
> B.f is no longer simply a function; is it possible, at that juncture, to
> arrange that it will borrow __doc__ off A.f ?
> (Only if B.f lacks __doc__, naturally.)
Hm, this is entering a whole realm of stuff where Python isn't very
helpful. Folks who know Eiffel have suggested inheriting pre- and
post-conditions. Others, coming from C++, have suggested automatic
calling of base class constructors in derived constructors. Now you
suggest inheriting docstrings.
Maybe there's something there, but it's definitely Python 3000
material...
> This would involve some added magic in the type of unbound methods but
> that's a pretty magical type *anyway* and it *looks* like it should be
> feasible by applying games similar to (though hopefully less complex
> than) those used by ExtensionClass.
>
> A derived class' re-implementation `should' behave like that of the
> base, or it abrogates its ADT,
Yes, but Python doesn't really try to enforce that (or even help you).
> so having the same doc as the method
> being over-ridde should be `usual'.
Unclear. It depends a lot on what's in the docstring. I have written
lots of docstrings that would be really misleading if they were
inherited!
> The exceptions incur a tiny cost -
> they have to supply a doc string, which can be empty if they want, but
> really they *should* be explaining why they abrogate the ADT anyway,
> given that the base class's other methods might exercise the replacement
> - and, without this borrowing, the usual case implies gratuitous
> duplication - either of the doc string or of the assignment from base.
> The latter is really ugly - I should not have to type __doc__ in any
> ordinary piece of code; only in introspectors.
>
> Note (for anyone who missed it) that Tony discovered one needn't go via
> im_func, as long as one assigns *before* B's namespace gets munged:
>
> >>> class E(A):
> ... def f(self): pass
> ... f.__doc__ = A.f.__doc__
> ...
> >>> E.f.__doc__
> 'doc string of A.f'
>
> This is because f is still an ordinary function object (in particular,
> it isn't yet E.f; E doesn't yet exist) when the assignment happened.
f is still an ordinary docstring even after the class definition is
complete -- but if you access it in the conventional way (as E.f) it
is munged on the way out. E.__dict__['f'] also gives the function.
(Not that I encourage using this!)
> There is, of course, an obvious problem: multiple inheritance, when more
> than one base supplies the over-ridden method. The solution is, of
> course, to borrow off the method on the earliest of the bases to provide
> it and leave the implementor to over-ride that by assigning if they
> must. This will be rare enough not to be an issue; and it will simply
> work, because either
>
> * you assign as above, in which case E.f had a __doc__ before munging,
> so borrowing off E's bases' .f didn't get invoked; or
>
> * you assign after the class body, via im_func, in which case you
> over-ride what the munging has done and it still works.
>
> How's the time machine doing ?
> Do methods yet `inherit' __doc__ when not over-ridden ?
>
> Eddy.
For this one, I prefer to use the time machine in the opposite
direction. Let's move this set of ideas to a new design for Py3K.
(PS, I regret that this is off-topic for doc-sig -- it should really
be moved to python-dev.)
--Guido van Rossum (home page: http://www.python.org/~guido/)