如何写出优雅的代码

如何写出优雅的代码

刚开始学编程的时候,最激动人心,也最捉人眼球的,莫过于各种炫酷拉风的功能了。

即使再深入一点,我们关注的通常也都是功能。只要功能实现了,什么代码风格、代码格式啥的,那都是无关痛痒的细节,who又care呢?

什么?PEP8?你说什么,大点儿声,我听不见——

代码规范是什么?

所谓“不以规矩,不能成方圆”。

在编程实践中,代码规范就是这个“规矩”;整洁易读可维护的代码就是这个“方圆”。而开发、协作的效率,就是层层掩藏下我们真正追求的东西。

——当然,更抽象地来说,如果说代码就是开发人员的作品,那么有的开发者是手工艺人,而有的开发者则可以称为艺术家。

艺术家怎么来的呢?齐白石老人家有云:学我者生,似我者死。成为艺术家的道路是曲折的——废话了哈哈。

首先我们得学习前人留下的宝贵经验,从中汲取养分,为我所用。

我们为什么需要代码规范

不知道读者有没有被各种各样的代码文件格式搞晕过。

文件格式到底有多少种呢?只看一看vscode支持的保存格式就可见一斑:

01

然鹅,实际上他们的内容没有什么本质的区别,都是“文本文件”。也就是说,都跟纯文本文件以及我们最常见的.txt文件是一伙的。这也就是为什么江湖上总流传着“大神都用记事本编程”这样的轶事——记事本写一个.txt也是写,多写一个.c也是写,再写写.py.go.java.rs又有何不妥?“无他,唯手熟尔。”

也就是说,只要内容没问题,即使文件后缀是.txt,也是可以作为程序源文件的。最直接的例子就是Python。

将以下内容保存为文件test_text.txt文件:

1
print("This is intterpreted from .txt")

再在当前路径下命令行执行:

1
2
$ python test_text.txt
// This is intterpreted from .txt

可以看到,程序源文件的后缀并不影响Python解释器的解释。

更进一步,把.txt改为.cpp

1
2
$ python test_text.cpps
// This is intterpreted from .cpp

众所周知,计算机对文本其实是不怎么感冒的,实际的文本文件存储在计算机内部依然是二进制数据,和其他的二进制文件没啥两样。

那我们为什么还要多此一举,把程序源文件存储为文本文件呢?直接用二进制难道不香吗?

是的。不香。

说一千道一万,我们写程序并不是为了给计算机看的,而是为别人甚至是为自己而写的——计算机只需要看到二进制就好了。

对计算机而言用不到的、各种曲里拐弯的编码规范,对人类而言就是一个整洁完备、定义良好的“接口”,更是一种人与人之间的“通信协议”。使用统一、高效的通信协议,能够大大降低开发者之间的交流成本,显著提升开发效率。

在网络通信中,通信协议有多重要,那么在软件开发领域,编程规范就有多重要。

编程规范老是忘,pylint来帮忙

说完编程规范的重要性,接下来我们还需要面对一个现实的问题:编程规范好是好,奈何记不住啊。

是的,编程规范的内容太多了。别的不说,单只Python一门语言,就有各种各样的增强提案(PEP),很多提案中都涉及到对编程风格的规范,即使有了一段时间的实际开发经验,也不一定真的能一一记住这些条目的内容。

这个时候自然而然地就会想到借助工具。好在,现代的IDE乃至vscode这样的编辑器,都能够针对不同的程序语言提供相应的代码检查功能。对于检查到的、不符合编程规范的条目都会划线甚至高亮显示出来,提示开发者进行修改。

但一来,这是被动的规范检查,可控性不强;二来,检查内容可能不是很全面,难免遗漏。

这里要介绍的就是一种主动检查代码规范的方式:使用pylint。

pylint的安装就不再赘述了,一如其他Python模块,直接pip一把梭就好:

1
$ pip install pylint

安装好之后使用就很简单了,pylint + [目标文件名]即可。

将如下文本保存为文件justdopython.py

1
2
3
4
5
6
7
8
def just():
    print("just")
    pass


def do():
    print("dopython")
    pass

然后执行命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ 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对某项规范进行检查:

1
2
3
def just(): # pylint: disable=missing-docstring
    print("just")
    pass

这样再使用pylint扫描源文件,此处就不会再报告相关冲突了。

再往下,还提示我们函数名“do”不符合蛇形命名风格——所谓蛇形命名,也就是全小写、单词之间使用下划线连接的命名风格,也是Python推荐的命名风格。

剩下一种“Unnecessary pass statement”则是提示pass语句多余,修复也就是了。

最后还需要提示的是,虽然pylint可以通过注释或者配置文件,取消掉某些规范的提示。但是依然建议尽量不要人为禁止相关提示。

有问题需要解决,而不是掩耳盗铃当做问题不存在。

随着项目中代码量的增长,前期遗留的小问题再想修复,成本会越来越高。

Python Geek Tech wechat
欢迎订阅 Python 技术,这里分享关于 Python 的一切。