Python3:函数的圈复杂度

你有没有见过那种长达几百行、逻辑错综复杂的“巨无霸”函数?那样的函数不光难读,改起来同样困难重重,人人唯恐避之不及。

编写函数最重要的原则就是:别写太复杂的函数。那什么样的函数才能算是过于复杂?一般会通过两个标准来判断,长度和圈复杂度

长度

长度也就是函数有多少行代码。不过不能武断地说,长函数就一定比短函数复杂。因为在不同的编程风格下,相同行数的代码所实现的功能可以有巨大差别,有人甚至能把一个完整的俄罗斯方块游戏塞进一行代码内。

但即便如此,长度对于判断函数复杂度来说仍然有巨大价值。在著作《代码大全(第 2 版)》中,Steve McConnell 提到函数的理想长度范围是 65 到 200 行,一旦超过 200 行,代码出现 bug 的概率就会显著增加。

对于 Python 这种强表现力的语言来说,65 行已经非常值得警惕了。假如你的函数超过 65 行,很大概率代表函数已经过于复杂,承担了太多职责,请考虑将它拆分为多个小而简单的子函数(类)吧。

圈复杂度

“圈复杂度”是由 Thomas J. McCabe 在 1976 年提出的用于评估函数复杂度的指标。它的值是一个正整数,代表程序内线性独立路径的数量。圈复杂度的值越大,表示程序可能的执行路径就越多,逻辑就越复杂。

如果某个函数的圈复杂度超过10,就代表它已经太复杂了,代码编写者应该想办法简化。优化写法或者拆分成子函数都是不错的选择。接下来,我们通过实际代码来体验一下圈复杂度的计算过程。

在Python中,可以通过radon工具计算函数的圈复杂度。安装命令:

pip3 install radon

假设我们有段代码示例如下,实现的功能是猜数字游戏,里面有1个whilie和2个if-else分支判断逻辑,文件名:complex_func.py。

import randomdef guess_number():# 生成一个随机数作为答案answer = random.randint(1, 100)# 初始化猜测次数guesses = 0print("欢迎来到猜数字游戏!我已经想好了一个1到100之间的数字,你需要猜出这个数字是多少。")# 开始循环,直到玩家猜中数字为止while True:# 获取玩家的猜测guess = int(input("请输入你猜测的数字:"))# 增加猜测次数guesses += 1# 检查玩家猜测的数字与答案的关系if guess < answer:print("你猜的数字太小了,请继续努力!")elif guess > answer:print("你猜的数字太大了,请再试一次!")else:print(f"恭喜你,你猜对了!答案是 {answer}。你一共猜了 {guesses} 次。")break  # 结束循环# 调用函数开始游戏
guess_number()

接下来我们使用radon来计算这个文件对应函数的圈复杂度,文件名:calculate_cyclomatic_complexity.py

from radon.complexity import cc_visit# 定义一个Python文件路径
file_path = 'complex_func.py'# 使用cc_visit函数计算代码的圈复杂度
with open(file_path, 'r') as file:code = file.read()results = cc_visit(code)print(results)# 打印结果
for result in results:print(result)

执行结果:可以看到函数圈复杂度为 4

$ python3 calculate_cyclomatic_complexity.py 
[Function(name='guess_number', lineno=3, col_offset=0, endline=27, is_method=False, classname=None, closures=[], complexity=4)]
F 3:0->27 guess_number - 4

我们接下来看另外一个完整的代码示例,其中被计算的函数为rank(),功能是按照电影分数计算评级,最后输出了圈复杂度和对应的评分等级,文件名:get_film_score.py

import radon
from radon.complexity import cc_rank, cc_visitdef calculate_complexity(source_code):"""Calculate the cyclomatic complexity of the given source code.Parameters:source_code (str): The source code to analyze.Returns:int: The cyclomatic complexity.str: The complexity rating."""try:# Visit the AST and calculate the complexityresults = cc_visit(source_code)complexity = results[0].complexity# Get the complexity ratingrating = cc_rank(complexity)return complexity, ratingexcept Exception as e:print("Error:", e)return None, None# Example usage:
if __name__ == "__main__":code = """
def rank(self):rating_num = float(self.rating)if rating_num >= 8.5:return 'S'elif rating_num >= 8:return 'A'elif rating_num >= 7:return 'B'elif rating_num >= 6:return 'C'else:return 'D'"""complexity, rating = calculate_complexity(code)if complexity is not None and rating is not None:print("Cyclomatic Complexity:", complexity)print("Complexity Rating:", rating)

运行结果:可以看到函数圈复杂度为 5,评级为 A

