Inheriting methods but over-riding docstrings

S

Steven D'Aprano

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

Gerard Flanagan

Steven said:
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?

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

Gabriel Genellina

En Sat, 16 Jan 2010 14:55:11 -0300, Steven D'Aprano
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?

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
 
A

Alf P. Steinbach

* Gabriel Genellina:
En Sat, 16 Jan 2010 14:55:11 -0300, Steven D'Aprano


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

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...read/thread/12a125ceddd401e2/c021547a1dc14a41>.

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


Cheers & hth.,

- Alf
 
S

Steve Holden

Gabriel said:
En Sat, 16 Jan 2010 14:55:11 -0300, Steven D'Aprano


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".
abs(number) -> number

Return the absolute value of the argument.
Perhaps I am misunderstanding "don't have docstrings". This code:
.... """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
 
J

J

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...read/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 :)
 
S

Steven D'Aprano

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
 
G

Gabriel Genellina

Gabriel Genellina wrote:
On behalf of all methods I would like to say "We demand the right to
docstrings".

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

Jean-Michel Pichavant

I'm not a big fan of super, but I'm still wondering if

would have made it.

What if Subclass has more than one base class ?


JM
 
J

Jean-Michel Pichavant

Duncan said:
No, it wouldn't.



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:


def parrot(self):
print "Base.parrot"



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



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



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



def parrot(self):
print "SubSubClass.parrot"
return super(self.__class__, self).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) ...
I see.

Then is there a reason why?

Or is it just a matter of preference ?

JM
 
A

Arnaud Delobelle

[...]
Then is there a reason why
?

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.
.... def foo(self): print 'A'
.... .... def foo(self):
.... super(B, self).foo()
.... print 'B'
.... .... def foo(self):
.... super(C, self).foo()
.... print 'C'
.... .... def foo(self):
.... super(D, self).foo()
.... print 'D'
....
[<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
 
G

Gabriel Genellina

En Mon, 18 Jan 2010 16:50:45 -0300, Jean-Michel Pichavant
Duncan said:
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
 
M

Michele Simionato

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
 
J

Jean-Michel Pichavant

Gabriel said:

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 :eek:)

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

JM
 
G

Gabriel Genellina

En Tue, 19 Jan 2010 08:44:09 -0300, Michele Simionato
On Jan 16, 6:55 pm, Steven D'Aprano <st...@REMOVE-THIS-
cybersource.com.au> wrote:

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

Michele Simionato

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
 
G

Gabriel Genellina

En Thu, 21 Jan 2010 03:17:40 -0300, Michele Simionato
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.

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,008
Latest member
HaroldDark

Latest Threads

Top