【Python CI】圈复杂度 lizard

1. 圈复杂度

1.1 什么是圈复杂度?

引用[1]
圈复杂度是 Thomas J. McCabe 在 1976年开创的软件指标,用来判断程序的复杂度。这个指标度量源代码中线性独立的路径或分支的数量。根据 McCabe 所说,一个方法的复杂度最好保持在10 以下。这是因为对人类记忆力的研究表明,人的短期记忆只能存储 7 件事(偏差为正负 2)。

如果开发人员编写的代码有 50 个线性独立的路径,那么为了在头脑中描绘出方法中发生的情况,需要的记忆力大约超过短期记忆容量的 5倍。简单的方法不会超过人的短期记忆力的极限,因此更容易应付,事实证明它们的错误更少。Enerjy 在 2008年所做的研究表明,在圈复杂度与错误数量之间有很强的相关性。复杂度为 11 的类的出错概率为 0.28,而复杂度为 74的类的出错概率会上升到 0.98。

1.2 计算原理

TODO

2. lizard

Git源码链接 https://github.com/terryyin/lizard

  • 默认递归分析文件夹
  • 支持15中开发语言: C/C++ (works with C++14); Java; C# (C Sharp); JavaScript (With ES6 and JSX); Objective-C; Swift; Python; Ruby; TTCN-3; PHP; Scala; GDScript;Golang; Lua; Rust
2.1 依赖安装

方式一 pip安装

pip install lizard

方式二 源码安装

python setup.py install --install-dir=/path/to/installation/directory/

方式三 直接运行
直接将lizard.py及依赖的目录移动到待检测的文件目录中, 直接在命令行中运行以下命令,控制台数据结果表格

python lizard.py 
2.2 使用

命令行使用

lizard [options] [PATH or FILE] [PATH] ...
# 示例 检测某目录下 除 tests目录下 Python文件
lizard mySource/ -x"./tests/*" -l python

-h, --help            显示帮助并退出
--version             显示当前lizard版本并退出
-l LANGUAGES, --languages LANGUAGES
                      开发语言列表, 检测支持的开发语言 例如:lizard -l cpp -l java
                      支持语言:cpp, csharp, java, javascript, objectivec, php, python, ruby, swift, ttcn
-V, --verbose         Output in verbose mode (long function name)
-C CCN, --CCN CCN     Threshold for cyclomatic complexity number warning.
                      The default value is 15. Functions with CCN bigger
                      than it will generate warning
-L LENGTH, --length LENGTH
                      Threshold for maximum function length warning. The
                      default value is 1000. Functions length bigger than it
                      will generate warning
-a ARGUMENTS, --arguments ARGUMENTS
                      Limit for number of parameters
-w, --warnings_only   Show warnings only, using clang/gcc's warning format
                      for printing warnings.
                      http://clang.llvm.org/docs/UsersManual.html#cmdoption-
                      fdiagnostics-format
-i NUMBER, --ignore_warnings NUMBER
                      If the number of warnings is equal or less than the
                      number, the tool will exit normally, otherwise it will
                      generate error. Useful in makefile for legacy code.
-x EXCLUDE, --exclude EXCLUDE    不检测文件
                      Exclude files that match this pattern. * matches
                      everything, ? matches any single character,
                      "./folder/*" exclude everything in the folder
                      recursively. Multiple patterns can be specified. Don't
                      forget to add "" around the pattern.
--csv                 Generate CSV output as a transform of the default
                      output
-X, --xml             Generate XML in cppncss style instead of the tabular
                      output. Useful to generate report in Jenkins server
-t WORKING_THREADS, --working_threads WORKING_THREADS
                      number of working threads. The default value is 1.
                      Using a bigger number can fully utilize the CPU and
                      often faster.
-m, --modified        Calculate modified cyclomatic complexity number,
                      which count a switch/case with multiple cases as
                      one CCN.
-E EXTENSIONS, --extension EXTENSIONS
                      User the extensions. The available extensions are:
                      -Ecpre: it will ignore code in the #else branch.
                      -Ewordcount: count word frequencies and generate tag
                      cloud. -Eoutside: include the global code as one
                      function.