虽然这个值没有达到危险线 10,但考虑到函数只有短短 10 行,5 已经足够引起重视了。

$ python3 get_film_score.py
Cyclomatic Complexity: 5
Complexity Rating: A

作为对比,我们再计算一下案例中使用bisect模块重构后的 rank() 函数

def rank(self):breakpoints = (6, 7, 8, 8.5)grades = ('D', 'C', 'B', 'A', 'S')index = bisect.bisect(breakpoints, float(self.rating))return grades[index]

运行结果:可以看到函数圈复杂度为 1,评级为 A。

$ python3 get_film_score.py
Cyclomatic Complexity: 1
Complexity Rating: A

可以看到,新函数的圈复杂度从 5 降至 1。1 是一个非常理想化的值,如果一个函数的圈复杂度为 1,就代表这个函数只有一条主路径,没有任何其他执行路径,这样的函数通常来说都十分简单、容易维护。

当然,在正常的项目开发流程中,我们一般不会在每次写完代码后,都手动执行一次 radon 命令检查函数圈复杂度是否符合标准,而会将这种检查配置到开发或部署流程中自动执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/827684.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式- 中介者模式(Mediator)

1. 概念 中介者模式&#xff08;Mediator Pattern&#xff09;&#xff0c;是一种对象行为型模式。该模式的主要目的是定义一个中介对象来封装一系列对象之间的交互&#xff0c;使原有对象之间的耦合变得松散&#xff0c;并且可以独立地改变它们之间的交互。 2. 原理结构图 抽…

【数据分析】学习笔记day1

sklearn与经典机器学习算法 机器学习的利器——sklearn机器学习的7个流程:sklearn的功能主要分为六大部分:目标: 1、掌握sklearn的基本用法 2、掌握线性回归的原理,并进行实践操作 3、理解监督学习经典算法、如K-近邻算法 4、理解非监督学习经典算法机器学习的利器——skle…

标准化,信息化,数字化,智能化

随着科技的飞速发展&#xff0c;标准化、信息化、数字化和智能化已经成为当今社会的主要发展趋势。这些趋势正在改变我们的生活、工作和社会&#xff0c;带来了前所未有的机遇和挑战。在这个快速变化的时代&#xff0c;我们是否已经做好了迎接未来的准备呢&#xff1f; 标准化…

Python Selenium无法打开Chrome浏览器处理自定义浏览器路径

问题 在使用Python Selenium控制Chrome浏览器操作的过程中&#xff0c;由于安装的Chrome浏览器的版本找不到对应版本的驱动chromedriver.exe文件&#xff0c;下载了小几个版本号的驱动软件。发现运行下面的代码是无法正常使用的&#xff1a; from selenium import webdriver …

Java中equals()方法的理解与使用

Java中equals()方法的理解与使用 在Java中&#xff0c;equals()方法是用于比较两个对象是否相等的重要方法。它属于Object类的方法&#xff0c;因此所有的Java对象都继承了这个方法。但是&#xff0c;Object类中的equals()方法默认实现是比较两个对象的引用是否相同&#xff0…

FPGA“题目周周练”活动来啦!

Hi&#xff0c;各位编程精英er~ 不知道大家的FPGA学习之旅到达哪一个阶段了呢&#xff1f;又在这个过程中遇到了哪些困惑&#xff1f; 作为一门高度专业化且充满挑战的技术&#xff0c;FPGA的学习是一场不断思考、认知、持续深化的过程。在这个过程中&#xff0c;思维的敏捷和…

【vue2】实现微信截图(复制图片)在项目内可粘贴

