views:

118

answers:

5

I'm using python 3.1.

Is it possible to create more than 1 docstring for a single module or function? I'm creating a program, and I'm intending to have multiple docstrings with a category for each. I intend to give other people the program so they can use it, and to make things easy for programmers and non-programmers alike, I'm putting a reference to the docstring for documentation within the program itself.

To be more specific, I have a menu in the program/module as an interface, and one of the options will allow access to the module docstring for documentation on the program. Thus, if it's possible, I want to make multiple docstrings to categorise different types of documentation. So it would be easier on the users if they want to see some part of the documentation.

eg. first docstring contains instructions on how to use the program. Second docstring contains information on how one part of the program works. Third docstring contains info on how another part works. etc.

Is this possible? And if so, how do you reference them?

Updates: Added a comment.

My original thought was to actually have more than one docstring in the sense of:

def foo():
    """docstring1: blah blah blah"""
    """docstring2: blah blah blah"""
    pass # Insert code here

Then there would be some code that I could use to allow me to reference each of these docstrings. So, I'm guessing that this isn't possible then?

A: 

You can replace the __doc__ attribute if you want to have more than one possible docstring, but please consider making the initial docstring flexible enough for all types.

Ignacio Vazquez-Abrams
Theanks for the fast reply, but how does this work, and how do I do replace the __doc__ attribute? Isn't the docstring created with the first set of triple quotes at the start of each module/function etc. and the __doc__ refers to it?
Eric
Verify few attributes in Python are immutable, not even `__class__`. Just bind a new string to the attribute.
Ignacio Vazquez-Abrams
A: 

Module is a collection of classes/functions/modules. So its docstring gives the intro about what it contains.

Class docstring tells what the class is about and its methods docstrings tell what the methods so. A class serves one purpose and a methods does a single thing so they should have single docstring.

Functions do one thing so one doctring should suffice for them.

I can't see what purpose will multiple docstrings suffice. Maybe your module is big. Divide into sub-modules and in the docstring for the module mention sub-modules.

Xolve
+1  A: 

Instead of using a function, you could use a class with usage and extra attributes defined. For example,

class Foo(object):
    '''Here is the function's official docstring'''
    usage='All about the usage'
    extra='How another part works'
    def __call__(self):
        # Put the foo function code here
        pass
foo=Foo()

You'd call it as usual: foo(), and you can get the official docstring, and alternate doc string like this:

print foo.__doc__
print foo.usage
print foo.extra

You can also attach extra attributes to plain functions (instead of using a class as I did above), but I think the syntax is a little uglier:

def foo():
    pass
foo.usage='Usage string'
foo.extra='Extra string'

And, modules are objects too. They can have extra attributes just as easily:

If you define module constants

USAGE='''blah blah'''
EXTRA='''meow'''

Then when you import the module:

import mymodule

You can access the official and alternate docstrings with

mymodule.__doc__
mymodule.USAGE
mymodule.EXTRA
unutbu
+1 for using attributes, but I think I would use a decorator - especially for the functions as it keeps the information near the declaration where it belongs.
gnibbler
If the OP's problem really requires a function to hold extra docstrings, it seems creating a callable class with a usage attribute or method would be the most readable solution. Having a long usage string as a parameter to a decorator may look like a huge cyst :)
unutbu
The one thing that looks wrong with your proposal is that `[item for item in dir((lambda: 2)) if item not in dir((foo))]` tells me `foo` is missing: `__closure__`, `__code__`, `__defaults__`, `__get__`, `__globals__`, `__name__`, `func_closure`, `func_code`, `func_defaults`, `func_dict`, `func_doc`, `func_globals` and `func_name`. I guess none of those matter for typical usage, but they're ready to bite you in the alps when you least expect it.You can't explicitly subclass functions. This is the main reason why I don't think classes are the right tool for the job here.
badp
@bp: The OP says he has a menu item which calls this function, and he'd like a special version of the docstring available for this situation. It seems to me then that what he wants is more than a normal function, he wants an object that can do at least two things: be called, and return a special docstring. That's why I think a class is appropriate. There is no given reason why we must stick with functions.
unutbu
@bp: I've been thinking a bit more about what you wrote and I'm afraid I don't understand what you mean by "foo is missing", nor how it may "bit me in the alps" (ouch!). Would you please explain?
unutbu
Well, by "foo is missing" I'm talking about a `foo` instance of your `Foo` class and about the result of the `[field for field in dir((lambda: 2)) if field not in dir((foo))]` comprehension. Some of those fields may be needed by certain language features; their absence might lead to weird errors.Sorry for the late reply.
badp
+3  A: 

