对于代码整洁,没有唯一的或者严格的定义,而且可能无法正式地衡量怎样才算代码整洁,因此你不能在代码仓库上运行一个可以告诉你代码是好是坏、可维护性如何的工具。当然,你可以运行检查器、代码校验器、静态分析器等工具。这些工具会给你很大的帮助。它们是必需的,但光有这些还远远不够。代码整洁与否不是机器或脚本能说了算的(到目前为止),而是作为专业人员的我们才能决定的。

为什么保持代码整洁如此重要?_软件开发

几十年来,我们沿用“编程语言”这个术语,并将其视为把我们的想法传达给计算机的语言,可以让计算机运行我们的程序。但是我们错了,这仅仅是部分事实。编程语言背后的“真正语言”是将我们的想法传达给其他开发人员的语言。

这才是代码整洁的真正本质所在。它取决于其他开发人员是否能够读取和维护代码。作为专业人士,我们是唯一能够判断这一点的人。想想看,作为开发人员,我们阅读代码的时间比实际编写代码的时间要多得多。每当我们想要更改或添加新功能,首先必须阅读需要修改或扩展的代码的所有上下文内容。编程语言(Python)就是开发人员实现互相沟通的语言。

为什么保持代码整洁如此重要,原因有很多。大多数原因与可维护性、减少技术债务、有效配合敏捷开发以及管理一个成功的项目的想法有关。

我们想探讨的第一个想法是关于敏捷开发和持续交付的。如果希望项目能够以稳定和可预测的速度不断成功地交付特性,那么必须有一个良好且可维护的代码库。

假设你正驾驶着一辆汽车行驶在去往某个目的地的道路上,而且想要在某个时间点之前到达那里。你必须预估自己到达目的地的时间,这样才能告知正在等你的人。如果汽车不出故障,道路十分平坦,那么你的预估不大可能有太大的偏差;相反,如果道路被破坏,你必须下车把石头移开,或者要避开裂缝,抑或每隔几千米就必须停下来检查一下发动机等,那么你不太可能确定什么时候到达(或者你是否能到达)。这个比喻明确易懂,这里的道路可以理解为代码。如果希望以稳定、恒定和可预测的速度向前推进项目,那么代码应该是可维护和可读的。

技术债务是指因妥协或所做出的不良决策而导致的软件问题。在某种程度上,我们可以从两个方面考虑技术债务问题。一是从过去到现在,如果我们当前面临的问题是由之前编写的错误代码造成的,那会怎样?二是从现在到将来,如果我们决定现在就走捷径,而不是花时间去寻找合适的解决方案,那么未来又会为自己带来什么麻烦?

“债务”这个词用得恰如其分。这是一笔债务,因为在未来代码将比现在更难以修改。产生的成本就是债务的利息。产生的技术债务意味着,明天修改代码比今天更困难,成本更高,而且后天的成本会更昂贵,等等。

一旦团队不能按时交付一些东西,并且不得不停下来去修复和重构代码,代码就要付出技术债务的代价。

技术债务最糟糕的一点是它代表了一个长期和根本的问题。这不是什么值得高度警觉的东西。相反,这是一个悄无声息的问题,这个问题分散在整个项目的各个部分,在某一天,在某一个特定的时间,这个问题会“醒来”,并成为项目推进的阻碍。

1 代码格式化在代码整洁中的作用


代码整洁是指根据一些标准(例如,PEP-8或由项目规范定义的自定义标准)进行的代码格式化和结构化吗?并非如此。

代码整洁远远不止编码标准、格式化、美化工具和其他有关代码布局的检查这些内容。代码整洁是关于实现高质量的软件和建立一个健壮、可维护和避免技术债务的系统的。一段代码或整个软件组件可以百分之百符合PEP-8(或任何其他准则)标准,但仍可能无法满足上述要求。

然而,不注意代码的结构会有一些危险。鉴于此,我们先来分析不良代码结构的问题以及如何解决这些问题,然后介绍如何为Python项目配置和使用工具,以便自动检查和纠正问题。

