Why can’t f-strings be used as docstring?

Asked

Viewed 320 times

10

In accordance with the PEP 257, one has:

A docstring is a literal string that occurs as the first statement in a module, Function, class, or method Definition. Such a docstring Becomes the __doc__ special attribute of that Object.

That is, the docstring is a literal string that is the first statement of a module, class or function, which will be accessible by the field __doc__ of the object.

def hello():
    'Exibe uma saudação'
    print('Hello')

print(hello.__doc__)  # Exibe uma saudação

See working on Repl.it | Github GIST

But if we try to use one f-string, with formatting, the value of __doc__ will be None.

__author__ = 'Anderson Carlos Woss'

def hello():
    f'Autor: {__author__}'
    print('Hello')

print(hello.__doc__)  # None

See working on Repl.it | Github GIST

Why the f-string cannot be used as docstring?

  • 1

    Because Guido said so! Now that he’s not in charge, he can use it! : Q :D I believe it is a limitation because it can only take a constant value. When using interpolation that is an action to be executed at the time of execution to give a result. It is worth as a response to elaborate a little more?

  • It is, especially if you have sources. This is the way. A f-string is no longer literal because it needs to treat interpolation and, by definition, cannot be used as a docstring.

  • I think so, I found justification :)

  • Until because the docstring is generated at "compile" time, not runtime, then how would you guarantee the value of the variable? In this case it has value, but imagine using a parameter inside the docstrings...

2 answers

5


Because the f-string ceases to be literal and by definition ceases to be a candidate to be docstring.

Also by the fact that the docstring is generated at compile time, when generated the byte-code that will be interpreted - as the Maniero comment in its reply, This was a design choice; they could make the value be evaluated at runtime, but they saw no reason for it. So, how to guarantee the value of the objects that will be interpolated? In the case of the question, interpolation occurs only with a global variable and could generate the docstring "Author: Anderson Carlos Woss", but what if I do it at some other point in the code __author__ = 'Foo', this should change the docstring? This worsens if we consider that we could add the value of a function parameter in the docstring:

def hello(name):
    f'Saudação para {name}'
    print(f'Hello, {name}')

In this case, what would be the value of hello.__doc__? One option would be to consider the string unformatted as docstring, but again, this was a design choice. What sense would have a docstring with the value Autor: {__author__}?

As for not being another string literal, see the difference between the opcodes generated for a string normal and a f-string.

Normal string

>>> print(dis.dis('"Hello"'))
  1           0 LOAD_CONST               0 ('Hello')
              2 RETURN_VALUE
None

F-String

>>> print(dis.dis('f"Hello {name}"'))
  1           0 LOAD_CONST               0 ('Hello ')
              2 LOAD_NAME                0 (name)
              4 FORMAT_VALUE             0
              6 BUILD_STRING             2
              8 RETURN_VALUE
None

The extra steps, to format and build the string end cause the f-string is no longer just a literal value and needs this execution. For example, see opcode generated when a string literal as docstring:

  2           0 LOAD_CONST               0 ('Hello world')
              2 STORE_NAME               0 (__doc__)

  3           4 LOAD_NAME                1 (print)
              6 LOAD_CONST               1 ('Hello')
              8 CALL_FUNCTION            1
             10 POP_TOP
             12 LOAD_CONST               2 (None)
             14 RETURN_VALUE
None

Note that it stores the string inside the object __doc__, be it the module, class or function. However, when reporting a f-string, the result is:

  2           0 LOAD_CONST               0 ('Hello ')
              2 LOAD_NAME                0 (name)
              4 FORMAT_VALUE             0
              6 BUILD_STRING             2
              8 POP_TOP

  3          10 LOAD_NAME                1 (print)
             12 LOAD_CONST               1 ('Hello')
             14 CALL_FUNCTION            1
             16 POP_TOP
             18 LOAD_CONST               2 (None)
             20 RETURN_VALUE
None

That is, the interpreter will evaluate the f-string, interpolation of values and return to string end that as it will no longer be a docstring, will be a string in the code and therefore discarded. How is not informed a string literally, the function ends up being without a docstring, so return None.

3

The mechanism of docstring It should be very simple, only to have a constant value that is stored somewhere and that if invoked it is brought up because who needed it. It was not the intention of the language to provide a more sophisticated mechanism. A f-string is more sophisticated, it has code execution to arrive at a result, because the interpolation does not occur magically just because the syntax seems to indicate this, there is the call of a function that properly concatenates the necessary texts.

This can be confirmed when they opened a bug in the documentation that has been complied with to indicate that it cannot even.

You could have chosen to make a more sophisticated mechanism that would allow you to solve at runtime, nor would it be too out of the philosophy of language, but in general it doesn’t pay, I even question the need for this tip ode thing in the current form, because language already has a more sophisticated way of doing this, although it requires more effort. And even to do exactly this manually is very simple.

  • 1

    "They could have chosen to make a more sophisticated mechanism that would allow solving at runtime," - They could not: a fstring depends on being executed, that is - it only has its rendered value when the function is called. A docstring becomes an attribute of the function object - regardless of whether the function is ever called or not - that is, it has to be known "from outside". F-strings like docstrings are not an option, they actually don’t even make sense.

  • They could, but it’s outside the scope of explaining how to do it, I know how to solve it, it might not even be the way you expect it, and of course, as I said it doesn’t make up for the effort because there won’t be a real gain, it’s not something that’s needed, and I don’t know if it’s inside what they proposed to do, but the important thing is that they could.

  • 1

    could not not. : -) not without destroying the language. is Python, not a hobbist language that wouldn’t mind destroying consistency in exchange for a Feature that wouldn’t be such a big deal. In particular, there is no frame of the function with its local variables when the function object is mounted. Another, that the code within fstrings can disengage side effects, since it can call functions - you would have something inside the body of a function being triggered when a module is simply imported: this would no longer be Python.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.