我正在向官方咨询。
我找不到注释(它们以#
开头)和docstring(它们应该以'''
开头)的语法。快速查看Python grammar specification as of Python 3.6页对这两种情况都没有帮助-docstring在那里被定义为longstrings
但在语法规范中没有出现名为STRING
的类型将进一步显示,但不会引用其定义。
鉴于此,我很好奇cpython编译器如何知道注释和docstring是什么。这项壮举是如何完成的?
我最初猜测,cpython编译器会在第一次传递时删除注释和docstring,但接下来的问题是help()
如何能够呈现相关的docstring。
最佳答案
第一节
评论怎么办?
在标记化/词法分析过程中,注释(任何前面的“#
”都被忽略),因此不需要编写规则来解析它们。它们不向解释器/编译器提供任何语义信息,因为它们只是为了提高程序的详细程度,因此会被忽略。
这是ansi c编程语言的lex规范:http://www.quut.com/c/ANSI-C-grammar-l-1998.html。我想提醒您注意这里处理评论的方式:
"/*" { comment(); }
"//"[^\n]* { /* consume //-comment */ }
现在,看看
int
的规则。"int" { count(); return(INT); }
以下是处理
int
和其他标记的lex函数:void count(void)
{
int i;
for (i = 0; yytext[i] != '\0'; i++)
if (yytext[i] == '\n')
column = 0;
else if (yytext[i] == '\t')
column += 8 - (column % 8);
else
column++;
ECHO;
}
在这里,它以
ECHO
语句结束,这意味着它是一个有效的标记,必须被解析。下面是处理注释的lex函数:
void comment(void)
{
char c, prev = 0;
while ((c = input()) != 0) /* (EOF maps to 0) */
{
if (c == '/' && prev == '*')
return;
prev = c;
}
error("unterminated comment");
}
这里没有。所以,什么都不退还。
这是一个典型的例子,但是python做了完全相同的事情。
第二节
docstrings怎么了?
注:我的回答的这一部分是对@martijnpieters回答的补充。这并不意味着要复制他在岗位上提供的任何信息。现在,说到这里,。。。
我最初猜测注释和docstring在
cpython编译器的第一次传递[…]
DocStrings(未分配给任何变量名的字符串文本,以及
ECHO
、'...'
、"..."
或'''...'''
内的任何内容)确实得到了处理。它们被解析为简单的字符串文本("""..."""
标记),正如martijn pieters在his answer中提到的。对于当前的文档,传递中只提到docstring被分配给函数/类/模块的STRING+
属性。它是如何做到的,并没有真正深入地提到任何地方。实际发生的情况是,它们被标记并被解析为字符串文本,生成的解析树将包含它们从解析树生成字节码,docstring在
__doc__
属性中的正确位置(它们不是下面所示的字节码的一部分)。我不会详细讨论,因为我在上面链接的答案描述了同样的细节。当然,完全忽略它们是可能的。如果使用
__doc__
(python -OO
标志表示“强烈优化”,而不是-OO
表示“温和优化”),生成的字节码存储在-O
文件中,不包括docstring。如下图所示:
使用以下代码创建文件
.pyo
:def foo():
""" docstring """
pass
现在,我们将使用普通标志集编译这段代码。
>>> code = compile(open('test.py').read(), '', 'single')
>>> import dis
>>> dis.dis(code)
1 0 LOAD_CONST 0 (<code object foo at 0x102b20ed0, file "", line 1>)
2 LOAD_CONST 1 ('foo')
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (foo)
8 LOAD_CONST 2 (None)
10 RETURN_VALUE
如您所见,字节码中没有提到docstring但是,他们在那里为了得到docstring,你可以…
>>> code.co_consts[0].co_consts
(' docstring ', None)
因此,如您所见,docstring确实保留下来,只是不作为主字节码的一部分。现在,让我们重新编译此代码,但优化级别设置为2(相当于
test.py
开关):>>> code = compile(open('test.py').read(), '', 'single', optimize=2)
>>> dis.dis(code)
1 0 LOAD_CONST 0 (<code object foo at 0x102a95810, file "", line 1>)
2 LOAD_CONST 1 ('foo')
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (foo)
8 LOAD_CONST 2 (None)
10 RETURN_VALUE
不,不同,但是…
>>> code.co_consts[0].co_consts
(None,)
docstrings现在不见了。
-OO
和-O
标记只删除一些内容(默认情况下,字节码优化是完成的……-OO
从生成的字节码中删除assert语句和-O
套件,而if __debug__:
另外忽略docstring)。生成的编译时间将略有减少。此外,执行速度保持不变,除非有大量-OO
和assert
语句,否则对性能没有影响。另外,请记住,只有当docstring是函数/类/模块定义中的第一项时,才会保留docstring所有额外的字符串在编译期间都会被删除如果将
if __debug__:
更改为以下值:def foo():
""" docstring """
"""test"""
pass
然后对
test.py
重复相同的过程,在编译时存储在optimization=0
变量中:>>> code.co_consts[0].co_consts
(' docstring ', None)
也就是说,
co_consts
被忽略了。您会感兴趣的是,这个删除是作为字节码基础优化的一部分完成的。第三节
附加读数
(你可能和我一样感兴趣。)
What does Python optimization (-O or PYTHONOPTIMIZE) do?
What do the python file extensions, .pyc .pyd .pyo stand for?
Are Python docstrings and comments stored in memory when a module is loaded?
Working with compile()
""" test """
模块dis
(由martijn提供)-所有编译器优化的源代码。如果你能理解的话,这真是太迷人了!