Python代码统计工具

标签: Python 代码统计


声明

本文将对《Python实现C代码统计工具(一)~(三)》中的C代码统计工具进行扩展,以支持Python脚本自身的行数统计。

一. 问题提出

此前实现的C代码统计工具仅能分析和统计C语言代码文件,但其设计思想也适用于Python代码及其他编码语言。

Python行数统计的难点在于注释行,因为Python有两种注释方式:简单明了的单行注释和复杂含糊的多行注释(块注释)。单行注释以#(pound或hash)符号起始,直至物理行的末尾(但字符串内的#并无注释作用)。多行注释可在每行头部添加#号,也可包入未命名的三引号字符串(triple-quoted strings,即多行字符串)内。除非未命名三引号字符串作为对象的文档字符串(docstring),即模块、类、或函数体的第一条语句为未命名字符串,否则可作为多行注释。

下面以总27_代7_注15_空5.py脚本为例,演示不同的注释方式。注意,该脚本仅作测试数据用,并非真实世界中的脚本文件。

#!/usr/bin/python
# -*- coding: utf-8 -*- #comment3 print 'code1'
'''comment4
print """comment5"""
comment6''' """comment7
'''print 'comment8 and comment9'
"""
print 'code2' def code3():
"""f = open('whatever', 'r')
multiline comment 10,11,12 make up a doc string
"""
print 'code4'
'''
print 'comment13, comment14 and comment15'
'''
return 'code5' help(code3); print 'code6'
print code3.__doc__, 'code7'

运行该脚本后,输出如下:

code1
code2
Help on function code3 in module __main__: code3()
f = open('whatever', 'r')
multiline comment 10,11,12 make up a doc string code6
f = open('whatever', 'r')
multiline comment 10,11,12 make up a doc string
code7

使用未命名三引号字符串做注释时,存在如下缺点:

  1. 未命名字符串本质上并非注释,而是不生成字节码的语句。因此,需要满足缩进要求(常错点)。
  2. 无法注释掉已包含相同三引号字符串的代码。
  3. IDE的语法高亮会将三引号字符串标记为字符串,而不是注释区。

    此外,大多数IDE均支持选择代码片段,并自动使用单行注释符对选区添加注释。以IDLE(Python GUI)为例,快捷键Alt+3可添加注释,Alt+4可删除注释。因此,建议总是使用#号添加多行注释,而三引号字符串仅用于调试过程中临时性地注释代码块。

二. 代码实现

为同时支持统计C和Python代码,需对CalcLines()和CountFileLines()函数稍作修改。其他函数实现参考C代码统计工具前述系列文章。可以看出,绝大部分实现只需少量或无需修改,这表明前期的函数划分和布局得当。

为求直观,将原先的CalcLines()函数重命名为CalcLinesCh()。接着,实现统计Python脚本行信息的CalcLinesPy()函数:

def CalcLinesPy(line, isBlockComment):
#isBlockComment[single quotes, double quotes]
lineType, lineLen = 0, len(line)
line = line + '\n\n' #添加两个字符防止iChar+2时越界
iChar, isLineComment = 0, False
while iChar < lineLen:
#行结束符(Windows:\r\n; MacOS 9:\r; OS X&Unix:\n)
#不可写为"if line[iChar] in os.linesep"(文件可能来自异种系统)
if line[iChar] == '\r' or line[iChar] == '\n':
break
elif line[iChar] == ' ' or line[iChar] == '\t': #空白字符
iChar += 1; continue
elif line[iChar] == '#': #行注释
isLineComment = True
lineType |= 2
elif line[iChar:iChar+3] == "'''": #单引号块注释
if isBlockComment[0] or isBlockComment[1]:
isBlockComment[0] = False
else:
isBlockComment[0] = True
lineType |= 2; iChar += 2
elif line[iChar:iChar+3] == '"""': #双引号块注释
if isBlockComment[0] or isBlockComment[1]:
isBlockComment[1] = False
else:
isBlockComment[1] = True
lineType |= 2; iChar += 2
else:
if isLineComment or isBlockComment[0] or isBlockComment[1]:
lineType |= 2
else:
lineType |= 1
iChar += 1 return lineType #Bitmap:0空行,1代码,2注释,3代码和注释

相应地,CountFileLines()函数作如下修改:

def CountFileLines(filePath, isRawReport=True, isShortName=False):
fileExt = os.path.splitext(filePath)
if fileExt[1] == '.c' or fileExt[1] == '.h':
CalcLinesFunc = CalcLinesCh
elif fileExt[1] == '.py':
CalcLinesFunc = CalcLinesPy
else:
return isBlockComment = [False]*2 #或定义为全局变量,以保存上次值
lineCountInfo = [0]*4 #[代码总行数, 代码行数, 注释行数, 空白行数]
with open(filePath, 'r') as file:
for line in file:
lineType = CalcLinesFunc(line, isBlockComment)
lineCountInfo[0] += 1
if lineType == 0: lineCountInfo[3] += 1
elif lineType == 1: lineCountInfo[1] += 1
elif lineType == 2: lineCountInfo[2] += 1
elif lineType == 3: lineCountInfo[1] += 1; lineCountInfo[2] += 1
else:
assert False, 'Unexpected lineType: %d(0~3)!' %lineType if isRawReport:
global rawCountInfo
rawCountInfo[:-1] = [x+y for x,y in zip(rawCountInfo[:-1], lineCountInfo)]
rawCountInfo[-1] += 1
elif isShortName:
detailCountInfo.append([os.path.basename(filePath), lineCountInfo])
else:
detailCountInfo.append([filePath, lineCountInfo])