I don't recommend trying to do something complicated with the docstrings. Best to keep the docstrings simple, and do something else if you want to make a bunch of different documentation options available.

If you really want to do what you described, I suggest you use tags to delimit sections within docstrings. Like so:

def foo(bar, baz):
    """Function foo()

* Summary:
    Function foo() handles all your foo-ish needs.  You pass in a bar and a baz and it foos them.

* Developers:
    When you change foo(), be sure you don't add any global variables, and don't forget to run the unit tests.

* Testers:
    When you test foo, be sure to try negative values for baz.
"""
    pass # code would go here

Then you can pretty easily split your string into chunks, and when the user chooses a menu item, show just the appropriate chunks.

s = foo.__doc__  # s now refers to the docstring

lst = s.split("\n* ")
section = [section for section in lst if section.startswith("Developers")][0]
print(section) # prints the "Developers" section

This way, when you are working in the interactive Python shell, you can say "help(foo)" and you will see all the docstrings. And, you are not changing the fundamental behavior of a basic part of Python, which would freak out other people trying to study your code.

You could also do something even simpler: just make a big global dictionary of docstrings for different purposes, and update it from the source code for each new thing.

doc_developers = {} doc_testers = {}

def foo(bar, baz):
    """Function foo()

Function foo() handles all your foo-ish needs.  You pass in a bar and a baz and it foos them."
    pass # code goes here

doc_developers["foo"] = "When you change foo(), be sure you don't add any global variables, and don't forget to run the unit tests."

doc_testers["foo"] = "When you change foo(), be sure you don't add any global variables, and don't forget to run the unit tests."

The biggest thing I don't like about this is that, if you change the name of function foo, you would need to change it in multiple places: once in the actual def and once per dictionary update line. But you could mostly fix that by writing a function:

def doc_dict = {} # this will be a dict of dicts
doc_dict["developers"] = {}
doc_dict["testers"] = {}

def doc_update(fn, d):
    name = fn.__name__
    for key, value in d.items():
        doc_dict[key][name] = value

def foo(bar, baz):
    """Function foo()

Function foo() handles all your foo-ish needs.  You pass in a bar and a baz and it foos them."
    pass # code goes here

d = { "developers": "When you change foo(), be sure you don't add any global variables, and don't forget to run the unit tests.",
"testers": " When you test foo, be sure to try negative values for baz."}

doc_update(foo, d)

There is probably a way to turn doc_update() into a decorator, but I'm out of time right now.

steveha
Use inspect.cleandoc so you can "naturally" write the docstring by indenting it like the rest of the code.
Roger Pate
+1  A: 

You want to consider using decorators to cleanly do what ~unutbu is proposing for functions: adding a separate field per each. For example:

def human_desc(description):
    def add_field(function):
        function.human_desc = description
        return function
    return add_field

This is what human_desc in action would look like:

@human_desc('This function eggfoobars its spam.')
def eggfoobar(spam):
    "Apply egg, foo and bar to our spam metaclass object stuff."
    print spam

Explaination

As the doc explains, that bit of code is equivalent to the following:

def eggfoobar(spam):
    "Apply egg, foo and bar to our spam metaclass object stuff."
    print spam
eggfoobar = human_desc('This function eggfoobars its spam.')(eggfoobar)

and human_desc('This function eggfoobars its spam.') returns the following function:

def add_field(function):
    function.human_desc = 'This function eggfoobars its spam.'
    return function

As you can see human_desc is a function that generates the above decorator for the value of description you pass as an argument. The decorator itself is a function that accepts a function to be decorated (modified) and returns it decorated (in this case, that is, with the addition of that bit of extra metadata). In short this is equivalent to:

def eggfoobar(spam):
    "Apply egg, foo and bar to our spam metaclass object stuff."
    print spam
eggfoobar.human_desc = 'This function eggfoobars its spam.'

The syntax, however, is far cleaner and less error prone.

Obviously, in either way, what you get is:

>>> print eggfoobar.human_desc
This function eggfoobars its spam.
badp