理解造轮子的程度
造轮子造轮子,造法也有很多种,我们可以从零件厂采购轮毂、轮胎,自己组装,也可以从冶金、找橡胶树资源开始。
1.1 整体引用
这一种方法省事,相当于只把内部的组件打乱重组,包装成一个新的样子出来。Pipenv 即属此类,它其实是由 pip(安装器)、virtualenv(虚拟环境)、pip-tools(依赖解析)几大部分组合而成,连接调度的方式居然是通过 subprocess call。
所以这里面子进程启动、输出结果解析,都是耗时的。其余的次要组件,包括依赖树显示、依赖安全性检查等,无一例外都是通过内嵌别的库实现的。这种方法,很懒,引用作者 Kenneth Reitz 本人的话,叫做
I (re)design beautiful APIs
这种有一大缺点,即无论要做什么,例如“缺陷修复”、feature 引入都非常依赖上游库的更新,非常不自由。
比如要安装一个包,用这种方法实现出来是这个样子:
1 | def install_requirement(requirement): |
1.2 使用内部API
当对于上游库的修改多到了一定程度,一气之下,决定化整为零,把依赖的库拆散,只取它内部的结构和接口来做。还是同样的功能,用 pip 内部的 API 实现起来,是这个样子的:
1 | def install_requirement(requirement): |
看看那一串长长的 import string
,人家都叫做 internal
还带下划线了,都挡不住你要从里面 import。
这就是这种方法的问题所在:非常不稳定。可能下个版本,pip 就升级了,API 会变得完全不同,那么就要做相应的改变。
1.3 自己动手,丰衣足食
改了几个版本以后,心里暗骂了一句 pip 的祖宗,责怪它为什么老改 API,一怒之下全部推倒重来,不求别人,全都自己实现,于是这一版代码变成了这样:
1 | def install_requirement(requirement): |
这里面省略了很多代码,这里的每一个函数,背后都是几十上百行的代码,因为 requirement 的类型是很多的,有本地的文件、有 Git 的地址,有的带 marker,有的带 extras……要覆盖到这所有的情况,难免出 bug。
这时就体现出用第三方库的优点来了:它们可能已经帮我们把所有的 bug 都踩过了,并受过生产环境的考验。
1.4 总结
所以造轮子要用哪种方法来造,是要经过仔细地考量的。
用了第一种方法,结果发现需要定制很多
用了第二种方法,发现被牵着走,依赖的 API 一改动,自己就要迭代
用了第三种方法,结果发现天天都在修 bug