计算Python程序中令牌/表达式的数量



有许多工具可以计算程序中的源代码行数。我现在用的是cloc。我经常用它来衡量我正在做的项目的复杂性,偶尔会花几个星期的时间来最小化这个度量。然而,这并不理想,因为它受到变量名长度等因素的影响。

是否有一种简单的方法,也许是利用python解释器/AST解析器本身,来计算python程序中不同令牌的数量?例如:
grammar = grammar_path.read_text(encoding="UTF-8")

这一行可能有6个令牌,如果我们把第二个参数计算到getattr(),然后再计算赋值操作符。

我希望在某个地方有这样的实现,我只是不知道该怎么谷歌找到它。如果知道是否有其他语言的现有工具可以做到这一点,那将会很有帮助。

grammar = grammar_path.read_text(encoding="UTF-8")有10个标记,如果算上行末尾的NEWLINE标记,则为11个。使用内置tokenize标准库模块中的generate_tokens方法可以很容易地看到这一点。(虽然我在下面的示例中使用的是v3.11,但tokenize模型从v2.2开始就可用了。不过,生成的令牌的细节已经发生了变化。)

注意,generate_token方法期望它的参数是一个在输入行上迭代的函数。为了进行简单的演示,我只使用sys.stdin.readline,它从stdin读取连续的行。更正常的用法是为打开读取的文件提供readline方法。我在示例中使用enumerate,以便对连续的令牌进行编号。

$ python3.11
Python 3.11.0 (main, Oct 24 2022, 19:56:01) [GCC 7.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import tokenize
>>> import sys
>>> for i, token in enumerate(tokenize.generate_tokens(sys.stdin.readline), start=1):
...   print(f"""{i:3}: {token}""")
... 
grammar = grammar_path.read_text(encoding="UTF-8")
1: TokenInfo(type=1 (NAME), string='grammar', start=(1, 0), end=(1, 7), line='grammar = grammar_path.read_text(encoding="UTF-8")n')
2: TokenInfo(type=54 (OP), string='=', start=(1, 8), end=(1, 9), line='grammar = grammar_path.read_text(encoding="UTF-8")n')
3: TokenInfo(type=1 (NAME), string='grammar_path', start=(1, 10), end=(1, 22), line='grammar = grammar_path.read_text(encoding="UTF-8")n')
4: TokenInfo(type=54 (OP), string='.', start=(1, 22), end=(1, 23), line='grammar = grammar_path.read_text(encoding="UTF-8")n')
5: TokenInfo(type=1 (NAME), string='read_text', start=(1, 23), end=(1, 32), line='grammar = grammar_path.read_text(encoding="UTF-8")n')
6: TokenInfo(type=54 (OP), string='(', start=(1, 32), end=(1, 33), line='grammar = grammar_path.read_text(encoding="UTF-8")n')
7: TokenInfo(type=1 (NAME), string='encoding', start=(1, 33), end=(1, 41), line='grammar = grammar_path.read_text(encoding="UTF-8")n')
8: TokenInfo(type=54 (OP), string='=', start=(1, 41), end=(1, 42), line='grammar = grammar_path.read_text(encoding="UTF-8")n')
9: TokenInfo(type=3 (STRING), string='"UTF-8"', start=(1, 42), end=(1, 49), line='grammar = grammar_path.read_text(encoding="UTF-8")n')
10: TokenInfo(type=54 (OP), string=')', start=(1, 49), end=(1, 50), line='grammar = grammar_path.read_text(encoding="UTF-8")n')
11: TokenInfo(type=4 (NEWLINE), string='n', start=(1, 50), end=(1, 51), line='grammar = grammar_path.read_text(encoding="UTF-8")n')

此时,循环正在等待下一行;为了终止循环,我需要输入一个输入结束标记(在Unix上是Control-D;Windows上的Control-Z;在这两种情况下,都按Enter)。然后标记器将返回一个最终的ENDMARKER标记:

12: TokenInfo(type=0 (ENDMARKER), string='', start=(2, 0), end=(2, 0), line='')

如文档中所述,您还可以使用标准库模块作为命令行实用程序来列出令牌。同样,我必须通过键入输入结束标记来终止输入,在此之后打印最后一行:

$ python3.11 -m tokenize
grammar = grammar_path.read_text(encoding="UTF-8")                                   
1,0-1,7:            NAME           'grammar'      
1,8-1,9:            OP             '='            
1,10-1,22:          NAME           'grammar_path' 
1,22-1,23:          OP             '.'            
1,23-1,32:          NAME           'read_text'    
1,32-1,33:          OP             '('            
1,33-1,41:          NAME           'encoding'     
1,41-1,42:          OP             '='            
1,42-1,49:          STRING         '"UTF-8"'      
1,49-1,50:          OP             ')'            
1,50-1,51:          NEWLINE        'n'           
2,0-2,0:            ENDMARKER      ''             

最新更新