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

Inheriting methods but over-riding docstrings

5 views
Skip to first unread message

Steven D'Aprano

unread,
Jan 16, 2010, 12:55:11 PM1/16/10
to
I have a series of subclasses that inherit methods from a base class, but
I'd like them to have their own individual docstrings. The obvious
solution (other than copy-and-paste) is this:


class Base(object):
colour = "Blue"
def parrot(self):
"""docstring for Base"""
return "Norwegian %s" % self.colour


class SubClass(Base):
colour = "Red"
def parrot(self):
"""docstring for Subclass"""
return super(Subclass, self).parrot()


but that adds an awful lot of boilerplate to my subclasses. Are there any
other good solutions to this problem?

--
Steven

Gerard Flanagan

unread,
Jan 16, 2010, 2:02:47 PM1/16/10
to pytho...@python.org

If I've understood, one idea is:

------------------------------
def type_factory(colour):

class Base(object):

def parrot(self):
"""Norwegian %s"""
return 1
parrot.__doc__ %= colour

return Base

class Base(object):

def __new__(cls, *args, **kw):
return type_factory(cls.colour)(*args, **kw)


class A(Base):
colour = "Blue"

class B(Base):
colour = "Red"

a = A()
b = B()

print inspect.getdoc(a.parrot)
Norwegian Blue
print inspect.getdoc(b.parrot)
Norwegian Red
----------------------------------------------

In the more general case, (ie. where you don't know that there is a
method called parrot and an attribute called colour), I imagine you
could do the same thing but at the metaclass level.

HTH

G.F.

Gabriel Genellina

unread,
Jan 17, 2010, 1:02:23 PM1/17/10
to pytho...@python.org
En Sat, 16 Jan 2010 14:55:11 -0300, Steven D'Aprano
<st...@remove-this-cybersource.com.au> escribiᅵ:

Methods don't have docstrings; functions do. So one has to "clone" the
function to set a new docstring.

<code>
def copy_function(fn, docstring):
fn = getattr(fn, "im_func", fn) # accomodate unbound methods in 2.x
function_type = type(lambda:0)
newfn = function_type(fn.__code__, fn.__globals__, fn.__name__,
fn.__defaults__, fn.__closure__)
newfn.__doc__ = docstring
return newfn

class Base(object):
colour = "Blue"
def parrot(self):
"""docstring for Base"""
return "Norwegian %s" % self.colour

class SubClass(Base):
colour = "Red"

parrot = copy_function(Base.parrot, "docstring for Subclass")

</code>

py> x = Base()
py> print(x.parrot())
Norwegian Blue
py> print x.parrot.__doc__
docstring for Base
py> y = SubClass()
py> print(y.parrot())
Norwegian Red
py> print y.parrot.__doc__
docstring for Subclass

--
Gabriel Genellina

Alf P. Steinbach

unread,
Jan 17, 2010, 7:45:17 PM1/17/10
to
* Gabriel Genellina:

Since Steven says he has a series of subclasses, perhaps wrapping those function
calls in a decorator?


<code>
def copy_function(fn, docstring):
# Gabriel Genellina's copy_function


fn = getattr(fn, "im_func", fn) # accomodate unbound methods in 2.x
function_type = type(lambda:0)
newfn = function_type(fn.__code__, fn.__globals__, fn.__name__,
fn.__defaults__, fn.__closure__)
newfn.__doc__ = docstring
return newfn

def docstrings(**kwargs):
def with_altered_docstrings(cls, kwargs=kwargs):
for kwarg in kwargs.items():
func_name = kwarg[0]
docstring = kwarg[1]
original_func = getattr(cls, func_name)
new_func = copy_function(getattr(cls, func_name), docstring)
setattr(cls, func_name, new_func)
return cls
return with_altered_docstrings


class Base(object):
colour = "Blue"
def parrot(self):
"""docstring for Base"""
return "Norwegian %s" % self.colour

@docstrings( parrot = "docstring for Subclass" )


class SubClass(Base):
colour = "Red"

</code>


This is my first decorator ever so it might be Incorect and nonIdiomatic...

Also, neither 'copy_function' nor 'docstrings' seem to work with slots.


