Comment string literals (c-strings)

I have autism, what’s obvious for you isn’t obvious for me.

It has his approval in 2011. Maybe you should ask today?

As far as I know, there is no style guide that advocates this. And I haven’t seen any code in a large project that does this.

Also most linters complain about it.

Personally I think it’s wrong to encourage this.

I tried to do research this time. This problem applies to many people.
I thought that /**/ was incompatible with Python’s syntax, just like //.
The implementation wouldn’t have been difficult. It’s more flexible than unused string literals.

Well, we’re discussing it now, right? Should we ask for Guido’s opinion?

Which linters? I use 6, all at the strictest settings: Flake8, MyPy, Pylance, Pylint, Ruff, PyCharm.
Only Pylint complaints about this.

FYI, this is correct, and I use it a lot to document usage and so on:

2 # number
'string'
{'key': 'value'}
[1, 2, 3, 4, 5, 6]

It compiles just fine.

Ruff will have it eventually.

That’s a docstring —not a comment. There’s nothing wrong with using docstrings on variables.

The problem is when people start using strings as comments.

In my opinion, part of programming isn’t writing code that just “compiles” or “works”. It’s about writing idiomatic code. Idiomatic code is better because it’s easier to read and understand. I think if you look at large Python projects written by people who have been writing Python for a long time, you’ll see that multi-line comments are overwhelmingly rendered using the comment syntax.

1 Like

Adding a new string prefix is a much bigger problem than OP thinks because it multiples the number of possible prefixes to recognize and test.

1 Like

I mean, this one would be incompatible with all others. It would implicitly act as r [1], and f and b (and u) don’t make sense.


  1. If it doesn’t, that would IMO be a major missed opportunity since accidental escape sequences are one of the biggest drawbacks of using strings as comments, especially on windows with paths ↩︎

1 Like

Exactly, it’s incompatible. I should have mentioned that explicitly.

It compiles to nothing. I’m saying we don’t need any multi-line comment syntax. You can already write expressions without assigning them to variables.

# dataclass template
{
    'name': 'John',
    'age': 30,
    'is_active': True,
}

…is much better than:

"""dataclass template
{
    'name': 'John',
    'age': 30,
    'is_active': True,
}
"""

Pretty much all my linters complain about that:

  • Pylance: Expression value is unused
  • Pylint: Statement seems to have no effect
  • Ruff: Found useless expression. Either assign it to a variable or remove it.
  • PyCharm: Statement seems to have no effect

And with good reason, they’re compiled to something (unlike unused string literals):

import dis

dis.dis('''print('Hello, world!')
"""The previous statement
   prints 'Hello, world!'."""''')

print("=" * 73)

dis.dis("""{
    "name": "John",
    "age": 30,
    "is_active": True,
}""")
  0           0 RESUME                   0

  1           2 PUSH_NULL
              4 LOAD_NAME                0 (print)
              6 LOAD_CONST               0 ('Hello, world!')
              8 CALL                     1
             16 POP_TOP

  2          18 RETURN_CONST             1 (None)
=========================================================================
  0           0 RESUME                   0

  2           2 LOAD_CONST               0 ('John')

  3           4 LOAD_CONST               1 (30)

  4           6 LOAD_CONST               2 (True)

  1           8 LOAD_CONST               3 (('name', 'age', 'is_active'))
             10 BUILD_CONST_KEY_MAP      3
             12 RETURN_VALUE

This works too (having some fun with dis):

import dis

dis.dis("""if False:
    print('Hello, world!')""")
  0           0 RESUME                   0

  1           2 RETURN_CONST             1 (None)

But this doesn’t? What’s up with that? Is it because we can shadow exit()?
Yes, exit is not a keyword unlike False.

import dis

dis.dis("""exit()
print('Hello, world!')""")
  0           0 RESUME                   0

  1           2 PUSH_NULL
              4 LOAD_NAME                0 (exit)
              6 CALL                     0
             14 POP_TOP

  2          16 PUSH_NULL
             18 LOAD_NAME                1 (print)
             20 LOAD_CONST               0 ('Hello, world!')
             22 CALL                     1
             30 POP_TOP
             32 RETURN_CONST             1 (None)

Hmm, is it a bug that builtins.__dict__ contains False, None and True? You can’t overwrite that:

>>> from builtins import __dict__
>>> "False" in __dict__, "None" in __dict__, "True" in __dict__
(True, True, True)
1 Like

This is interpreted as an expression that returns a value (which you can tell from the last opcode of the result RETURN_VALUE). You are still correct that this doesn’t get optimized away even in a larger context, at least not by the peephole optimizer (despite there not being a reason it couldn’t), you should be careful that your tests actual mean what you think they mean.

1 Like

Not a bug - it’s a documented member of builtins:

1 Like

Ah, this works:

FALSE = getattr(builtins, "False")
NONE = getattr(builtins, "None")
TRUE = getattr(builtins, "True")
print(FALSE, NONE, TRUE)

+1000 this.

The last thing I want to see is code where things inside some
expression look like strings but should be ignored.

3 Likes