如何写出优雅的代码
来源:Python 技术「ID: pythonall」
刚开始学编程的时候,最激动人心,也最捉人眼球的,莫过于各种炫酷拉风的功能了。
即使再深入一点,我们关注的通常也都是功能。只要功能实现了,什么代码风格、代码格式啥的,那都是无关痛痒的细节,who又care呢?
什么?PEP8?你说什么,大点儿声,我听不见——
代码规范是什么?
所谓“不以规矩,不能成方圆”。
在编程实践中,代码规范就是这个“规矩”;整洁易读可维护的代码就是这个“方圆”。而开发、协作的效率,就是层层掩藏下我们真正追求的东西。
——当然,更抽象地来说,如果说代码就是开发人员的作品,那么有的开发者是手工艺人,而有的开发者则可以称为艺术家。
艺术家怎么来的呢?齐白石老人家有云:学我者生,似我者死。成为艺术家的道路是曲折的——废话了哈哈。
首先我们得学习前人留下的宝贵经验,从中汲取养分,为我所用。不是说
我们为什么需要代码规范
不知道读者有没有被各种各样的代码文件格式搞晕过。
文件格式到底有多少种呢?只看一看vscode支持的保存格式就可见一斑:
然鹅,实际上他们的内容没有什么本质的区别,都是“文本文件”。也就是说,都跟纯文本文件以及我们最常见的.txt
文件是一伙的。这也就是为什么江湖上总流传着“大神都用记事本编程”这样的轶事——记事本写一个.txt
也是写,多写一个.c
也是写,再写写.py
、.go
、.java
、.rs
又有何不妥?“无他,唯手熟尔。”
也就是说,只要内容没问题,即使文件后缀是.txt
,也是可以作为程序源文件的。最直接的例子就是Python。
将以下内容保存为文件test_text.txt
文件:
print("This is intterpreted from .txt")
再在当前路径下命令行执行:
$ python test_text.txt
// This is intterpreted from .txt
可以看到,程序源文件的后缀并不影响Python解释器的解释。
更进一步,把.txt
改为.cpp
:
$ python test_text.cpps
// This is intterpreted from .cpp
众所周知,计算机对文本其实是不怎么感冒的,实际的文本文件存储在计算机内部依然是二进制数据,和其他的二进制文件没啥两样。
那我们为什么还要多此一举,把程序源文件存储为文本文件呢?直接用二进制难道不香吗?
是的。不香。
说一千道一万,我们写程序并不是为了给计算机看的,而是为别人甚至是为自己而写的——计算机只需要看到二进制就好了。
对计算机而言用不到的、各种曲里拐弯的编码规范,对人类而言就是一个整洁完备、定义良好的“接口”,更是一种人与人之间的“通信协议”。使用统一、高效的通信协议,能够大大降低开发者之间的交流成本,显著提升开发效率。
在网络通信中,通信协议有多重要,那么在软件开发领域,编程规范就有多重要。
编程规范老是忘,pylint来帮忙
说完编程规范的重要性,接下来我们还需要面对一个现实的问题:编程规范好是好,奈何记不住啊。
是的,编程规范的内容太多了。别的不说,单只Python一门语言,就有各种各样的增强提案(PEP),很多提案中都涉及到对编程风格的规范,即使有了一段时间的实际开发经验,也不一定真的能一一记住这些条目的内容。
这个时候自然而然地就会想到借助工具。好在,现代的IDE乃至vscode这样的编辑器,都能够针对不同的程序语言提供相应的代码检查功能。对于检查到的、不符合编程规范的条目都会划线甚至高亮显示出来,提示开发者进行修改。
但一来,这是被动的规范检查,可控性不强;二来,检查内容可能不是很全面,难免遗漏。
这里要介绍的就是一种主动检查代码规范的方式:使用pylint。
pylint的安装就不再赘述了,一如其他Python模块,直接pip一把梭就好:
$ pip install pylint
安装好之后使用就很简单了,pylint + [目标文件名]
即可。
将如下文本保存为文件justdopython.py
:
def just():
print("just")
pass
def do():
print("dopython")
pass
然后执行命令:
$ pylint justdopython.py
// ************* Module justdopython
// test_text.py:11:0: C0304: Final newline missing (missing-final-newline)
// test_text.py:1:0: C0111: Missing module docstring (missing-docstring)
// test_text.py:1:0: C0111: Missing function docstring (missing-docstring)
// test_text.py:3:4: W0107: Unnecessary pass statement (unnecessary-pass)
// test_text.py:6:0: C0103: Function name "do" doesn't conform to snake_case naming style (invalid-name)
// test_text.py:6:0: C0111: Missing function docstring (missing-docstring)
// test_text.py:8:4: W0107: Unnecessary pass statement (unnecessary-pass)
// -------------------------------------------------------------------
// Your code has been rated at -1.67/10 (previous run: 0.00/10, -1.67)
逐条看过去,可以看到输出内容首先列出了不符合规范的条目,分别给出了与规范冲突的位置(行列)、违反的规范编号以及具体违反的内容和类型。最后pylint还给源文件打了个分。
好嘛,满分10分,得了个-1.67分……
注意其中第一个与规范不符的条目“Final newline missing”。Python编程规范推荐我们在文件的最后留一个空行。
随后的“Missing module docstring”和“Missing function docstring”都是缺少对模块和函数的描述字符串导致的,我们按要求加上即可;既方便快速理解相应模块/函数,又可以用于之后自动化生成文档。
当然也有的人总是懒得给函数写描述文档,此时可以使用特殊的注释来禁止pylint对某项规范进行检查:
def just(): # pylint: disable=missing-docstring
print("just")
pass
这样再使用pylint扫描源文件,此处就不会再报告相关冲突了。
再往下,还提示我们函数名“do”不符合蛇形命名风格——所谓蛇形命名,也就是全小写、单词之间使用下划线连接的命名风格,也是Python推荐的命名风格。
剩下一种“Unnecessary pass statement”则是提示pass
语句多余,修复也就是了。
最后还需要提示的是,虽然pylint可以通过注释或者配置文件,取消掉某些规范的提示。但是依然建议尽量不要人为禁止相关提示。
有问题需要解决,而不是掩耳盗铃当做问题不存在。
随着项目中代码量的增长,前期遗留的小问题再想修复,成本会越来越高。
总结
今天我们讲了编程规范,希望小伙伴们平时多注意,把自己写的代码作为艺术品来打磨,写出风格良好的代码。