Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Override a method but inherit the docstring

3,179 views
Skip to first unread message

Ben Finney

unread,
Jul 16, 2009, 9:01:49 PM7/16/09
to
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), 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

Jean-Paul Calderone

unread,
Jul 16, 2009, 9:13:34 PM7/16/09
to pytho...@python.org

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

Maxim Khitrov

unread,
Jul 16, 2009, 9:21:16 PM7/16/09
to pytho...@python.org
On Thu, Jul 16, 2009 at 9:13 PM, Jean-Paul Calderone<exa...@divmod.com> wrote:
> On Fri, 17 Jul 2009 11:01:49 +1000, Ben Finney <ben+p...@benfinney.id.au>
> wrote:
>>
> 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.

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

Rhodri James

unread,
Jul 16, 2009, 9:31:43 PM7/16/09
to pytho...@python.org
On Fri, 17 Jul 2009 02:01:49 +0100, Ben Finney
<ben+p...@benfinney.id.au> wrote:

> 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

Paul McGuire

unread,
Jul 16, 2009, 9:35:05 PM7/16/09
to
On Jul 16, 8:01 pm, Ben Finney <ben+pyt...@benfinney.id.au> wrote:
> 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.
>
> What is the most Pythonic, DRY-adherent, and preferably least-ugly
> approach to override a method, but have the same docstring on both
> methods?
>

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

Ben Finney

unread,
Jul 16, 2009, 10:58:48 PM7/16/09
to
Paul McGuire <pt...@austin.rr.com> writes:

> 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

Peter Otten

unread,
Jul 17, 2009, 2:58:40 AM7/17/09
to
Ben Finney wrote:

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

Steven D'Aprano

unread,
Jul 17, 2009, 3:52:19 AM7/17/09
to
On Fri, 17 Jul 2009 12:58:48 +1000, Ben Finney wrote:

>> 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

Ben Finney

unread,
Jul 17, 2009, 6:03:29 AM7/17/09
to
Peter Otten <__pet...@web.de> writes:

> 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

David Stanek

unread,
Jul 17, 2009, 8:08:48 AM7/17/09
to Peter Otten, pytho...@python.org

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

David Stanek

unread,
Jul 17, 2009, 8:21:39 AM7/17/09
to Steven D'Aprano, pytho...@python.org

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

Shai

unread,
Jul 23, 2009, 7:51:43 PM7/23/09
to
On Jul 17, 10:52 am, Steven D'Aprano <st...@REMOVE-THIS-

cybersource.com.au> wrote:
>
> 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".
>

... 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.

Aahz

unread,
Jul 26, 2009, 11:55:18 AM7/26/09
to
In article <056f629b-aa63-458a...@h11g2000yqb.googlegroups.com>,

Shai <sh...@platonix.com> wrote:
>
>class DocInherit(object):
> """
> Docstring inheriting method descriptor
>
> The class itself is also used as a decorator
> """

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

Jean-Michel Pichavant

unread,
Jul 27, 2009, 10:05:11 AM7/27/09
to Ben Finney, pytho...@python.org
I am using epydoc and if the docstring is present only in the baseclass
method, it will repeat the docstring for the child methods. So
basically, there's nothing to do.
I've also tried within the python interpreter, and it can perfectly
solve docstring inheritance. So why would you explicitly assign
docstring to child methods ?

JM

Shai

unread,
Jul 27, 2009, 5:15:00 PM7/27/09
to
On Jul 27, 5:05 pm, Jean-Michel Pichavant <jeanmic...@sequans.com>
wrote:

> Ben Finney wrote:
> >
> > 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.
>
> I've also tried within the python interpreter, and it can perfectly
> solve docstring inheritance. So why would you explicitly assign
> docstring to child methods ?
>

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.

Jean-Michel Pichavant

unread,
Jul 28, 2009, 5:33:43 AM7/28/09
to Shai, pytho...@python.org
Shai wrote:
> On Jul 27, 5:05 pm, Jean-Michel Pichavant <jeanmic...@sequans.com>
> wrote:
>
>> Ben Finney wrote:
>>
>>> 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.
>>>
>> I've also tried within the python interpreter, and it can perfectly
>> solve docstring inheritance. So why would you explicitly assign
>> docstring to child methods ?
>>
>>
>
> 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.
>
>
You're right. I must have made some dumb mistake. So interpreters do not
solve docstring inheritance, Epydoc does.

JM

Shai

unread,
Jul 28, 2009, 4:49:57 PM7/28/09
to
On Jul 26, 6:55 pm, a...@pythoncraft.com (Aahz) wrote:
>
> Nice!  Maybe stick this on the Cookbook?

http://code.activestate.com/recipes/576862/

Thanks for the suggestion,

Shai.

0 new messages