综上所述,我们可以说代码整洁与PEP-8或编码风格没有任何关系。代码整洁的意义远不止于此,除了可维护性和软件的质量,它还意味着更有意义的东西。不过,正如你将看到的,要实现高效工作,正确地格式化代码非常重要。

2 在项目中遵循编码风格准则

编码准则是项目在质量标准下开发时必须考虑的最低要求。在本节中,我们将探讨其背后的原因。接下来我们开始探讨如何通过工具自动在项目中遵循编码风格准则。

在试图考虑在代码布局中找到某种好的特性时,我们首先想到的就是一致性。我们希望代码能够具有一致的结构,以便更易阅读和理解。如果代码不正确或者结构不一致,并且团队中的每个人都以自己的方式做事,那么最终得到的将是需要额外努力和集中精力才能正确理解的代码。这样的代码很容易引起误解,并且由此引发的漏洞或微小的错误很可能被忽略。

上述情况是我们想要避免的。我们想要的是一眼就能读懂和理解的代码。

如果开发团队的成员都同意采用标准化的方式编写代码,那么所得到的代码看起来会更加熟悉。这样,你就能快速识别模式,并且记住这些模式,进而能更容易地理解内容和检测错误。例如,当某些代码出错时,你可能会在你熟悉的模式中看到一些奇怪的东西——它们会吸引你的注意,再仔细观察,就很可能发现错误!

正如经典著作Code Complete中所述的,在名为Perception in Chess(1973年)的论文中对此进行了有趣的分析,该论文提到了一项实验,以确定不同的人如何理解或记忆不同的棋局。该实验针对不同级别的棋手(新手、中级棋手和象棋高手)以及棋盘上不同位置的棋局来进行统计。他们发现,当棋子的位置是随机的时候,新手能和象棋高手表现得一样好。因为这只是一个记忆练习,任何人都可以发挥出合理的水平。但当棋子的位置遵循一个可能发生在一场真正对弈中的一些逻辑顺序(或者,遵守某种一致性,坚持某种模式时)时,那么象棋高手们的表现比其他人要好得多了。

现在我们想象一下,同样的情况也适用于软件开发。作为Python方面的软件工程师专家,我们就好比上述例子中的象棋高手。如果代码的结构是随机的,没有遵循任何逻辑或者没有遵循任何标准,我们就会像一个新手开发人员一样,很难发现错误;如果我们习惯以结构化的方式阅读代码,并且通过遵循这种模式学会从代码中快速获得想法,就会在项目开发中比其他开发人员更有优势。

就Python而言,你应该遵循的编码风格是PEP-8。你可以对其进行扩展或采用其中的一部分,以适应正在参与的项目的某种特殊性(如行的长度、字符串的注释等)。不过,我们建议,无论你使用最原始版本的PEP-8规范还是对它进行扩展,都应该坚持使用,而不是从头开始尝试另一个不同的标准。

这是因为PEP-8充分考虑了Python语法的许多特殊性(通常不适用于其他语言),并且它是由对Python语法做出贡献的核心Python开发人员创建的。因此,我们认为其他标准其实很难与PEP-8相提并论,更不用说超越它了。

尤其是,在处理代码时,PEP-8还有一些不错的改进特性。

(1)可进行grep。这就是在代码中对内容进行grep的能力,即在某些文件(以及这些文件的某个部分)中搜索所要查找的特定字符串。PEP-8引入的特性之一是区分将值赋值写入变量的方式和传递给函数的关键字参数的方式。

为了更好地理解这一点,我们用一个示例加以阐释。假设我们正在进行调试,需要找到名为location的参数值的传递位置。我们可以运行以下grep命令,获悉要查找内容所在的文件和行号。

$ grep -nr "location=" .
./core.py:13: location=current_location,

现在,我们想知道这个变量在哪里被分配这个值,则可以运行以下命令。

$ grep -nr "location =" .
./core.py:10: current_location = get_location()