Steven: on a personal note, earlier when I saw you (I think it was you) using
the "Norwegian Parrot" example I thought it referred to me because that was the
only sense I could make of it, it followed right after some discussion we had.
Thus my impression of you or or responses in this group was colored by a false
interpretation. But, checking, which is often a good idea!, and *which I should
have done then*, as far as I can see the term was first used in this group in
April 2001, <url:
http://groups.google.com/group/comp.lang.python/browse_thread/thread/12a125ceddd401e2/c021547a1dc14a41>.

It's still a mystery to me what it refers to, though... :-)


Cheers & hth.,

- Alf

Steve Holden

unread,
Jan 17, 2010, 9:23:45 PM1/17/10
to pytho...@python.org
Gabriel Genellina wrote:
> En Sat, 16 Jan 2010 14:55:11 -0300, Steven D'Aprano
> <st...@remove-this-cybersource.com.au> escribiᅵ:
>
> Methods don't have docstrings; functions do. So one has to "clone" the
> function to set a new docstring.
>
On behalf of all methods I would like to say "We demand the right to
docstrings".

>>> print abs.__doc__
abs(number) -> number

Return the absolute value of the argument.
>>>

Perhaps I am misunderstanding "don't have docstrings". This code:

>>> class TheHellOfIt:
... """Classes, of course, as first-class citizens and have
docstrings."""
... def method(self):
... """Discrimination against methods, I call it."""
... pass
...
>>> print TheHellOfIt.method.__doc__
Discrimination against methods, I call it.
>>> import sys
>>> help(sys.modules["__main__"])

Gives:

Help on built-in module __main__:

NAME
__main__

FILE
(built-in)

CLASSES
TheHellOfIt

class TheHellOfIt
| Classes, of course, as first-class citizens and have docstrings.
|
| Methods defined here:
|
| method(self)
| Discrimination against methods, I call it.

So, are the demands of the methods reasonable, or do they already have
what they are so loudly demanding?

regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
PyCon is coming! Atlanta, Feb 2010 http://us.pycon.org/
Holden Web LLC http://www.holdenweb.com/
UPCOMING EVENTS: http://holdenweb.eventbrite.com/

J

unread,
Jan 17, 2010, 9:28:03 PM1/17/10
to Alf P. Steinbach, pytho...@python.org
On Sun, Jan 17, 2010 at 19:45, Alf P. Steinbach <al...@start.no> wrote:
> Steven: on a personal note, earlier when I saw you (I think it was you)
> using the "Norwegian Parrot" example I thought it referred to me because
> that was the only sense I could make of it, it followed right after some
> discussion we had. Thus my impression of you or or responses in this group
> was colored by a false interpretation. But, checking, which is often a good
> idea!, and *which I should have done then*, as far as I can see the term was
> first used in this group in April 2001, <url:
> http://groups.google.com/group/comp.lang.python/browse_thread/thread/12a125ceddd401e2/c021547a1dc14a41>.
>
> It's still a mystery to me what it refers to, though... :-)

I can't really comment on decorators and subclassing yet (still a
newb) but I am finding this discussion very informative... and lo
there IS something I can comment on.

The Norwegian Blue is a remarkable bird. Beautiful plumage! Though
they do like to lie around on their backs a bit, and seem to spend
their time either resting, or pining for the fjords. Yes lovely
plumage, indeed!

Google the remarkable Norwegian Blue Parrot and you'll fall in love as
I did so long ago :)

--

Marie von Ebner-Eschenbach - "Even a stopped clock is right twice a
day." - http://www.brainyquote.com/quotes/authors/m/marie_von_ebnereschenbac.html

Steven D'Aprano

unread,
Jan 18, 2010, 12:34:59 AM1/18/10
to
On Mon, 18 Jan 2010 01:45:17 +0100, Alf P. Steinbach wrote:

> Steven: on a personal note, earlier when I saw you (I think it was you)
> using the "Norwegian Parrot" example I thought it referred to me because
> that was the only sense I could make of it, it followed right after some
> discussion we had. Thus my impression of you or or responses in this
> group was colored by a false interpretation. But, checking, which is
> often a good idea!, and *which I should have done then*, as far as I can
> see the term was first used in this group in April 2001, <url:
> http://groups.google.com/group/comp.lang.python/browse_thread/
thread/12a125ceddd401e2/c021547a1dc14a41>.
>
> It's still a mystery to me what it refers to, though... :-)


