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. [Android Pro] so 动态加载—解决sdk过大问题

    原文地址: https://blog.csdn.net/Rong_L/article/details/75212472 前言 相信Android 开发中大家或多或少都会集成一些第三方sdk, 而其中难 ...

  2. 几款流行的HTML5 UI 框架比较

    手机HTML5开发,大部分都使用现有的框架,这里简单比较几个流行的UI 框架.作者(启明星工作室 http://www.dotnetcms.org) 比较一下几款流行的HTML5框架,个人意见,仅供参 ...

  3. 9.7 dubbo心跳机制

    dubbo的心跳机制: 目的:检测provider与consumer之间的connection连接是不是还连接着,如果连接断了,需要作出相应的处理. 原理: provider:dubbo的心跳默认是在 ...

  4. VS2010环境下Winpcap配置方法 (转)

    VS2010 配置Winpcap 新建一个项目,GetDevs.cpp.用来测试.测试代码最后有给出. View->Property Manager Debug|Win32 -> Mirc ...

  5. Gradle Build Tool

    转自知乎: nonesuccess 通俗的说:gradle是打包用的. 你觉得解决你的问题了吗?如果没解决,那是你的问题提得不够好.比如我猜你应该提:为什么要打包发布,打包发布有几种常见方法,为什么这 ...

  6. 解决Ubuntu Chrome浏览器很卡不响应的问题

    1. 设定字体,使用Ubuntu Tweak Tool把系统字体设定为默认字体,而不是文泉驿字体: 2. 使用ADBLock Plus把垃圾的广告过滤掉,不然网页上很多Flash就会导致网页非常的卡顿 ...

  7. array2json() - Convert PHP arrays to JSON

    array2json is a PHP function that will convert the array given as its argument into a JSON string. T ...

  8. Ubuntu 下搭建 Android 开发环境(图文)

    转自 http://dawndiy.com/archives/153/ 1.安装JDK 1.下载JDK 目前最新的JDK版本是:Java SE Development Kit 7u5 下载地址: 查看 ...

  9. 【Android】详解Android Service

    目录结构: contents structure [+] Service简单概述 Service在清单文件中的声明 Service启动服务 Service绑定服务 扩展Binder类 使用Messen ...

  10. openssl - 怎么打开POD格式的帮助文件

    原文链接: http://zhidao.baidu.com/link?url=47I6A0YGA9FnK6galKZ5sxPSZzFGRdng2qhACb4ToBuhuyMhdrwcYpZmNI28y ...