PEP-8建立了这样一种约定,即当通过关键字向函数传递参数时,不使用空格,但在分配变量时使用空格。因此,我们可以调整搜索条件(第一次搜索时等号两侧没有空格,第二次搜索时等号两侧都有一个空格),从而提高搜索效率。这是遵守约定的好处之一。

(2)一致性。如果代码看起来有一种统一的格式,阅读起来就会容易得多。这对于新加入项目的人来说尤为重要,如果你希望有新的开发人员加入项目,或者为团队聘用新的(可能经验不足的)程序员,那么他们势必要熟悉代码(甚至可能由多个代码仓库组成)。如果代码格式、文档、命名约定等在所有代码仓库的所有文件中都是相同的,那么他们的工作将变得更加轻松。

(3)代码质量。以结构化的方式查看代码,你一下子就能更熟练地理解它(就像在Perception in Chess中所说的那样),并且更容易发现程序的漏洞和错误。除此之外,检查代码质量的工具也会提示潜在的错误。对代码的静态分析可能有助于降低每行代码的错误率。

为什么保持代码整洁如此重要?_软件开发_02


这是一本关于Python软件工程原理方面的书。

关于软件工程的书有很多,关于Python的可用资源也有很多,但要将这两者结合起来,还有许多工作要做。本书正是尝试在这二者之间架起一座桥梁。

要想在一本书中涵盖关于软件工程的所有主题是不现实的,因为软件工程的领域十分广泛,而且针对某个特定的主题会有专门的图书去介绍。本书重点介绍Python软件工程的主要实践和原则,旨在帮助读者编写更易于维护的代码,同时教读者利用Python的特性来编写代码。

章节提要

第1章 简介、代码格式和工具,介绍搭建Python开发环境所需的主要工具、Python开发人员在开始使用该语言时需要了解的基本知识,以及维护项目中代码可读性的一些指导原则,如用于静态分析、文档、类型检查和代码格式化的工具。

第2章 Python风格代码,介绍Python中的第一个习惯用法——我们在后续章节中将继续使用它。本章还会介绍一些Python的独有特性,以及如何使用它们,并且开始围绕“Python风格代码如何能够让代码质量更高”展开论述。

第3章 好代码的一般特征,回顾软件工程的一般原则,以期帮助读者编写可维护的代码。本章就这个话题展开讨论,并利用Python语言中的工具应用这些原则。

第4章 SOLID原则,介绍面向对象软件设计的SOLID原则。SOLID是软件工程领域的行业术语,即SRP、OCP、LSP、ISP和DIP。本章会展示这5项原则在Python中的应用。可以说,鉴于Python语言的性质,并非所有方法都完全适用。

第5章 用装饰器改进代码,介绍Python的最大特性之一——装饰器。在了解如何创建装饰器(用于函数和类)之后,我们将其用于代码重用、责任分离和创建更细粒度的函数。

第6章 用描述符从对象中获取更多信息,探讨Python中的描述符,它把面向对象设计提升到了一个新的层次。尽管这更多只是一个与框架和工具相关的特性,但我们可以看到如何用描述符提高代码的可读性,以及如何重用代码。

第7章 使用生成器,说明生成器可能是Python的最佳特性。事实上,迭代是Python的核心组件,这让我们认为它引申出了一种新的编程范式。一般来说,通过使用生成器和迭代器,我们可以考虑编写程序的方式。基于从生成器中吸取的知识,我们将进一步了解Python中的协同程序以及异步编程的基本知识。

第8章 单元测试和重构,讨论单元测试在任何所谓“可维护的代码库”中的重要性。本章回顾了单元测试的重要性,并探究了单元测试的主要框架(unittest和pytest)。

第9章 常见的设计模式,回顾如何在Python中实现最常见的设计模式,不是从解决问题的角度,而是通过研究如何利用更好和更易于维护的解决方案来解决问题。本章提到了Python的一些特性,这些特性使得一些设计模式不可见,并采用实用的方法实现了其中的一些设计模式。

第10章 整洁架构,强调代码整洁是实现良好架构的基础。我们在第1章中提到的所有细节,以及在此过程中重温的其他内容,在部署系统时都将在整个设计中发挥关键作用。