The following is a common idiom::
class FooGonk(object):
def frobnicate(self):
""" Frobnicate this gonk. """
basic_implementation(self.wobble)
class BarGonk(FooGonk):
def frobnicate(self):
special_implementation(self.warble)
The docstring for ‘FooGonk.frobnicate’ is, intentionally, perfectly
applicable to the ‘BarGonk.frobnicate’ method also. Yet in overriding
the method, the original docstring is not associated with it.
Ideally there would be a way to specify that the docstring should be
inherited. The best I can come up with is::
class BarGonk(FooGonk):
def frobnicate(self):
special_implementation(self.warble)
frobnicate.__doc__ = FooGonk.frobnicate.__doc__
but that violates DRY (the association between BarGonk and FooGonk is
being repeated), puts the docstring assignment awkwardly after the end
of the method instead of at the beginning where docstrings normally go,
and reads poorly besides.
What is the most Pythonic, DRY-adherent, and preferably least-ugly
approach to override a method, but have the same docstring on both
methods?
--
\ “Why, I'd horse-whip you if I had a horse.” —Groucho Marx |
`\ |
_o__) |
Ben Finney
How about this?
class BarGonk(FooGonk):
@inherit_docstring
def frobnicate(self):
special_implementation(self.warble)
The implementation of "inherit_docstring" is left as an exercise for the
reader (it's not utterly trivial, I admit, as "FooGonk" will not readily
be at hand, but it is still possible).
By the way, I don't think this is a particularly good idea. Presumably
there is a reason your implementation is special. It would probably be
better if this were reflected in the docstring somehow. Perhaps this
idea is a better one:
class BarGonk(FooGonk):
@append_to_docstring
def frobnicate(self):
"""
This implementation takes the warble into consideration.
"""
special_implementation(self.warble)
With the result of BarGonk.frobnicate.__doc__ being set to:
Frobnicate this gonk.
This implementation takes the warble into consideration.
Jean-Paul
Another way is to use a metaclass. Have its __new__ method loop
through all attributes and compare those with what is already defined
in bases. If you find a match, copy the __doc__ attribute. The
advantage here is that it will work for all methods without any
additional code, not counting the "__metaclass__ = ..." line. If you
define a metaclass for the base, then no modifications are required
for any subclasses. I do agree, however, that the best thing to do is
to write a very short explanation for what the override is for.
- Max
> Howdy all,
>
> The following is a common idiom::
>
> class FooGonk(object):
> def frobnicate(self):
> """ Frobnicate this gonk. """
> basic_implementation(self.wobble)
>
> class BarGonk(FooGonk):
> def frobnicate(self):
> special_implementation(self.warble)
>
> The docstring for ‘FooGonk.frobnicate’ is, intentionally, perfectly
> applicable to the ‘BarGonk.frobnicate’ method also. Yet in overriding
> the method, the original docstring is not associated with it.
>
> Ideally there would be a way to specify that the docstring should be
> inherited. The best I can come up with is::
>
> class BarGonk(FooGonk):
> def frobnicate(self):
> special_implementation(self.warble)
> frobnicate.__doc__ = FooGonk.frobnicate.__doc__
>
> but that violates DRY (the association between BarGonk and FooGonk is
> being repeated),
Not really. Consider the case of BarGonk being a subclass of FooGonk
and BazGonk; which docstring would you wish to inherit?
> puts the docstring assignment awkwardly after the end
> of the method instead of at the beginning where docstrings normally go,
> and reads poorly besides.
Sounds like a job for a decorator!
(This is probably unbelievably ugly and unwise, since I don't use
decorators at all often.)
def copydoc(cls):
def _fn(fn):
if fn.__name__ in cls.__dict__:
fn.__doc__ = cls.__dict__[fn.__name__].__doc__
return fn
return _fn
class BarGonk(FooGonk):
@copydoc(FooGonk)
def frobnicate(self):
special_implementation(self.warble)
--
Rhodri James *-* Wildebeest Herder to the Masses
Two ideas come to mind, the decorator way and the metaclass way. I am
not a guru at either, but these two examples work:
# the decorator way
def inherit_docstring_from(cls):
def docstring_inheriting_decorator(fn):
fn.__doc__ = getattr(cls,fn.__name__).__doc__
return fn
return docstring_inheriting_decorator
class FooGonk(object):
def frobnicate(self):
""" Frobnicate this gonk. """
basic_implementation(self.wobble)
class BarGonk(FooGonk):
@inherit_docstring_from(FooGonk)
def frobnicate(self):
special_implementation(self.warble)
bg = BarGonk()
help(bg.frobnicate)
Prints:
Help on method frobnicate in module __main__:
frobnicate(self) method of __main__.BarGonk instance
Frobnicate this gonk.
Using a decorator in this manner requires repeating the super class
name. Perhaps there is a way to get the bases of BarGonk, but I don't
think so, because at the time that the decorator is called, BarGonk is
not yet fully defined.
# The metaclass way
from types import FunctionType
class DocStringInheritor(type):
def __new__(meta, classname, bases, classDict):
newClassDict = {}
for attributeName, attribute in classDict.items():
if type(attribute) == FunctionType:
# look through bases for matching function by name
for baseclass in bases:
if hasattr(baseclass, attributeName):
basefn = getattr(baseclass,attributeName)
if basefn.__doc__:
attribute.__doc__ = basefn.__doc__
break
newClassDict[attributeName] = attribute
return type.__new__(meta, classname, bases, newClassDict)
class FooGonk2(object):
def frobnicate(self):
""" Frobnicate this gonk. """
basic_implementation(self.wobble)
class BarGonk2(FooGonk2):
__metaclass__ = DocStringInheritor
def frobnicate(self):
special_implementation(self.warble)
bg = BarGonk2()
help(bg.frobnicate)
Prints:
Help on method frobnicate in module __main__:
frobnicate(self) method of __main__.BarGonk2 instance
Frobnicate this gonk.
This metaclass will walk the list of bases until the desired
superclass method is found AND if that method has a docstring and only
THEN does it attach the superdocstring to the derived class method.
Please use carefully, I just did the metaclass thing by following
Michael Foord's Metaclass tutorial (http://www.voidspace.org.uk/python/
articles/metaclasses.shtml), I may have missed a step or two.
-- Paul
> Two ideas come to mind, the decorator way and the metaclass way. I am
> not a guru at either, but these two examples work:
I think the decorator idea is most attractive to me, since it can be
applied per method.
> # the decorator way
> def inherit_docstring_from(cls):
> def docstring_inheriting_decorator(fn):
> fn.__doc__ = getattr(cls,fn.__name__).__doc__
> return fn
> return docstring_inheriting_decorator
That works, thank you.
> Using a decorator in this manner requires repeating the super class
> name. Perhaps there is a way to get the bases of BarGonk, but I don't
> think so, because at the time that the decorator is called, BarGonk is
> not yet fully defined.
Yes, I tried a few different ways, but within the decorator it seems the
function object is quite unaware of what class it is destined for.
--
\ “We are human only to the extent that our ideas remain humane.” |
`\ —_Breakfast of Champions_, Kurt Vonnegut |
_o__) |
Ben Finney
Just thinking aloud: Write a patch for pydoc that looks up the base-class
documentation.
B.f.__doc__ will continue to return None, but
help(B.f) will show something like
No documentation available for B.f.
Help for A.f:
yadda yadda
Of course that might be misleading when A.f and B.f are up to something
completely different...
Peter
>> Using a decorator in this manner requires repeating the super class
>> name. Perhaps there is a way to get the bases of BarGonk, but I don't
>> think so, because at the time that the decorator is called, BarGonk is
>> not yet fully defined.
>
> Yes, I tried a few different ways, but within the decorator it seems the
> function object is quite unaware of what class it is destined for.
When the decorator is called, the function object is just a function
object, not a method, so there is no concept of "what class it is
destined for".
>>> def dec(func):
... print type(func)
... try:
... print func.im_class
... except:
... print "no im_class"
... return func
...
>>> class Test(object):
... @dec
... def spam(self):
... pass
...
<type 'function'>
no im_class
>>> type(Test.spam)
<type 'instancemethod'>
>>> Test.spam.im_class
<class '__main__.Test'>
I suppose you could try to determine what namespace you're currently when
the class is created, but that's surely going to be fragile and messy.
--
Steven
> Just thinking aloud: Write a patch for pydoc that looks up the
> base-class documentation.
That doesn't scale; I want the docstring to be discovered by the normal
interface (the ‘__doc__’ attribute) by *every* tool that gathers
docstrings from methods.
Also, I want to have this behaviour not for every method missing a
docstring, but only for selected methods.
> Of course that might be misleading when A.f and B.f are up to
> something completely different...
Exactly.
--
\ “Facts do not cease to exist because they are ignored.” —Aldous |
`\ Huxley |
_o__) |
Ben Finney
This should never be the case. It violates LSP and would be very confusing to
readers of the code.
--
David
blog: http://www.traceback.org
twitter: http://twitter.com/dstanek
It isn't too bad. I got the idea to use the method's enclosing
scope[1] in my decorator[2] from DecoratorTools. I am working to
remove it because it make me sad, but it does work.
[1] http://code.google.com/p/snake-guice/source/browse/snakeguice/decorators.py#51
[2] http://code.google.com/p/snake-guice/source/browse/snakeguice/decorators.py#58
... which points to the better solution: use a descriptor. With the
doc_inherit decorator defined below, one may write
class Foo(object):
def foo(self):
"Frobber"
pass
class Bar(Foo):
@doc_inherit
def foo(self):
pass
and it appears to work. The code below is a little longish because we
need to do slightly different things when called for a class and for
an instance. But there's no need to repeat the parent name, no need
to look into namespaces (which, as you said, is probably messy and
fragile), and it seems pretty readable, too.
from functools import wraps
class DocInherit(object):
"""
Docstring inheriting method descriptor
The class itself is also used as a decorator
"""
def __init__(self, mthd):
self.mthd = mthd
self.name = mthd.__name__
def __get__(self, obj, cls):
if obj:
return self.get_with_inst(obj, cls)
else:
return self.get_no_inst(cls)
def get_with_inst(self, obj, cls):
overridden = getattr(super(cls, obj), self.name, None)
@wraps(self.mthd, assigned=('__name__','__module__'))
def f(*args, **kwargs):
return self.mthd(obj, *args, **kwargs)
return self.use_parent_doc(f, overridden)
def get_no_inst(self, cls):
for parent in cls.__mro__[1:]:
overridden = getattr(parent, self.name, None)
if overridden: break
@wraps(self.mthd, assigned=('__name__','__module__'))
def f(*args, **kwargs):
return self.mthd(*args, **kwargs)
return self.use_parent_doc(f, overridden)
def use_parent_doc(self, func, source):
if source is None:
raise NameError, ("Can't find '%s' in parents"%self.name)
func.__doc__ = source.__doc__
return func
doc_inherit = DocInherit
Combining docstrings (as suggested by Jean-Paul Calderone), and
taking proper care of classmethods and staticmethods, are left
as an exercise to the reader.
Have fun,
Shai.
Nice! Maybe stick this on the Cookbook?
--
Aahz (aa...@pythoncraft.com) <*> http://www.pythoncraft.com/
"At Resolver we've found it useful to short-circuit any doubt and just
refer to comments in code as 'lies'. :-)"
--Michael Foord paraphrases Christian Muirhead on python-dev, 2009-03-22
JM
What do you mean by "can perfectly solve docstring inheritance" ?
After the following,
class Foo(object):
def foo(self):
"Frobber"
pass
class Bar(Foo):
def foo(self):
pass
help(Bar.foo) does not display "Frobber" on my interpreter.
JM