Setting the docstring to an expression inside def
Asked Answered
M

4

4

I would like to set the func_doc (as an expression) within def.

def f():
    '''My function help''' #Set the docstring

def g():
    "My function " + "help" # An expression, so not read as a docstring
    # can I put something here to set the docstring as an expression?
g.func_doc # is None
g.func_doc = "My function " + "help" # This works

Is this possible?

(two reasons I can think for doing this: importing a function from a module (and you want to import the docstring too) and using a lexer.)

Martamartaban answered 31/8, 2012 at 15:39 Comment(1)
Initially I thought this was a duplicate of this question but I realize now that it isn't. Still, that thread provides a potential solution.Niobium
A
6

You can't do that, since only a string literal is recognized as a docstring. But you can use a decorator to set or modify a function's docstring. (You can also modify __doc__ explicitly in executable code, but a decorator is much cleaner since it is logically part of the declaration).

This can be useful, for example, if you have several functions that should contain the same text as (part of) their docstring. Here's a little decorator that appends its argument (literal or a variable) to a function's declared docstring.

def docstring(docstr, sep="\n"):
    """
    Decorator: Append to a function's docstring.
    """
    def _decorator(func):
        if func.__doc__ is None:
            func.__doc__ = docstr
        else:
            func.__doc__ = sep.join([func.__doc__, docstr])
        return func
    return _decorator

It can be used like this:

@docstring("copyright by nobody")
def testme():
    "This function does nothing"
    pass

Or you can execute it directly, to modify an existing function (perhaps imported from another module):

from re import sub
docstring("Copyright unknown")(sub)
Aalesund answered 31/8, 2012 at 15:46 Comment(1)
If you are using pylint, you probably have to disable E1101 as well: # pylint: disable=E1101.Placatory
L
1

No, you cannot set the docstring of a function from within the body of the function, not without executing code either outside of the function, or inside the function (which requires calling the function first).

The way python normally sets the docstring is to take the first line of the function suite (everything indented under the def line) and if that's a string literal, it removes that from the suite and makes that the docstring. Python then compiles the rest of the suite into the function code object, and creates a new function() object by passing in the compiled code object and the docstring (among others).

If you use an expression that happens to produce a string instead, then that 'trick' doesn't work; python will ignore that expression, leaving it as part of the function suite to compile. Because the function suite itself is compiled and not executed, you cannot 'reach out' and set the docstring to be used on the function object during compilation time.

The only way you can dynamically set a function's docstring is by referring to a function object directly and setting it's __doc__ or func_doc variable (the two are aliases). Sure, that can be done in the function suite, but that's rather pointless, the docstring will be wrong until you do so.

Note that when you import a function from a module, you already get the docstring too:

>>> import itertools
>>> print itertools.groupby.__doc__
groupby(iterable[, keyfunc]) -> create an iterator which returns
(key, sub-iterator) grouped by each value of key(value).

There is no need to import the docstring separately.

Liew answered 31/8, 2012 at 15:45 Comment(5)
You absolutely can set the docstring of a function within the function. Apologies for the formatting : def thisfunction(): """The old doc string""" thisfunction.__doc__ = 'The New doc string' thisfunction() print(thisfunction.__doc__)Ratline
@Tony that’s not the same thing. The docstring literal at the start is not an expression. Adding an expression that is part of the function body is something different.Liew
@Tony: and doing it in the function is pointless. You’d have to call the function before the ‘correct’ docstring is set. If you are going to generate one dynamically, do so at module level, or use a decorator.Liew
@TonySuffolk66: edited to further emphasise this, but my intention was in the answer already: Because the function suite itself is compiled and _not executed_, you cannot 'reach out' and set the docstring to be used on the function object during compilation time. (bold emphasis added in the comment).Liew
I do agree that you can't set the documentation string using an expression at 'compile' time, and that to change it you have to either decorate or execute the function. Maybe I was being a bit too pendantic in my comment (and maybe my comment came off a bit too confrontational - apoloigies in that case).Ratline
A
0

You cannot. The rule is: A string literal as first statement is taken as docstring. If it's an expression, it is not a string literal, and hence ignored.

You can also explicitly assign to the docstring attribute afterwards if you really need this. I don't see why you'd need it though. Your reasons seem fishy to me:

  • Importing a function (or anything else, really) gives you the very same object, with the same docstring. Wrapping a function is another story, but for what we have functools.wraps.
  • Factoring out parts of a regex into a separate variable doesn't make anything more readable per se. Having the token definition at hand is vital for understanding the action code.
Adytum answered 31/8, 2012 at 15:44 Comment(0)
H
0

You can modify the __doc__ attribute of the function at runtime:

>>> def what():
...    """docstring"""
...    what.__doc__ += " x"
...    print what.__doc__
... 
>>> what()
docstring x
>>> what()
docstring x x
>>> what()
docstring x x x
>>> what()
docstring x x x x
Hamo answered 31/8, 2012 at 17:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.