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.
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.
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.
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.
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 ↩︎
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)
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.
Not a bug - it’s a documented member of builtins
:
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.