It refers to the famous Monty Python "Dead Parrot Sketch", involving a
Norwegian Blue parrot that definitely isn't dead but merely pining for
the fjords.

http://www.mtholyoke.edu/~ebarnes/python/dead-parrot.htm

http://en.wikipedia.org/wiki/Dead_Parrot


No doubt you'll be able to find it on Youtube.


A little-known FAQ is that Python is named for Monty Python, not the
snake, and that traditionally metasyntactic variables such as foo, bar
etc. are frequently named after Monty Python sketches. E.g. I will
frequently reference the Cheeseshop sketch, the Spanish Inquisition,
Ethel the Aardvark, Spam (the lunch meat, not the email), and similar.

E.g. instead of foo, bar, baz, we frequently use spam, ham, eggs.


http://www.python.org/doc/faq/general/#why-is-it-called-python


--
Steven

Gabriel Genellina

unread,
Jan 18, 2010, 9:11:46 AM1/18/10
to pytho...@python.org
En Sun, 17 Jan 2010 23:23:45 -0300, Steve Holden <st...@holdenweb.com>
escribi�:
> Gabriel Genellina wrote:

>> Methods don't have docstrings; functions do. So one has to "clone" the
>> function to set a new docstring.
>>
> On behalf of all methods I would like to say "We demand the right to
> docstrings".
>
>>>> print abs.__doc__
> abs(number) -> number
>
> Return the absolute value of the argument.
>>>>
>
> Perhaps I am misunderstanding "don't have docstrings". This code:

Sorry, I wasn't clear at all. What I wanted to say is that docstrings are
actually stored as *function* attributes; methods inherit the docstring
from the function they are built on (as a read-only attribute).


py> class Base(object):
... def method(self):
... "my docstring"
...
py> Base.method.__doc__
'my docstring'
py> Base.method.__doc__ = "another docstring"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: attribute '__doc__' of 'instancemethod' objects is not
writable
py> Base.method.im_func.__doc__
'my docstring'
py> Base.method.im_func.__doc__ = "another docstring"
py> Base.method.__doc__
'another docstring'


So, if you want Base.foo and Derived.foo to share the same behavior but
have separate docstrings (what Steven asked for), you have to create
separate functions for both. That's what my already posted code does.

> So, are the demands of the methods reasonable, or do they already have
> what they are so loudly demanding?

Well, they could demand the right to have a docstring of their own. Since
3.x eliminated a whole category of methods (unbound methods are gone),
their ability to press for demands was greatly reduced. I don't think they
could get such right granted in the near future...

--
Gabriel Genellina

Jean-Michel Pichavant

unread,
Jan 18, 2010, 12:54:06 PM1/18/10
to pytho...@python.org

>>> class SubClass(Base):
>>> colour = "Red"
>>> def parrot(self):
>>> """docstring for Subclass"""
>>> return super(Subclass, self).parrot()
I'm not a big fan of super, but I'm still wondering if

>>> return super(self.__class__, self).parrot()

would have made it.

What if Subclass has more than one base class ?


JM

Duncan Booth

unread,
Jan 18, 2010, 1:11:14 PM1/18/10
to
Jean-Michel Pichavant <jeanm...@sequans.com> wrote:

>
>>>> class SubClass(Base):
>>>> colour = "Red"
>>>> def parrot(self):
>>>> """docstring for Subclass"""
>>>> return super(Subclass, self).parrot()
> I'm not a big fan of super, but I'm still wondering if
>
> >>> return super(self.__class__, self).parrot()
>
> would have made it.

No, it wouldn't.

> What if Subclass has more than one base class ?

super() will work in that case provided you specify the current class. It
never works correctly if you specify the class of self.

Consider another level of subclasses and it may be clearer:

>>> class Base(object):
def parrot(self):
print "Base.parrot"


>>> class SubClass(Base):
def parrot(self):
print "SubClass.parrot"
return super(SubClass, self).parrot()


>>> class SubSubClass(SubClass):
def parrot(self):
print "SubSubClass.parrot"
return super(SubSubClass, self).parrot()


>>> SubSubClass().parrot()
SubSubClass.parrot
SubClass.parrot
Base.parrot

>>> class SubClass(Base):
def parrot(self):
print "SubClass.parrot"
return super(self.__class__, self).parrot()


>>> class SubSubClass(SubClass):
def parrot(self):
print "SubSubClass.parrot"
return super(self.__class__, self).parrot()

