Python代码统计工具
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
使用未命名三引号字符串做注释时,存在如下缺点:
- 未命名字符串本质上并非注释,而是不生成字节码的语句。因此,需要满足缩进要求(常错点)。
- 无法注释掉已包含相同三引号字符串的代码。
- 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代码统计工具的更多相关文章
- Python实现代码统计工具——终极加速篇
Python实现代码统计工具--终极加速篇 声明 本文对于先前系列文章中实现的C/Python代码统计工具(CPLineCounter),通过C扩展接口重写核心算法加以优化,并与网上常见的统计工具做对 ...
- python 练习(一)代码统计工具的实现
最近部门成立了一个python学习小组,旨在让大家在做项目中开始成长起来,于是老大就给布置了第一个小任务:代码统计工具,具体的需求如下: 需求: . 能够统计指定目录下C++程序的代码行数. . C+ ...
- Python实现C代码统计工具(四)
目录 Python实现C代码统计工具(四) 标签: Python 计时 持久化 声明 运行测试环境 一. 自定义计时函数 1.1 整个程序计时 1.2 代码片段计时 1.3 单条语句计时 二. 性能优 ...
- Python实现C代码统计工具(三)
目录 Python实现C代码统计工具(三) 声明 一. 性能分析 1.1 分析单条语句 1.2 分析代码片段 1.3 分析整个模块 二. 制作exe Python实现C代码统计工具(三) 标签: Py ...
- Python实现C代码统计工具(二)
目录 Python实现C代码统计工具(二) 声明 一. 问题提出 二. 代码实现 三. 效果验证 Python实现C代码统计工具(二) 标签: Python 代码统计 声明 本文将对<Pytho ...
- Python实现C代码统计工具(一)
目录 Python实现C代码统计工具(一) 声明 一. 问题提出 二. 代码实现 三. 效果验证 四. 后记 Python实现C代码统计工具(一) 标签: Python 代码统计 声明 本文将基于Py ...
- C++代码统计工具
自己前几天写的C++代码统计工具. http://pan.baidu.com/s/17SnnH
- Python代码分析工具
Python代码分析工具:PyChecker.Pylint - CSDN博客 https://blog.csdn.net/permike/article/details/51026156
- python代码检查工具pylint 让你的python更规范
1.pylint是什么? Pylint 是一个 Python 代码分析工具,它分析 Python 代码中的错误,查找不符合代码风格标准(Pylint 默认使用的代码风格是 PEP 8,具体信息,请参阅 ...
随机推荐
- JProfiler远程监控
1. 服务端安装JProfiler(与客户端版本一致) 2. 客户端配置连接: A).session——integration wizards——New remote integration B) ...
- 【docker】docker部署spring boot服务,但是docker logs查看容器输出控制台日志,没有日志打印,日志未打印,docker logs不打印容器日志
如题: docker部署spring boot服务,但是docker logs查看容器输出控制台日志,没有日志打印,日志未打印,docker logs不打印容器日志 场景再现: docker部署并启动 ...
- 关于#progma comment 中库文件相对路径问题
最近做一个验证程序的对话框编程,因为里面要要用到静态链接库,所以就稍微的学习了下静态链接库知识,学习的过程中感觉到了自己所了解的东西实在是少的可怜,更加坚定了自己要更加上进的决心,要把以前所丢掉的都给 ...
- 如何使用Cassandra来存储time-series类型的数据
Cassandra非常适合存储时序类型的数据,本文我们将使用一个气象站的例子,该气象站每分钟需要存储一条温度数据. 一.方案1,每个设备占用一行 这个方案的思路就是给每个数据源创建一行 ...
- 微软BI 之SSIS 系列 - Execute SQL Task 中的 Single Row 与 Full Result Set 的处理技巧
开篇介绍 Execute SQL Task 这个控件在微软BI ETL 项目中使用的频率还是非常高的,也是大部分入门 SSIS 初学者最早接触到的几个控制流控件. 我们通常使用 Execute SQL ...
- storm杂谈之Why use netty as transport instead of zeromq
Storm后来用Netty来代替了zmq,这个能够參考一下两篇blog 这两篇blog具体的阐述原因以及一些性能測试, 大家參考一下 Reference 1.Netty 4 Reduces GC Ov ...
- GraphQL,你准备好了么?
一个多月前,facebook 将其开源了一年多的 GraphQL 标记为 production ready ( http://graphql.org/blog/production-ready/ ), ...
- Django-启动文件的制作
在Django项目下的app.py中写入这几行代码,当启动的时候会找项目下名为:stark.py的文件并先加载 #app.py from django.apps import AppConfig cl ...
- CentOS 7 安装配置zabbix 3.2.8
运行环境:CentOS 7.2 LNMP(已提前安装好此环境) 1.首先导入zabbix安装源# rpm -ivh http://repo.zabbix.com/zabbix/3.2/rhel/7/x ...
- 1.关于Swift
关于SwiftSwift是一种新的编程语言,用于iOS和OS X的应用程序,建立在最佳的C和Objective-C之上,没有C兼容性的限制.Swift采用的安全模式设计,并增加了现代的功能,使编程更简 ...