返回

深思加密狗复制公众号;纯真网络

发布时间:2023-11-23 15:14:16 178

模块(Module)是我们用来组织 Python 代码的基本单位。很多功能强大的复杂站点,都由成百上千个独立模块共同组成。

虽然模块有着不可替代的用处,但它有时也会给我们带来麻烦。比如,当你接手一个新项目后,刚展开项目目录。第一眼就看到了攀枝错节、难以理解的模块结构,那你肯定会想:“这项目也太难搞了。”

在这篇文章里,我准备了一个和模块有关的小故事与你分享。


一个关于模块的小故事

小 R 是一个刚从学校毕业的计算机专业学生。半个月前,他面试进了一家互联网公司做 Python 开发,负责一个与用户活动积分有关的小项目。项目的主要功能是查询站点活跃用户,并为他们发送有关活动积分的通知:“亲爱的用户,您好,您当前的活动积分为 x”。

项目主要由 ​​notify_users.py​​ 脚本和 fancy_site 包组成,结构与各文件内容如下:

文件 ​​notify_users.py​​:

文件 ​​fancy_site/users.py​​:

文件:​​fancy_site/marketing.py​​:

只要在项目目录下执行 ​​python notify_user.py​​,就能实现给所有活跃用户发送通知。

需求变更

但有一天,产品经理找过来说,光给用户发站内信通知还不够,容易被用户忽略。除了站内信以外,我们还需要同时给用户推送一条短信通知。

琢磨了五秒钟后,小 R 跟产品经理说:“这个需求可以做!”。毕竟给手机号发送短信的 ​​send_sms()​​ 函数早就已经有人写好了。他只要先给 ​​add_notification​​ 方法添加一个可选参数 ​​enable_sms=False​​,当传值为 ​​True​​ 时调用 ​​fancy_site.marketing​​ 模块里的 ​​send_sms​​ 函数就行。

一切听上去根本没有什么难度可言,十分钟后,小 R 就把 ​​user.py​​ 改成了下面这样:

但是,当他修改完代码,再次执行 ​​notify_users.py​​ 脚本时,程序却报错了:

错误信息说,无法从 ​​fancy_site.users​​ 模块导入 ​​User​​ 对象。

解决环形依赖问题

小 R 仔细分析了一下错误,发现错误是因为 ​​users​​ 与 ​​marketing​​ 模块之间产生的环形依赖关系导致的。

当程序在 ​​notify_users.py​​ 文件导入 ​​fancy_site.users​​ 模块时, ​​users​​ 模块发现自己需要从 ​​marketing​​ 模块那里导入 ​​send_sms​​ 函数。而解释器在加载 ​​marketing​​ 模块的过程中,又反过来发现自己需要依赖 ​​users​​ 模块里面的 ​​User​​ 对象。

如此一来,整个模块依赖关系成为了环状,程序自然也就没法执行下去了。

不过,没有什么问题能够难倒一个可以正常访问 Google 的程序员。小 R 随便上网一搜,发现这样的问题很好解决。因为 Python 的 import 语句非常灵活,他只需要 把在 ​​users​​​ 模块内导入 ​​send_sms​​ 函数的语句挪到 ​​add_notification​​ 方法内,延缓 ​​import​​ 语句的执行就行啦

改动一行代码后,大功告成。小 R 简单测试后,发现一切正常,然后把代码推送了上去。不过小 R 还没来得及为自己点个赞,意料之外的事情发生了。

这段明明几乎完美的代码改动在 Code Review 的时候被审计人小 C 拒绝了。

小 C 的疑问

小 R 的同事小 C 是一名有着多年经验的 Python 程序员,他对小 R 说:“使用延迟 import,虽然可以马上解决包导入问题。但这个小问题背后隐藏了更多的信息。比如,你有没有想过 send_sms 函数,是不是已经不适合放在 marketing 模块里了?

被小 C 这么一问,聪明的小 R 马上意识到了问题所在。要在 users 模块内发送短信,重点不在于用延迟导入解决环形依赖。而是要以此为契机,发现当前模块间依赖关系的不合理,拆分/合并模块,创建新的分层与抽象,最终消除环形依赖

认识清楚问题后,他很快提交了新的代码修改。在新代码中,他创建了一个专门负责通知与消息类的工具模块 ​​msg_utils​​,然后把 ​​send_sms​​ 函数挪到了里面。之后 ​​users​​ 模块内就可以毫无困难的从 ​​msg_utils​​ 模块中导入 ​​send_sms​​ 函数了

新的模块依赖关系如下图所示:

在新的模块结构中,整个项目被整齐的分为三层,模块间的依赖关系也变得只有单向流动。之前在函数内部 ​​import​​ 的“延迟导入”技巧,自然也就没有用武之地了。

小 R 修改后的代码获得了大家的认可,很快就被合并到了主分支。故事暂告一段落,那么这个故事告诉了我们什么道理呢?


总结

模块间的循环依赖是一个在大型 Python 项目中很常见的问题,越复杂的项目越容易碰到这个问题。当我们在参与这些项目时,如果对模块结构、分层、抽象缺少应有的重视。那么项目很容易就会慢慢变得复杂无比、难以维护

所以,合理的模块结构与分层非常重要。它可以大大降低开发人员的心智负担和项目维护成本。这也是我为什么要和你分享这个简单故事的原因。“在函数内延迟 import” 的做法当然没有错,但我们更应该关注的是:整个项目内的模块依赖关系与分层是否合理

特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(0)
按点赞数排序
用户头像
精选文章
thumb 中国研究员首次曝光美国国安局顶级后门—“方程式组织”
thumb 俄乌线上战争,网络攻击弥漫着数字硝烟
thumb 从网络安全角度了解俄罗斯入侵乌克兰的相关事件时间线
下一篇
各种JS代码的混淆效果一览 2023-11-23 12:01:52