Override a method but inherit the docstring
Paul McGuire
ptmcg at austin.rr.com
Thu Jul 16 21:35:05 EDT 2009
On Jul 16, 8:01 pm, Ben Finney <ben+pyt... at 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
More information about the Python-list
mailing list