-s SORTING, --sort SORTING
                      Sort the warning with field. The field can be nloc,
                      cyclomatic_complexity, token_count, parameter_count,
                      etc. Or an customized file.
-W WHITELIST, --whitelist WHITELIST  白名单
                      The path and file name to the whitelist file. It's
                      './whitelizard.txt' by default.
结果解读
NLOC    the nloc (lines of code without comments),
CCN     cyclomatic complexity number
token   token count of functions.
param   parameter count of functions.

结果样例

================================================== ============
  NLOC CCN令牌参数function @ line @ file
-------------------------------------------------- ------------
    10 2 29 2 start_new_player @ 26 @。/ html_game.c
   ...
     6 1 3 0 set_shutdown_flag @ 449 @。/ httpd.c
    24 3 61 1 server_main @ 454 @。/ httpd.c
-------------------------------------------------- ------------
2文件分析。
================================================== ============
LOC Avg.NLOC AvgCCN Avg.ttoken function_cnt文件
-------------------------------------------------- ------------
    191 15 3 51 12 ./html_game.c
    363 24 4 86 15 ./httpd.c

=====================================
!!!! 警告(CCN> 15)!!!!
=====================================
    66 19 247 1 accept_request @ 64 @。/ httpd.c
================================================== ==============================
NLOC平均总NLOC平均CCN平均代币Fun Cnt警告cnt Fun Rt NLOC Rt
-------------------------------------------------- ------------------------------
       554 20 4.07 71.15 27 1 0.04 0.12
更多用法:
  • 白名单设置
  • CNN阈值设置
  • 重复代码检测
2.* Jenkins中配置

TODO

3. PyCmetrics

TODO

4.pygenie

TODO

5. 圈复杂度优化建议

  • 尽量少用 if …else … ,等分支语句
  • 每个函数要有明确的功能实现,不要为了追求行数少而合并功能实现
  • 模型数据的赋值、校验最好放在 model 自己里面
  • 逻辑模块和数据模块要区分开编写

引用

[1] 用 Python 编写干净、可测试、高质量的代码

剪刀石头布蜥蜴Spock(也被称为Scissors, Paper, Lizard, Spock)是Python中一种扩展的传统剪刀石头布游戏,Spock是由科幻剧《星际迷航》中的虚构生物Mr. Spock创造的,规则是用Spock代表"石头"和"水"之间的关系,Spock赢过石头(Rock),但输于蜥蜴(Lizard)。游戏规则如下: 1. 剪刀(Scissors)胜过布(Paper); 2. 布(Paper)胜过石头(Rock); 3. 石头(Rock)胜过蜥蜴(Lizard); 4. 蜥蜴(Lizard)胜过剪刀(Scissors); 5. Spock(通常是代表蜥蜴和石头)对任何都无胜无负,包括它自己。 在Python中实现这个游戏,你可以创建一个函数来判断两个玩家的选择并返回结果。这里是一个简单的示例代码: ```python def game_choice(player1, player2): if player1 == "Spock": if player2 in ["Scissors", "Lizard"]: return "Player 2 wins" else: return "Player 1 wins (ties)" elif player1 == "Scissors": if player2 == "Paper": return "Player 2 wins" else: return "Player 1 wins" elif player1 == "Paper": if player2 == "Rock": return "Player 2 wins" else: return "Player 1 wins" elif player1 == "Rock": if player2 == "Lizard": return "Player 2 wins" else: return "Player 1 wins" elif player1 == "Lizard": if player2 == "Scissors": return "Player 2 wins" else: return "Player 1 wins" else: return "Invalid choice" # 示例玩家输入 player1 = input("Player 1, choose (Scissors, Paper, Rock, Lizard, Spock): ") player2 = input("Player 2, choose the same: ") print(game_choice(player1, player2)) ``` 你可以运行这个代码,并让用户输入选择,然后看看游戏结果。当然,为了增加趣味性,你可以封装成一个循环,让游戏进行多次或添加一个用户界面。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值