>>> SubSubClass().parrot()
SubClass.parrot
SubClass.parrot
SubClass.parrot
SubClass.parrot
SubClass.parrot
SubClass.parrot
SubClass.parrot
SubClass.parrot
SubClass.parrot
... (you're in an infinite loop now) ...

Jean-Michel Pichavant

unread,
Jan 18, 2010, 2:50:45 PM1/18/10
to duncan...@suttoncourtenay.org.uk, pytho...@python.org
I see.

Then is there a reason why
>>> return super(Subclass, self).parrot()
would be prefered over the "classic"
>>> return Base.parrot(self)
?

Or is it just a matter of preference ?

JM

Arnaud Delobelle

unread,
Jan 18, 2010, 3:19:40 PM1/18/10
to
Jean-Michel Pichavant <jeanm...@sequans.com> writes:

[...]


> Then is there a reason why
>>>> return super(Subclass, self).parrot()
> would be prefered over the "classic"
>>>> return Base.parrot(self)
> ?
>
> Or is it just a matter of preference ?

Using super() calls the next method in the class's Method Resolution
Order (mro - to see it on class A, use A.mro()). This guarantees that
ancestor methods won't be called twice when the inheritance graph is not
a tree. Here is a (minimal?) example.

>>> class A(object):
... def foo(self): print 'A'
...
>>> class B(A):
... def foo(self):
... super(B, self).foo()
... print 'B'
...
>>> class C(A):
... def foo(self):
... super(C, self).foo()
... print 'C'
...
>>> class D(B, C):
... def foo(self):
... super(D, self).foo()
... print 'D'
...
>>> d = D()
>>> d.foo()
A
C
B
D
>>> D.mro()
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>]

The reason why super() may be necessary is that if an object is an
instance of say class C, its method resolution order above class C is
not known at compile time.

HTH

--
Arnaud

Gabriel Genellina

unread,
Jan 18, 2010, 7:57:15 PM1/18/10
to pytho...@python.org
En Mon, 18 Jan 2010 16:50:45 -0300, Jean-Michel Pichavant
<jeanm...@sequans.com> escribi�:

> Duncan Booth wrote:
>> Jean-Michel Pichavant <jeanm...@sequans.com> wrote:
>>
>>
>>>>>> class SubClass(Base):
>>>>>> colour = "Red"
>>>>>> def parrot(self):
>>>>>> """docstring for Subclass"""
>>>>>> return super(Subclass, self).parrot()
>>>>>>
>>> I'm not a big fan of super, but I'm still wondering if

>>> return super(self.__class__, self).parrot()

>>> would have made it.
>>
>> No, it wouldn't. [...]
>>
> I see.


> Then is there a reason why
> return super(Subclass, self).parrot()
> would be prefered over the "classic"
> return Base.parrot(self)
> ?
> Or is it just a matter of preference ?

For a longer explanation, see:

James Knight: Python's Super Considered Harmful
http://fuhm.net/super-harmful/

Michele Simionato: Things to Know About Python Super
http://www.artima.com/weblogs/viewpost.jsp?thread=236275
http://www.artima.com/weblogs/viewpost.jsp?thread=236278
http://www.artima.com/weblogs/viewpost.jsp?thread=237121

--
Gabriel Genellina

Michele Simionato

unread,
Jan 19, 2010, 6:44:09 AM1/19/10
to
On Jan 16, 6:55 pm, Steven D'Aprano <st...@REMOVE-THIS-

cybersource.com.au> wrote:
> I have a series of subclasses that inherit methods from a base class, but
> I'd like them to have their own individual docstrings.

The following is not tested more than you see and will not work for
builtin methods, but it should work in the common cases:

from types import FunctionType, CodeType

def newfunc(func, docstring):
c = func.func_code
nc = CodeType(c.co_argcount, c.co_nlocals, c.co_stacksize,
c.co_flags, c.co_code, c.co_consts, c.co_names,
c.co_varnames, c.co_filename, func.__name__,
c.co_firstlineno, c.co_lnotab, c.co_freevars,
c.co_cellvars)
nf = FunctionType(nc, func.func_globals, func.__name__)
nf.__doc__ = docstring
return nf