CountFileLines()函数根据后缀判断文件类型并调用相应的统计函数,并扩展isBlockComment列表以存储两种Python块注释(三单引号和三双引号)。除此之外,别无其他修改。

三. 效果验证

将本文的统计实现命名为PCLineCounter.py。首先,混合统计C文件和Python脚本:

E:\PyTest>PCLineCounter.py -d lctest
FileLines CodeLines CommentLines EmptyLines CommentPercent FileName
6 3 4 0 0.57 E:\PyTest\lctest\hard.c
33 19 15 4 0.44 E:\PyTest\lctest\line.c
243 162 26 60 0.14 E:\PyTest\lctest\subdir\CLineCounter.py
44 34 3 7 0.08 E:\PyTest\lctest\subdir\test.c
44 34 3 7 0.08 E:\PyTest\lctest\test.c
27 7 15 5 0.68 E:\PyTest\lctest\总27_代7_注15_空5.py
------------------------------------------------------------------------------------------
397 259 66 83 0.20 <Total:6 Code Files>

然后,统计纯Python脚本,并通过cProfile命令分析性能:

E:\PyTest>python -m cProfile -s tottime PCLineCounter.py -d -b C:\Python27\Lib\encodings_trim > out.txt

截取out.txt文件部分内容如下:

FileLines  CodeLines  CommentLines  EmptyLines  CommentPercent  FileName
157 79 50 28 0.39 __init__.py
527 309 116 103 0.27 aliases.py
50 27 8 15 0.23 ascii.py
80 37 22 21 0.37 base64_codec.py
103 55 24 25 0.30 bz2_codec.py
69 38 10 21 0.21 charmap.py
307 285 262 16 0.48 cp1252.py
39 26 5 8 0.16 gb18030.py
39 26 5 8 0.16 gb2312.py
39 26 5 8 0.16 gbk.py
80 37 22 21 0.37 hex_codec.py
307 285 262 16 0.48 iso8859_1.py
50 27 8 15 0.23 latin_1.py
47 24 10 13 0.29 mbcs.py
83 58 36 17 0.38 palmos.py
175 148 127 18 0.46 ptcp154.py
238 183 28 30 0.13 punycode.py
76 45 15 16 0.25 quopri_codec.py
45 24 8 13 0.25 raw_unicode_escape.py
38 24 4 10 0.14 string_escape.py
49 26 9 14 0.26 undefined.py
45 24 8 13 0.25 unicode_escape.py
45 24 8 13 0.25 unicode_internal.py
126 93 10 23 0.10 utf_16.py
42 23 6 13 0.21 utf_16_be.py
42 23 6 13 0.21 utf_16_le.py
150 113 16 21 0.12 utf_32.py
37 23 5 9 0.18 utf_32_be.py
37 23 5 9 0.18 utf_32_le.py
42 23 6 13 0.21 utf_8.py
117 84 14 19 0.14 utf_8_sig.py
130 70 32 28 0.31 uu_codec.py
103 56 23 25 0.29 zlib_codec.py
------------------------------------------------------------------------------------------
3514 2368 1175 635 0.33 <Total:33 Code Files>
10180 function calls (10146 primitive calls) in 0.168 seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function)
3514 0.118 0.000 0.122 0.000 PCLineCounter.py:45(CalcLinesPy)
56 0.015 0.000 0.144 0.003 PCLineCounter.py:82(CountFileLines)
33 0.005 0.000 0.005 0.000 {open}
1 0.004 0.004 0.005 0.005 collections.py:1(<module>)
4028/4020 0.004 0.000 0.004 0.000 {len}
57 0.004 0.000 0.004 0.000 {nt._isdir}
259 0.002 0.000 0.003 0.000 ntpath.py:96(splitdrive)
1 0.002 0.002 0.007 0.007 argparse.py:62(<module>)
1 0.002 0.002 0.168 0.168 PCLineCounter.py:6(<module>)

为避免制作单个exe时体积过大,作者拷贝Lib\encodings目录后,删除该目录内不需要的语言文件并重命名为encodings_trim。

最后,需要指出的是,本文实现未区分三引号块注释与参与赋值或计算的字符串(如s='''devil'''money=10 if '''need''' else 0),也未处理单个单引号或双引号括起的未命名字符串(如"I'm a bad comment")。毕竟,这些并非良好的Python编程风格。而且,实际应用中其实并不要求非常准确地统计代码行和注释行。

