1

I'd like to provide documentation (within my program) on certain dynamically created objects, but still fall back to using their class documentation. Setting __doc__ seems a suitable way to do so. However, I can't find many details in the Python help in this regard, are there any technical problems with providing documentation on an instance? For example:

class MyClass:
    """
    A description of the class goes here.
    """

a = MyClass()
a.__doc__ = "A description of the object"

print( MyClass.__doc__ )
print( a.__doc__ )
c z
  • 7,726
  • 3
  • 46
  • 59
  • Are you planning on dynamically creating documentation to certain modules? – Rohi May 22 '18 at 15:58
  • I'm planning on certain dynamically created objects having documentation. – c z May 22 '18 at 16:04
  • I see. I guess you should add this detail to the question, cause that is the only scenario I can think of that will actually make sense. – Rohi May 22 '18 at 16:05
  • 3
    Who is the expected audience for the dynamically created docstrings? – VPfB May 22 '18 at 16:06
  • Also have a look at this : https://stackoverflow.com/questions/2693883/dynamic-function-docstring/2694358 – Rohi May 22 '18 at 16:06
  • @cz You still need to define what "having documentation" means. Sure, anything you assign to `__doc__` is accessible via `__doc__`, but the same is true with, say, `doc`. The only thing special about `__doc__` is that it's used for things like `help(a)`, IDE auto-help, Sphinx documentation generation, etc. – abarnert May 22 '18 at 17:47

4 Answers4

5

__doc__ is documented as a writable attribute for functions, but not for instances of user defined classes. pydoc.help(a), for example, will only consider the __doc__ defined on the type in Python versions < 3.9.

Other protocols (including future use-cases) may reasonably bypass the special attributes defined in the instance dict, too. See Special method lookup section of the datamodel documentation, specifically:

For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.

So, depending on the consumer of the attribute, what you intend to do may not be reliable. Avoid.

A safe and simple alternative is just to use a different attribute name of your own choosing for your own use-case, preferably not using the __dunder__ syntax convention which usually indicates a special name reserved for some specific use by the implementation and/or the stdlib.

wim
  • 338,267
  • 99
  • 616
  • 750
  • I think the part about only considering the __doc__ of the type is no longer true for recent python version @wim – hassec Jan 11 '22 at 16:21
  • 1
    @hassec You're right, that behavior has changed a couple of years after this question posted - I've edited the answer to qualify that statement. Thanks for the comment. – wim Jan 12 '22 at 03:00
2

There are some pretty obvious technical problems; the question is whether or not they matter for your use case.

Here are some major uses for docstrings that your idiom will not help with:

  • help(a): Type help(a) in an interactive terminal, and you get the docstring for MyClass, not the docstring for a.
  • Auto-generated documentation: Unless you write your own documentation generator, it's not going to understand that you've done anything special with your a value. Many doc generators do have some way to specify help for module and class constants, but I'm not aware of any that will recognize your idiom.
  • IDE help: Many IDEs will not only auto-complete an expression, but show the relevant docstring in a tooltip. They all do this statically, and without some special-case code designed around your idiom (which they're unlikely to have, given that it's an unusual idiom), they're almost certain to fetch the docstring for the class, not the object.

Here are some where it might help:

  • Source readability: As a human reading your source, I can tell the intent from the a.__doc__ = … right near the construction of a. Then again, I could tell the same intent just as easily from a Sphinx comment on the constant.
  • Debugging: pdb doesn't really do much with docstrings, but some GUI debuggers wrapped around it do, and most of them are probably going to show a.__doc__.
  • Custom dynamic use of docstrings: Obviously any code that you write that does something with a.__doc__ is going to get the instance docstring if you want it to, and therefore can do whatever it wants with it. However, keep in mind that if you want to define your own "protocol", you should use your own name, not one reserved for the implementation.

Notice that most of the same is true for using a descriptor for the docstring:

>>> class C:
...     @property
...     def __doc__(self):
...         return('C doc')
>>> c = C()

If you type c.__doc__, you'll get 'C doc', but help(c) will treat it as an object with no docstring.


It's worth noting that making help work is one of the reasons some dynamic proxy libraries generate new classes on the fly—that is, a proxy to underlying type Spam has some new type like _SpamProxy, instead of the same GenericProxy type used for proxies to Hams and Eggseses. The former allows help(myspam) to show dynamically-generated information about Spam. But I don't know how important a reason it is; often you already need dynamic classes to, e.g., make special method lookup work, at which point adding dynamic docstrings comes for free.

abarnert
  • 354,177
  • 51
  • 601
  • 671
1

I think it's preferred to keep it under the class via your doc string as it will also aid any developer that works on the code. However if you are doing something dynamic that requires this setup then I don't see any reason why not. Just understand that it adds a level of indirection that makes things less clear to others.

Remember to K.I.S.S. where applicable :)

rsiemens
  • 615
  • 6
  • 15
0

I just stumbled over this and noticed that at least with python 3.9.5 the behavior seems to have changed.

E.g. using the above example, when I call:

help(a)

I get:

Help on MyClass in module __main__:

<__main__.MyClass object>
    A description of the object

Also for reference, have a look at the pydoc implementation which shows:

def _getowndoc(obj):
    """Get the documentation string for an object if it is not
    inherited from its class."""
    try:
        doc = object.__getattribute__(obj, '__doc__')
        if doc is None:
            return None
        if obj is not type:
            typedoc = type(obj).__doc__
            if isinstance(typedoc, str) and typedoc == doc:
                return None
        return doc
    except AttributeError:
        return None
hassec
  • 686
  • 4
  • 18
  • 1
    Appears as though this changed in Python 3.9 https://bugs.python.org/issue40257 – wim Jan 12 '22 at 02:53