需求 后台管理在上传图片地方需要将复制的图片粘贴上传 一、添加事件 在原有上传组件的基础上添加 paste事件 二、方法 onPaste(e) {const items (e.clipboardData || window.clipboardData).items;let blob null;for (let i 0; i < items.length; i) {if (items[i].ty…

034——从GUI->Client->Server->driver实现读写EEPROM

目录 1、修改GUI 2、修改client 3、 修改server 4、 修改driver_handele 5、 测试和提交 1、修改GUI 之前叫IIC&#xff0c;我们其实是借助EEPROM来测试IIC是不是好用所以名称改一改 长得有点奇怪 这样虽然一样长了但是还是很奇怪。 就先这样吧 layout_l [[tool.name(N…

100个实用电气知识

在当今社会&#xff0c;电力作为日常生活和工作中不可或缺的能源&#xff0c;扮演着越来越重要的角色。为了更好地利用电力资源&#xff0c;了解电气知识成为了越来越多人的需求。在电气领域&#xff0c;有很多实用的知识&#xff0c;这些知识对于从事电气工作的人来说是非常重…

二叉树oj题(2)

1.二叉树的最近公共祖先 解题思路&#xff1a;方法一&#xff1a; 1.先判断p或者q 是不是 root当中的一个 2.左子树当中递归査找p或者q 3.右子树当中递归查找p或者q 如何查找: root 的 left 和 right 都不为空 ->root root的 left 为空 right 不为空->right这一侧找…

出海不出局 | 小游戏引爆高线市场,新竞争态势下的应用出海攻略

出海小游戏&#xff0c;出息了&#xff01; 根据 Sensor Tower 近期发布的“2024 年 3 月中国手游收入 TOP30”榜单&#xff0c;出海小游戏在榜单中成了亮眼的存在。 其中&#xff0c;《菇勇者传说》3 月海外收入环比增长 63%&#xff0c;斩获出海手游收入增长冠军&#xff0c…

vue element-ui 表格横向滚动条在合计项下方

目前效果 需求效果 1.隐藏bodyWrapper滚动条&#xff0c;显示footerWrapper滚动条 css代码如下&#xff1a; div ::v-deep .el-table--scrollable-x .el-table__body-wrapper{overflow-x: hidden!important;z-index: 2!important;} div ::v-deep .el-table__footer-wrapper …

理发师问题的业务建模方案

背景 题目&#xff1a; 假设有一个理发店只有一个理发师&#xff0c;一张理发时坐的椅子&#xff0c;若干张普通椅子顾客供等候时坐。没有顾客时&#xff0c;理发师睡觉。顾客一到&#xff0c;叫醒理发师 。如果理发师没有睡觉&#xff0c;而在为别人理发&#xff0c;他就会坐…

Llama改进之——均方根层归一化RMSNorm

引言 在学习完GPT2之后&#xff0c;从本文开始进入Llama模型系列。 本文介绍Llama模型的改进之RMSNorm(均方根层归一化)。它是由Root Mean Square Layer Normalization论文提出来的&#xff0c;可以参阅其论文笔记1。 LayerNorm 层归一化(LayerNorm)对Transformer等模型来说…

Android—— log的记忆

一、关键log 1.Java的 backtrace(堆栈log) 上述是一个空指针异常&#xff0c;问题出现在sgtc.settings&#xff0c;所以属于客户UI问题。 2.WindowManager(管理屏幕上的窗口和视图层次结构) 3.ActivityManager(管理应用程序生命周期和任务栈) 4.wifi操作 (1) 连接wifi&#…

开源大模型Llama3,堪比GPT-4。手把手本地安装,纯小白可操作,不需要编程经验,国内可下载,可视化使用。

最近最劲爆科技动态&#xff0c;Meta开源Llama3模型&#xff0c;最强开源模型。 Llama3发布后&#xff0c;扎克伯格亲自给媒体表示“要超越所有人&#xff0c;做最领先AI”。 吴恩达等一众大佬表示祝贺。 在线体验地址&#xff1a;https://www.meta.ai/ 不过国内在线体验基本…

算法人生(11):从“梯度提升树(GBDT)”看“2/8时间管理法”

梯度提升树&#xff08;Gradient Boosting Decision Trees, GBDT&#xff09;是一种高效的机器学习算法&#xff0c;它通过迭代构建多个决策树并综合它们的预测结果来提高预测的准确率。GBDT的核心思想在于&#xff0c;每一棵树都尝试对前一棵树留下的预测残差进行修正&#xf…

OpenWRT磁盘扩容(PVE虚拟机方案)

官方扩容指导文档 PVE给虚拟机磁盘扩容 给虚拟机磁盘扩容&#xff0c;选中OpenWRT的硬盘&#xff0c;随后选择调整大小 输入增量大小&#xff0c;即增加多少磁盘空间给硬盘。这里我选择增加4G 进入OpenWRT控制台界面安装一些linux常用查看磁盘的工具&#xff08;也可以通过网…

EasyPoi实现简单的Excel导出、导入

EasyPoi实现Excel导出、导入 下面这种方式不需要模板&#xff0c;更加方便但是不能进行复杂的导出 一、引入依赖 <dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-base</artifactId><version>4.4.0</version><…

XiaodiSec day010 Learn Note 小迪安全学习笔记

XiaodiSec day010 Learn Note 小迪安全学习笔记 记录得比较凌乱&#xff0c;不尽详细 day10 no10 app 小程序 抓包封包 工具 AppInfoScanner 这个工具能对目标 app 进行反编译 oss 存储服务?? 一个服务&#xff0c;在阿里云中 工具&#xff1a;安卓修改大师 一个反编译…