Python代码统计工具的更多相关文章

  1. Python实现代码统计工具——终极加速篇

    Python实现代码统计工具--终极加速篇 声明 本文对于先前系列文章中实现的C/Python代码统计工具(CPLineCounter),通过C扩展接口重写核心算法加以优化,并与网上常见的统计工具做对 ...

  2. python 练习(一)代码统计工具的实现

    最近部门成立了一个python学习小组,旨在让大家在做项目中开始成长起来,于是老大就给布置了第一个小任务:代码统计工具,具体的需求如下: 需求: . 能够统计指定目录下C++程序的代码行数. . C+ ...

  3. Python实现C代码统计工具(四)

    目录 Python实现C代码统计工具(四) 标签: Python 计时 持久化 声明 运行测试环境 一. 自定义计时函数 1.1 整个程序计时 1.2 代码片段计时 1.3 单条语句计时 二. 性能优 ...

  4. Python实现C代码统计工具(三)

    目录 Python实现C代码统计工具(三) 声明 一. 性能分析 1.1 分析单条语句 1.2 分析代码片段 1.3 分析整个模块 二. 制作exe Python实现C代码统计工具(三) 标签: Py ...

  5. Python实现C代码统计工具(二)

    目录 Python实现C代码统计工具(二) 声明 一. 问题提出 二. 代码实现 三. 效果验证 Python实现C代码统计工具(二) 标签: Python 代码统计 声明 本文将对<Pytho ...

  6. Python实现C代码统计工具(一)

    目录 Python实现C代码统计工具(一) 声明 一. 问题提出 二. 代码实现 三. 效果验证 四. 后记 Python实现C代码统计工具(一) 标签: Python 代码统计 声明 本文将基于Py ...

  7. C++代码统计工具

    自己前几天写的C++代码统计工具. http://pan.baidu.com/s/17SnnH

  8. Python代码分析工具

    Python代码分析工具:PyChecker.Pylint - CSDN博客 https://blog.csdn.net/permike/article/details/51026156

  9. python代码检查工具pylint 让你的python更规范

    1.pylint是什么? Pylint 是一个 Python 代码分析工具,它分析 Python 代码中的错误,查找不符合代码风格标准(Pylint 默认使用的代码风格是 PEP 8,具体信息,请参阅 ...

随机推荐

  1. keybd_event 被 SendInput 替代

    keybd_event   函数功能:该函数合成一次击键事件.系统可使用这种合成的击键事件来产生WM_KEYUP或WM_KEYDOWN消息,键盘驱动程序的中断处理程序调用keybd_event函数.在 ...

  2. 版本视图找不到数据 EDITIONING VIEW

    Oracle database 12 以后的版本,特别在EBS R12.2.X加入了版本视图这种技术,跟MOAC有点像. CREATE OR REPLACE FORCE EDITIONING VIEW ...

  3. A very simple C++ module to encrypt/decrypt strings based on B64 and Vigenere ciper.

    A very simple C++ module to encrypt/decrypt strings based on B64 and Vigenere ciper. https://github. ...

  4. 关于 TVM

    偶然间对 arm 中 mali 显示核心感兴趣,找到的 TVM.将了解到的信息做个备忘. TVM 是 Tensor Virtual Machine 的所写? 官网上,TVM 定义自己为一种 Inter ...

  5. 新课程网上选课系统V1.0—适用于中小学校本课程选课、选修课选课

    学校要开设选修课,人工选课实施了两年,耗时耗力,于是打算用网上选课,在网上搜索了一番,没多少实用的,有一个网上用的比较多的,功能太简单了,于是打算自己开发一个,功能参考了部分学校的功能,也有基于Aja ...

  6. Wifiner for Mac(WiFi 状况分析工具)破解版安装

    1.软件简介    Wifiner 是 macOS 系统上一款 Wifi 分析工具,仅需几次点击即可对您的 Wi-Fi 网络连接进行分析和故障排除.扫描您的 Wi-Fi 网络,获取包含交互式彩色编码热 ...

  7. C#面试题(转载)

    原文地址:100道C#面试题(.net开发人员必备)  https://blog.csdn.net/u013519551/article/details/51220841 1. .NET和C#有什么区 ...

  8. javascript中的this在不同场景下的区别

    javascript在初版的设计上存在失误,导致了这门语言在使用时,经验型写法并不能得到像其它几个流行语言一样预期.其中的this的使用就是一个典型. this在javascript中是由解释器注入的 ...

  9. Hexo NexT 博客后台管理指南

    上篇文章讲到,将Hexo NexT 博客成功上传到GitHub 并绑定到我们自定义的域名下了. 但是却还是有个问题,那就是Hexo NexT 博客如何进行后台管理呢? 如果总是通过手动创建文件的方式, ...

  10. 系统用户在Samba服务器中起一个别名

    (1)通过/etc/samba/smbusers文件设置用户映射关系 如实列: # cat /etc/samba/smbusers # Unix_name = SMB_name1 SMB_name2 ...