Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #7273 > unrolled thread
| Started by | Eric Snow <ericsnowcurrently@gmail.com> |
|---|---|
| First post | 2011-06-09 00:22 -0600 |
| Last post | 2011-06-10 17:19 +0000 |
| Articles | 13 — 4 participants |
Back to article view | Back to comp.lang.python
how to inherit docstrings? Eric Snow <ericsnowcurrently@gmail.com> - 2011-06-09 00:22 -0600
Re: how to inherit docstrings? Ben Finney <ben+python@benfinney.id.au> - 2011-06-09 16:37 +1000
Re: how to inherit docstrings? Eric Snow <ericsnowcurrently@gmail.com> - 2011-06-09 01:13 -0600
Re: how to inherit docstrings? Ben Finney <ben+python@benfinney.id.au> - 2011-06-09 17:44 +1000
Re: how to inherit docstrings? Duncan Booth <duncan.booth@invalid.invalid> - 2011-06-09 11:23 +0000
Re: how to inherit docstrings? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-06-09 14:56 +0000
Re: how to inherit docstrings? Ben Finney <ben+python@benfinney.id.au> - 2011-06-10 07:33 +1000
Re: how to inherit docstrings? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-06-10 09:25 +0000
Re: how to inherit docstrings? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-06-10 16:47 +0000
Re: how to inherit docstrings? Eric Snow <ericsnowcurrently@gmail.com> - 2011-06-10 11:01 -0600
Re: how to inherit docstrings? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-06-10 17:27 +0000
Re: how to inherit docstrings? Eric Snow <ericsnowcurrently@gmail.com> - 2011-06-10 12:48 -0600
Re: how to inherit docstrings? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-06-10 17:19 +0000
| From | Eric Snow <ericsnowcurrently@gmail.com> |
|---|---|
| Date | 2011-06-09 00:22 -0600 |
| Subject | how to inherit docstrings? |
| Message-ID | <mailman.42.1307600576.11593.python-list@python.org> |
Sometimes when using class inheritance, I want the overriding methods of the subclass to get the docstring of the matching method in the base class. You can do this with decorators (after the class definition), with class decorators, and with metaclasses [1]. However, I was hoping for a way to do it with just function decorators on the methods (no metaclass or class decorator). I am not sure if this is doable. I realize now that this is exactly the reason I got to thinking last week about objects being notified when they are bound [2]. So, is there a way to do this with just decorators, or am I "stuck" with the metaclass/class decorator route? (It's not all that bad :) Thanks! -eric p.s. Am I missing something or can you really not change the docstring of a class? I was thinking about the idea of inheriting class docstrings too. [1] http://code.activestate.com/recipes/577743-using-decorators-to-inherit-function-docstrings/ [2] http://mail.python.org/pipermail/python-ideas/2011-June/010446.html
[toc] | [next] | [standalone]
| From | Ben Finney <ben+python@benfinney.id.au> |
|---|---|
| Date | 2011-06-09 16:37 +1000 |
| Message-ID | <87k4cvy0gc.fsf@benfinney.id.au> |
| In reply to | #7273 |
Eric Snow <ericsnowcurrently@gmail.com> writes:
> p.s. Am I missing something or can you really not change the docstring
> of a class? I was thinking about the idea of inheriting class
> docstrings too.
The docstring of an object (whether function or class or module) is the
object's ‘__doc__’ attribute. Access that attribute to get the
docstring; re-bind that attribute to set a different docstring.
So, it's even possible to do what you ask without decorators at all:
class Foo(object):
def frob(self):
""" Frobnicate thyself. """
class Bar(Foo):
def frob(self):
pass
frob.__doc__ = Foo.frob.__doc__
Not very elegant, and involving rather too much repetition; but not
difficult.
--
\ “We are no more free to believe whatever we want about God than |
`\ we are free to adopt unjustified beliefs about science or |
_o__) history […].” —Sam Harris, _The End of Faith_, 2004 |
Ben Finney
[toc] | [prev] | [next] | [standalone]
| From | Eric Snow <ericsnowcurrently@gmail.com> |
|---|---|
| Date | 2011-06-09 01:13 -0600 |
| Message-ID | <mailman.44.1307603589.11593.python-list@python.org> |
| In reply to | #7274 |
On Thu, Jun 9, 2011 at 12:37 AM, Ben Finney <ben+python@benfinney.id.au> wrote: > Eric Snow <ericsnowcurrently@gmail.com> writes: > >> p.s. Am I missing something or can you really not change the docstring >> of a class? I was thinking about the idea of inheriting class >> docstrings too. > > The docstring of an object (whether function or class or module) is the > object's ‘__doc__’ attribute. Access that attribute to get the > docstring; re-bind that attribute to set a different docstring. > Sorry, I should have been more clear: >>> class X: ... "some doc" ... >>> X.__doc__ 'some doc' >>> X.__doc__ = "another doc" Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: attribute '__doc__' of 'type' objects is not writable That is on 3.3. > So, it's even possible to do what you ask without decorators at all: > > class Foo(object): > def frob(self): > """ Frobnicate thyself. """ > > class Bar(Foo): > def frob(self): > pass > frob.__doc__ = Foo.frob.__doc__ > > Not very elegant, and involving rather too much repetition; but not > difficult. > Yeah, definitely you can do it directly for each case. However, the inelegance, repetition, and immodularity are exactly why I am pursuing a solution. :) (I included a link in the original message to examples of how you can already do it with metaclasses and class decorators too.) I'm just looking for a way to do it with decorators in the class body without using metaclasses or class decorators. Thanks -eric > -- > \ “We are no more free to believe whatever we want about God than | > `\ we are free to adopt unjustified beliefs about science or | > _o__) history […].” —Sam Harris, _The End of Faith_, 2004 | > Ben Finney > -- > http://mail.python.org/mailman/listinfo/python-list >
[toc] | [prev] | [next] | [standalone]
| From | Ben Finney <ben+python@benfinney.id.au> |
|---|---|
| Date | 2011-06-09 17:44 +1000 |
| Message-ID | <87fwnjxxcv.fsf@benfinney.id.au> |
| In reply to | #7278 |
Eric Snow <ericsnowcurrently@gmail.com> writes: > AttributeError: attribute '__doc__' of 'type' objects is not writable > > That is on 3.3. Well, that sucks :-( Where can we see the discussion of that change before it was implemented? > I'm just looking for a way to do it with decorators in the class body > without using metaclasses or class decorators. Yes, that'd be nice. Do you have a specification in mind for how it would work? Perhaps time to start a thread on the ‘python-ideas’ forum. -- \ “Following fashion and the status quo is easy. Thinking about | `\ your users' lives and creating something practical is much | _o__) harder.” —Ryan Singer, 2008-07-09 | Ben Finney
[toc] | [prev] | [next] | [standalone]
| From | Duncan Booth <duncan.booth@invalid.invalid> |
|---|---|
| Date | 2011-06-09 11:23 +0000 |
| Message-ID | <Xns9EFF7DF666E20duncanbooth@127.0.0.1> |
| In reply to | #7280 |
Ben Finney <ben+python@benfinney.id.au> wrote: > Eric Snow <ericsnowcurrently@gmail.com> writes: > >> AttributeError: attribute '__doc__' of 'type' objects is not writable >> >> That is on 3.3. > > Well, that sucks :-( > > Where can we see the discussion of that change before it was > implemented? > Change? What change? C:\Python27>python Python 2.7 (r27:82525, Jul 4 2010, 09:01:59) [MSC v.1500 32 bit (Intel)] on win 32 Type "help", "copyright", "credits" or "license" for more information. >>> class C(object): ... "Hello world" ... >>> C.__doc__ = "whatever" Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: attribute '__doc__' of 'type' objects is not writable >>> Or even: Python 2.3.5 (#1, Oct 13 2005, 09:17:23) [GCC 3.2.3 20030502 (Red Hat Linux 3.2.3-52)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> class C(object): ... "Hello world" ... >>> C.__doc__ = "whatever" Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: attribute '__doc__' of 'type' objects is not writable >>> -- Duncan Booth http://kupuguy.blogspot.com
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2011-06-09 14:56 +0000 |
| Message-ID | <4df0df26$0$29977$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #7280 |
On Thu, 09 Jun 2011 17:44:32 +1000, Ben Finney wrote: > Eric Snow <ericsnowcurrently@gmail.com> writes: > >> AttributeError: attribute '__doc__' of 'type' objects is not writable >> >> That is on 3.3. > > Well, that sucks :-( > > Where can we see the discussion of that change before it was > implemented? It goes back to Python 2.2, when new style classes were first introduced. [steve@sylar ~]$ python2.2 Python 2.2.3 (#1, Aug 12 2010, 01:08:27) [GCC 4.1.2 20070925 (Red Hat 4.1.2-27)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> class K(object): ... "foo" ... >>> K.__doc__ = 'bar' Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: attribute '__doc__' of 'type' objects is not writable It's an unnecessary restriction, as far as I'm concerned, but an old one. -- Steven
[toc] | [prev] | [next] | [standalone]
| From | Ben Finney <ben+python@benfinney.id.au> |
|---|---|
| Date | 2011-06-10 07:33 +1000 |
| Message-ID | <87y61awuz5.fsf@benfinney.id.au> |
| In reply to | #7303 |
Steven D'Aprano <steve+comp.lang.python@pearwood.info> writes: > On Thu, 09 Jun 2011 17:44:32 +1000, Ben Finney wrote: > > > Eric Snow <ericsnowcurrently@gmail.com> writes: > > > >> AttributeError: attribute '__doc__' of 'type' objects is not writable > >> > >> That is on 3.3. > > > > Well, that sucks :-( > > > > Where can we see the discussion of that change before it was > > implemented? > > It goes back to Python 2.2, when new style classes were first introduced. […] > It's an unnecessary restriction, as far as I'm concerned, but an old one. Well, it's incompatible with the Python compiler I keep in my head. Have these developers no consideration for backward-thinking-compatibility? -- \ “The fact that I have no remedy for all the sorrows of the | `\ world is no reason for my accepting yours. It simply supports | _o__) the strong probability that yours is a fake.” —Henry L. Mencken | Ben Finney
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2011-06-10 09:25 +0000 |
| Message-ID | <4df1e300$0$29977$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #7321 |
On Fri, 10 Jun 2011 07:33:34 +1000, Ben Finney wrote: > Steven D'Aprano <steve+comp.lang.python@pearwood.info> writes: >> It's an unnecessary restriction, as far as I'm concerned, but an old >> one. > > Well, it's incompatible with the Python compiler I keep in my head. Have > these developers no consideration for backward-thinking-compatibility? +1 QOTW -- Steven
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2011-06-10 16:47 +0000 |
| Message-ID | <4df24a87$0$30002$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #7273 |
On Thu, 09 Jun 2011 00:22:54 -0600, Eric Snow wrote:
> Sometimes when using class inheritance, I want the overriding methods of
> the subclass to get the docstring of the matching method in the base
> class. You can do this with decorators (after the class definition),
> with class decorators, and with metaclasses [1].
Here's some Python 3 code that uses a factory function as a metaclass to
inherit docstrings. Give the class a docstring of an empty string, and it
will be inherited from the first superclass found with a non-empty
docstring.
def InheritableDocstring(name, bases, dict):
mro = None
docstring = dict.get('__doc__')
if docstring == '':
# Search the MRO for the first non-empty docstring. We let Python
# do all the hard work of calculating the MRO.
mro = type('K', bases, {}).__mro__[1:] # Exclude the class K.
# Also exclude object.
assert mro[-1] == object
mro = mro[:-1]
for cls in mro:
if cls.__doc__:
docstring = cls.__doc__
break
else:
docstring = None
dict['__doc__'] = docstring
assert dict.get('__doc__') != ''
# Create the class we want, and return it.
cls = type(name, bases, dict)
if mro:
assert cls.__mro__ == (cls,) + mro + (object,)
return cls
class A(metaclass=InheritableDocstring):
pass
class B(A, metaclass=InheritableDocstring):
''
class C(B, metaclass=InheritableDocstring):
'A docstring.'
class D(B, metaclass=InheritableDocstring):
pass
class E(D, C, metaclass=InheritableDocstring):
''
class F(E, metaclass=InheritableDocstring):
''
assert all(cls.__doc__ is None for cls in (A, B, D))
assert all(cls.__doc__ == 'A docstring.' for cls in (C, E, F))
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Eric Snow <ericsnowcurrently@gmail.com> |
|---|---|
| Date | 2011-06-10 11:01 -0600 |
| Message-ID | <mailman.86.1307725303.11593.python-list@python.org> |
| In reply to | #7382 |
On Fri, Jun 10, 2011 at 10:47 AM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> Here's some Python 3 code that uses a factory function as a metaclass to
> inherit docstrings. Give the class a docstring of an empty string, and it
> will be inherited from the first superclass found with a non-empty
> docstring.
>
>
Yeah, the idea of an empty docstring to trigger docstring inheritance
really appeals to me. Nice example. Incidently, aren't metaclasses
always inherited, as opposed to class decorators (which are never)?
-eric
>
> def InheritableDocstring(name, bases, dict):
> mro = None
> docstring = dict.get('__doc__')
> if docstring == '':
> # Search the MRO for the first non-empty docstring. We let Python
> # do all the hard work of calculating the MRO.
> mro = type('K', bases, {}).__mro__[1:] # Exclude the class K.
> # Also exclude object.
> assert mro[-1] == object
> mro = mro[:-1]
> for cls in mro:
> if cls.__doc__:
> docstring = cls.__doc__
> break
> else:
> docstring = None
> dict['__doc__'] = docstring
> assert dict.get('__doc__') != ''
> # Create the class we want, and return it.
> cls = type(name, bases, dict)
> if mro:
> assert cls.__mro__ == (cls,) + mro + (object,)
> return cls
>
>
>
> class A(metaclass=InheritableDocstring):
> pass
>
> class B(A, metaclass=InheritableDocstring):
> ''
>
> class C(B, metaclass=InheritableDocstring):
> 'A docstring.'
>
> class D(B, metaclass=InheritableDocstring):
> pass
>
> class E(D, C, metaclass=InheritableDocstring):
> ''
>
> class F(E, metaclass=InheritableDocstring):
> ''
>
> assert all(cls.__doc__ is None for cls in (A, B, D))
> assert all(cls.__doc__ == 'A docstring.' for cls in (C, E, F))
>
>
>
> --
> Steven
> --
> http://mail.python.org/mailman/listinfo/python-list
>
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2011-06-10 17:27 +0000 |
| Message-ID | <4df25403$0$30002$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #7385 |
On Fri, 10 Jun 2011 11:01:41 -0600, Eric Snow wrote: > On Fri, Jun 10, 2011 at 10:47 AM, Steven D'Aprano > <steve+comp.lang.python@pearwood.info> wrote: >> Here's some Python 3 code that uses a factory function as a metaclass >> to inherit docstrings. Give the class a docstring of an empty string, >> and it will be inherited from the first superclass found with a >> non-empty docstring. >> >> >> > Yeah, the idea of an empty docstring to trigger docstring inheritance > really appeals to me. Nice example. Incidently, aren't metaclasses > always inherited, as opposed to class decorators (which are never)? Metaclasses are inherited, but the example I give uses a factory function as a metaclass: it manipulates the docstring inside the dict, then returns an ordinary class. That makes it just a fancy class decorator using metaclass syntax. The type of each class A, B, ... F is just type, which means that when you subclass each class you don't get any magic metaclass behaviour unless you explicitly set the metaclass directly. That is: assert type(A) is type succeeds, so class B(A) doesn't do anything special unless you explicitly set the metaclass. I followed up with a second example using a conventional metaclass, that is, where the type of each class is *not* type. In that case, the magic behaviour is inherited and there's no need to explicitly set the metaclass except for the first time. (Whew. Talking about metaclasses is hard work.) -- Steven
[toc] | [prev] | [next] | [standalone]
| From | Eric Snow <ericsnowcurrently@gmail.com> |
|---|---|
| Date | 2011-06-10 12:48 -0600 |
| Message-ID | <mailman.94.1307731741.11593.python-list@python.org> |
| In reply to | #7388 |
FYI, I started this topic up on python-ideas, as it seemed valid enough from the responses I've gotten here [1]. -eric [1] http://mail.python.org/pipermail/python-ideas/2011-June/010473.html
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2011-06-10 17:19 +0000 |
| Message-ID | <4df25210$0$30002$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #7382 |
On Fri, 10 Jun 2011 16:47:03 +0000, Steven D'Aprano wrote:
> On Thu, 09 Jun 2011 00:22:54 -0600, Eric Snow wrote:
>
>> Sometimes when using class inheritance, I want the overriding methods
>> of the subclass to get the docstring of the matching method in the base
>> class. You can do this with decorators (after the class definition),
>> with class decorators, and with metaclasses [1].
>
>
> Here's some Python 3 code that uses a factory function as a metaclass to
> inherit docstrings. Give the class a docstring of an empty string, and
> it will be inherited from the first superclass found with a non-empty
> docstring.
[...]
And here's a version using a more conventional metaclass. Extending this
to work on methods is left as an exercise.
class MetaDocstring(type):
@staticmethod
def get_mro(bases):
return type('K', bases, {}).__mro__[1:-1]
@staticmethod
def get_docstring(mro):
for k in mro:
if k.__doc__:
return k.__doc__
def __new__(cls, name, bases, dict):
mro = None
docstring = dict.get('__doc__')
if docstring == '':
mro = cls.get_mro(bases)
dict['__doc__'] = cls.get_docstring(mro)
assert dict.get('__doc__') != ''
# Create the class we want, and return it.
K = super().__new__(cls, name, bases, dict)
if mro:
assert K.__mro__ == (K,) + mro + (object,)
return K
class U(metaclass=MetaDocstring):
pass
class V(U):
''
class W(V):
'A docstring.'
class X(V):
pass
class Y(X, W):
''
class Z(Y):
''
assert all(type(cls) is MetaDocstring for cls in (U, V, W, X, Y, Z))
assert all(cls.__doc__ is None for cls in (U, V, X))
assert all(cls.__doc__ == 'A docstring.' for cls in (W, Y, Z))
--
Steven
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web