def setdocstring(method, docstring):
cls = method.im_class
basefunc = getattr(super(cls, cls), method.__name__).im_func
setattr(cls, method.__name__, newfunc(basefunc, docstring))


# example of use

class B(object):
def m(self):
"base"
return 'ok'

class C(B):
pass

setdocstring(C.m, 'C.m docstring')

print B.m.__doc__ # the base docstring
print C.m.__doc__ # the new docstring

Jean-Michel Pichavant

unread,
Jan 19, 2010, 7:33:54 AM1/19/10
to pytho...@python.org
Gabriel Genellina wrote:
>> I see.
>> Then is there a reason why
>> return super(Subclass, self).parrot()
>> would be prefered over the "classic"
>> return Base.parrot(self)
>> ?
>> Or is it just a matter of preference ?
>
> For a longer explanation, see:
>
> James Knight: Python's Super Considered Harmful
> http://fuhm.net/super-harmful/
>
> Michele Simionato: Things to Know About Python Super
> http://www.artima.com/weblogs/viewpost.jsp?thread=236275
> http://www.artima.com/weblogs/viewpost.jsp?thread=236278
> http://www.artima.com/weblogs/viewpost.jsp?thread=237121
>

Thanks to all who replied to this thread.
I didn't remember why I didn't want to dive into super in the first
place, now I remember :o)

I'm sure about one thing about super: it has a misleading name.

JM

Gabriel Genellina

unread,
Jan 20, 2010, 6:09:17 PM1/20/10
to pytho...@python.org
En Tue, 19 Jan 2010 08:44:09 -0300, Michele Simionato
<michele....@gmail.com> escribi�:

> On Jan 16, 6:55 pm, Steven D'Aprano <st...@REMOVE-THIS-
> cybersource.com.au> wrote:

>> I have a series of subclasses that inherit methods from a base class,
>> but
>> I'd like them to have their own individual docstrings.
>

> from types import FunctionType, CodeType
>
> def newfunc(func, docstring):
> c = func.func_code
> nc = CodeType(c.co_argcount, c.co_nlocals, c.co_stacksize,
> c.co_flags, c.co_code, c.co_consts, c.co_names,
> c.co_varnames, c.co_filename, func.__name__,
> c.co_firstlineno, c.co_lnotab, c.co_freevars,
> c.co_cellvars)
> nf = FunctionType(nc, func.func_globals, func.__name__)
> nf.__doc__ = docstring
> return nf
>
> def setdocstring(method, docstring):
> cls = method.im_class
> basefunc = getattr(super(cls, cls), method.__name__).im_func
> setattr(cls, method.__name__, newfunc(basefunc, docstring))
>

> class B(object):
> def m(self):
> "base"
> return 'ok'
>
> class C(B):
> pass
>
> setdocstring(C.m, 'C.m docstring')

This is basically the same technique as in
<http://permalink.gmane.org/gmane.comp.python.general/651001> but there is
a difference: you clone the function object *and* the code object it is
based on. As I understand it, code objects are immutable and there is no
need to clone them, but I may be wrong. Why did you feel the need to clone
the code object too?

--
Gabriel Genellina

Michele Simionato

unread,
Jan 21, 2010, 1:17:40 AM1/21/10
to
On Jan 21, 12:09 am, "Gabriel Genellina" <gagsl-...@yahoo.com.ar>
wrote:

> This is basically the same technique as in  
> <http://permalink.gmane.org/gmane.comp.python.general/651001> but there is  
> a difference: you clone the function object *and* the code object it is  
> based on. As I understand it, code objects are immutable and there is no  
> need to clone them, but I may be wrong. Why did you feel the need to clone  
> the code object too?

No need. I just had the newfunc utility in my library (I think copied
from a recipe in the Python cookbook by Alex Martelli) so I used it.
In this case it is overkill, though. Also, I had no read your post
when I posted my solution, otherwise I would not have sent it ;)
Anyway, the setdocstring utility to extract the parent method was not
explicit in your post and may be of some use to somebody.

M. Simionato

Gabriel Genellina

unread,
Jan 22, 2010, 12:13:52 AM1/22/10
to pytho...@python.org
En Thu, 21 Jan 2010 03:17:40 -0300, Michele Simionato
<michele....@gmail.com> escribi�:

Ah, ok! I was afraid I missed something -- but reusing code is a perfectly
valid reason!

--
Gabriel Genellina

0 new messages