|
|
|
@ -0,0 +1,325 @@
|
|
|
|
|
## 2.3. **代码规范学习**
|
|
|
|
|
|
|
|
|
|
### 2.3.1. 项目文件的命名规范
|
|
|
|
|
|
|
|
|
|
(1) 官方命名规范
|
|
|
|
|
|
|
|
|
|
Python官方对于包和模块的命名要求并不多,具体如下:
|
|
|
|
|
|
|
|
|
|
『模块应该有短的、全小写的名称。如果可以提高可读性,可以在模块名称中使用下划线。 Python 包也应该有短的、全小写的名称,尽管不鼓励使用下划线。』
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(2) 项目命名规范
|
|
|
|
|
|
|
|
|
|
本项目中除了__init__.py等有特殊作用的文件外,文件的命名规范大致可以总结为:
|
|
|
|
|
|
|
|
|
|
a. 文件名只包含字母、数字和下划线(_),不包含空格或其他特殊字符;
|
|
|
|
|
|
|
|
|
|
b. 文件名以.py作为文件扩展名结尾,表示这是一个Python代码文件;
|
|
|
|
|
|
|
|
|
|
c. 文件名用小写字母命名,避免使用大写字母,避免在不同操作系统上出现问题。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(3) 在自己代码中实践
|
|
|
|
|
|
|
|
|
|
根据上面总结的文件命名规范,将自己之前项目中的文件按规范进行重新命名,具体如下:
|
|
|
|
|
|
|
|
|
|
![image-20240428213312003](http://fufu-imgsubmit.oss-cn-beijing.aliyuncs.com/img/image-20240428213312003.png)
|
|
|
|
|
|
|
|
|
|
### 2.3.2. 变量的命名规范
|
|
|
|
|
|
|
|
|
|
(1) 官方命名规范
|
|
|
|
|
|
|
|
|
|
Python的官方文档中给出了官方对变量的命名规范,以下是对文档内容的简要介绍:
|
|
|
|
|
|
|
|
|
|
**常见的几种命名风格:**
|
|
|
|
|
|
|
|
|
|
a. 单个小写字母
|
|
|
|
|
|
|
|
|
|
b. 单个大写字母
|
|
|
|
|
|
|
|
|
|
c. lowercase
|
|
|
|
|
|
|
|
|
|
d. lower_case_with_underscores
|
|
|
|
|
|
|
|
|
|
e. UPPERCASE
|
|
|
|
|
|
|
|
|
|
f. UPPER_CASE_WITH_UNDERSCORES
|
|
|
|
|
|
|
|
|
|
g. CapitalizedWords(或者 CapWords,或者 CamelCase——如此命名是因为它的字母看起来凹凸不平)。有时也称为 StudlyCaps。注意:在 CapWords 中使用缩略词时,请将缩略词的所有字母大写。因此 HTTPServerError 比 HttpServerError 更好。
|
|
|
|
|
|
|
|
|
|
h. mixedCase(与 CapitalizedWords 的区别在于首字母小写字符!)
|
|
|
|
|
|
|
|
|
|
i. Capitalized_Words_With_Underscores(丑陋的!)
|
|
|
|
|
|
|
|
|
|
**前导或尾随下划线的特殊形式:**
|
|
|
|
|
|
|
|
|
|
a. _single_leading_underscore:“内部使用”指标较弱。例如,不导入名称以下划线开头的对象。from M import *
|
|
|
|
|
|
|
|
|
|
b. single_trailing_underscore_:按惯例使用以避免与Python关键字冲突,例如:tkinter.Toplevel(master, class_='ClassName')
|
|
|
|
|
|
|
|
|
|
c. __double_leading_underscore:命名类属性时,调用名称修改(在类 FooBar 内,__boo变为 _FooBar__boo;见下文)。
|
|
|
|
|
|
|
|
|
|
d. __double_leading_and_trailing_underscore__:存在于用户控制的命名空间中的“神奇”对象或属性。例如__init__,__import__或__file__。永远不要发明这样的名字;仅按照记录使用它们。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(2) 项目命名规范
|
|
|
|
|
|
|
|
|
|
在本项目代码中,变量的命名格式大致依照了官方文档的规范,同时在上述规范的基础上还做了一些拓展,大致总结为以下几点:
|
|
|
|
|
|
|
|
|
|
a. 变量名尽量小写, 如有多个单词,用下划线隔开;
|
|
|
|
|
|
|
|
|
|
b. 类内部变量命名,用单下划线(_)开头(该变量可被继承访问);
|
|
|
|
|
|
|
|
|
|
c. 类内私有变量命名,用双下划线(__)开头(该变量不可被继承访问)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(3) 在自己代码中实践
|
|
|
|
|
|
|
|
|
|
根据上面总结的变量命名规范,将自己之前代码中的变量按规范进行重新命名,具体如下:
|
|
|
|
|
|
|
|
|
|
![image-20240428213348703](http://fufu-imgsubmit.oss-cn-beijing.aliyuncs.com/img/image-20240428213348703.png)
|
|
|
|
|
|
|
|
|
|
### 2.3.3. 类的编写规范
|
|
|
|
|
|
|
|
|
|
(1) 官方编写规范
|
|
|
|
|
|
|
|
|
|
Python官方没有对类的编写做严格的规范,这里使用谷歌的标准,其中介绍类的编写规范大致如下:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
类应该在其定义下有一个用于描述该类的文档字符串。如果你的类有公共属性(`Attributes`),那么文档中应该有一个属性(`Attributes` )段,并且应该遵守和函数参数相同的格式:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
class SampleClass:
|
|
|
|
|
|
|
|
|
|
"""Summary of class here.
|
|
|
|
|
|
|
|
|
|
Longer class information...
|
|
|
|
|
|
|
|
|
|
Longer class information...
|
|
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
|
|
|
|
|
likes_spam: A boolean indicating if we like SPAM or not.
|
|
|
|
|
|
|
|
|
|
eggs: An integer count of the eggs we have laid.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, likes_spam: bool = False):
|
|
|
|
|
|
|
|
|
|
"""Initializes the instance based on spam preference.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
likes_spam: Defines if instance exhibits this preference.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
self.likes_spam = likes_spam
|
|
|
|
|
self.eggs = 0
|
|
|
|
|
|
|
|
|
|
def public_method(self):
|
|
|
|
|
"""Performs operation blah."""
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
所有类文档字符串都应以一行摘要开头,描述类实例所代表的内容。这意味着 Exception 的子类还应该描述异常代表什么,而不是它可能发生的上下文。 类文档字符串不应重复不必要的信息,例如该类是一个类。
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# 推荐格式:
|
|
|
|
|
class CheeseShopAddress:
|
|
|
|
|
"""The address of a cheese shop.
|
|
|
|
|
...
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class OutOfCheeseError(Exception):
|
|
|
|
|
"""No more cheese is available."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 不推荐的格式:
|
|
|
|
|
class CheeseShopAddress:
|
|
|
|
|
"""Class that describes the address of a cheese shop.
|
|
|
|
|
...
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class OutOfCheeseError(Exception):
|
|
|
|
|
"""Raised when no more cheese is available."""
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(2) 项目编写规范
|
|
|
|
|
|
|
|
|
|
本项目代码中,对类的编写规范大致总结如下:
|
|
|
|
|
|
|
|
|
|
a. 类名使用大写字母开头的驼峰命名法(CamelCase)。
|
|
|
|
|
|
|
|
|
|
b. 方法和属性使用小写字母和下划线的方式(snake_case)。
|
|
|
|
|
|
|
|
|
|
c. __init__ 方法作为构造函数,用于初始化对象的属性。
|
|
|
|
|
|
|
|
|
|
d. 公共成员不以双下划线开头,私有成员以双下划线开头。
|
|
|
|
|
|
|
|
|
|
e. 为类和方法编写文档字符串(docstring)。
|
|
|
|
|
|
|
|
|
|
f. 使用空行划分不同的方法或属性。
|
|
|
|
|
|
|
|
|
|
g. 指定父类时使用继承,例如:class ChildClass(ParentClass)。
|
|
|
|
|
|
|
|
|
|
h. 根据需要使用特殊的魔术方法,例如 __str__、__eq__ 等。
|
|
|
|
|
|
|
|
|
|
i. 在方法中适当处理可能引发的异常。
|
|
|
|
|
|
|
|
|
|
j. 保持一致的缩进、命名风格和代码布局,提高代码可读性。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(3) 在自己代码中实践
|
|
|
|
|
|
|
|
|
|
根据上面总结的文件命名规范,将自己之前项目代码中的类按规范进行重新编写,具体如下:
|
|
|
|
|
|
|
|
|
|
![image-20240428213112217](http://fufu-imgsubmit.oss-cn-beijing.aliyuncs.com/img/image-20240428213112217.png)
|
|
|
|
|
|
|
|
|
|
### 2.3.4. 注释书写规范
|
|
|
|
|
|
|
|
|
|
\1. 官方注释书写规范
|
|
|
|
|
|
|
|
|
|
Python官方对于注释的规范大致如下:
|
|
|
|
|
|
|
|
|
|
需要遵循的原则:
|
|
|
|
|
|
|
|
|
|
a. 与代码相矛盾的注释比没有注释更糟糕。当代码更改时,始终优先考虑使注释保持最新!
|
|
|
|
|
|
|
|
|
|
b. 注释应该是完整的句子。第一个单词应大写,除非它是以小写字母开头的标识符(切勿更改标识符的大小写!)。
|
|
|
|
|
|
|
|
|
|
c. 块注释通常由一个或多个由完整句子组成的段落组成,每个句子以句号结尾。
|
|
|
|
|
|
|
|
|
|
d. 在多句注释中,除了最后一句之外,您应该在句末句号后使用一两个空格。
|
|
|
|
|
|
|
|
|
|
e. 确保您的评论对于使用您所写语言的其他使用者来说清晰且易于理解。
|
|
|
|
|
|
|
|
|
|
f. 来自非英语国家的 Python 程序员:请用英语写下您的注释,除非您 120% 确定该代码永远不会被不会说您的语言的人阅读。
|
|
|
|
|
|
|
|
|
|
不同注释的书写规范:
|
|
|
|
|
|
|
|
|
|
a. 块注释(block comments)
|
|
|
|
|
|
|
|
|
|
块注释通常适用于其后面的部分(或全部)代码,并且缩进到与该代码相同的级别。块注释的每一行都以 和`#`一个空格开头(除非它是注释内的缩进文本)。块注释内的段落由包含单个`#`.
|
|
|
|
|
|
|
|
|
|
b. 内嵌注释(inline comments)
|
|
|
|
|
|
|
|
|
|
谨慎使用内联注释。
|
|
|
|
|
|
|
|
|
|
内联注释是与语句位于同一行的注释。内联注释与语句之间应至少用两个空格分隔。它们应该以 # 和一个空格开头。
|
|
|
|
|
|
|
|
|
|
内联注释是不必要的,而且如果它们陈述了显而易见的内容,实际上会分散注意力。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(1) 项目注释书写规范
|
|
|
|
|
|
|
|
|
|
本项目中合理使用了注释来帮助使用者理解代码,在使用块注释的同时尽量规避了内嵌注释的使用,除了上面提到的之外,还需要注意的有:
|
|
|
|
|
|
|
|
|
|
a. 使用内嵌注释时,应离开代码最少两个空格。
|
|
|
|
|
|
|
|
|
|
b. 不要描述代码,阅读代码的时候,一般不是看不懂,而不是不认识。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(2) 在自己代码中实践
|
|
|
|
|
|
|
|
|
|
根据上面总结注释书写规范,将自己之前项目代码中的注释重新书写,删除了部分无用的内嵌注释,将块注释改成了英文,并规范了部分用语的格式,具体如下:
|
|
|
|
|
|
|
|
|
|
![image-20240428212843374](http://fufu-imgsubmit.oss-cn-beijing.aliyuncs.com/img/image-20240428212843374.png)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 2.3.5. 单元测试的编写
|
|
|
|
|
|
|
|
|
|
(1) 什么是单元测试
|
|
|
|
|
|
|
|
|
|
单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。单元测试是测试代码中最小的功能单元的过程。软件测试有助于确保代码质量,是软件开发过程中不可或缺的一部分。软件开发时,为每个代码单元的单元测试,可以先将单元测试编写为代码。然后,在每次更改软件代码时自动运行该测试代码。这样,如果测试失败,就能快速隔离出存在漏洞或错误的代码区域。单元测试可以强化模块化思维范式,并提高测试的覆盖率和质量。自动单元测试有助于确保开发人员有更多时间专注于编码。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(1) 官方提供的测试方法
|
|
|
|
|
|
|
|
|
|
Python官方提供了一个专门用于单元测试的框架unittest,其中主要的功能大致如下:
|
|
|
|
|
|
|
|
|
|
a. 测试脚手架(test fixture):
|
|
|
|
|
|
|
|
|
|
测试脚手架是为了准备测试所需的工作和相关的清理操作。它包括在每个测试运行之前设置测试环境,以及在测试完成后进行必要的清理。测试脚手架的目的是确保测试在一致的环境中运行,以便获得可重复和可靠的测试结果。在unittest框架中,可以使用setUp()方法在每个测试用例执行前进行脚手架的设置,并使用tearDown()方法进行清理操作。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
b. 测试用例(test case):
|
|
|
|
|
|
|
|
|
|
测试用例是独立的测试单元,用于检查特定数据输入的响应。每个测试用例通常是一个类,继承自unittest.TestCase,并包含一个或多个测试方法。测试方法应以"test_"开头,这样unittest框架才能自动识别并执行这些方法作为测试。测试用例的目的是验证代码在各种情况下的行为是否符合预期。每个测试用例应该关注一个特定的功能或场景,并进行必要的断言来验证结果。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
c. 测试套件(test suite):
|
|
|
|
|
|
|
|
|
|
测试套件是一组测试用例或其他测试套件的集合,用于组织需要一起执行的测试。测试套件可以包含多个测试用例,也可以包含其他测试套件,从而形成层次结构。测试套件的目的是提供一种灵活的方式来组织和管理测试,使测试执行更加高效和可维护。unittest框架提供了多种方法来创建和管理测试套件,例如使用TestLoader类的loadTestsFrom...()方法来加载测试用例或测试套件,以及使用TestSuite类的addTest()和addTests()方法来添加测试用例或测试套件。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
d. 测试运行器(test runner):
|
|
|
|
|
|
|
|
|
|
测试运行器是执行和输出测试结果的组件。它负责执行测试套件中的测试用例,并生成测试报告。测试运行器的作用是自动化地运行测试,并提供有关测试结果的反馈。unittest框架提供了多种测试运行器,可以根据需要选择合适的运行器。例如,TextTestRunner是unittest框架默认的测试运行器,它在命令行输出文本结果;HtmlTestRunner可以生成HTML格式的测试报告。测试运行器可以输出测试的执行状态、通过的测试数量、失败的测试数量等信息,帮助开发人员快速了解测试的结果。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(2) 项目中的单元测试
|
|
|
|
|
|
|
|
|
|
本项目中example文件夹下提供了几个不同功能的工具,并附带有相应的单元测试代码,下面以cipher_test.py文件为例,分析该项目中的单元测试写法,代码内容如下:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
"""Tests for the cipher module."""
|
|
|
|
|
|
|
|
|
|
from fire import testutils
|
|
|
|
|
from examples.cipher import cipher
|
|
|
|
|
|
|
|
|
|
class CipherTest(testutils.BaseTestCase):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def testCipher(self):
|
|
|
|
|
self.assertEqual(cipher.rot13('Hello world!'), 'Uryyb jbeyq!')
|
|
|
|
|
self.assertEqual(cipher.caesar_encode(13, 'Hello world!'), 'Uryyb jbeyq!')
|
|
|
|
|
self.assertEqual(cipher.caesar_decode(13, 'Uryyb jbeyq!'), 'Hello world!')
|
|
|
|
|
self.assertEqual(cipher.caesar_encode(1, 'Hello world!'), 'Ifmmp xpsme!')
|
|
|
|
|
self.assertEqual(cipher.caesar_decode(1, 'Ifmmp xpsme!'), 'Hello world!')
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
|
|
|
|
|
testutils.main()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
首先,该项目使用的测试工具是自带的单元测试工具testutils。该工具是在unittest的基础上,进一步编写的一些函数和类方法;
|
|
|
|
|
|
|
|
|
|
然后将单元测试的对象cipher模块引入,用于之后的测试;
|
|
|
|
|
|
|
|
|
|
接着定义了一个继承自testutils.BaseTestCase的类CipherTest,并为其编写了testCipher方法,用于提供针对密码相关功能的测试用例;
|
|
|
|
|
|
|
|
|
|
最后,调用testutils.main()函数,开始对测试对象的单元测试。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(3) 在自己代码中实践
|
|
|
|
|
|
|
|
|
|
模仿上面项目中的例子,为自己之前项目中的模块也编写相应的单元测试代码,具体如下:
|
|
|
|
|
|
|
|
|
|
![image-20240428212724803](http://fufu-imgsubmit.oss-cn-beijing.aliyuncs.com/img/image-20240428212724803.png)
|