• Python 编程规范和软件开发目录规范的重要性


    1 编程规范的重要性

    1.1 注释

    每条注释以井号(#)开始,一直到该行末尾结束,一直到该行末尾结束。我们可以在注释中放任何东西,因为Python 会完全无视他们的存在。这里给出以后几条规则:
    (1) 假设读者的Python 水平和你一样(比如说,不要去解释 “ 什么是字符串 ” , 也不要去解释 “ 什么是赋值语句 ”)
    (2) 不要去注释毫无意义的事情。
    (3) 很多程序员会在代码上写上一些以 **“ TODO ” 或者 “ FIXME ”**开始的注释,目的就是为了提醒他们回来编写或清理一些未完成的一些问题。
    (4) 如果你在编写某段程序的时候需要使劲思考的话,应该编写注释,以后别人不会在这个地方绞尽脑汁。尤其要注意的是,如果你在开发程序的时候或者函数编写的时候使用要点来描述,尽量写的细致一点,在开发工作完成之后,还应该将原来的要点全部保留下来直接做解释。
    (5) 同样,如果某个bug很难查明,或者其修改方案比较复杂,那么你就应该编写一条注释对其进行解释。如果不这么做,那么今后其他复杂该部分的代码的程序员就会可能认定他们没有必要这么复杂并将其改为原来的样子,从而将你的心血付诸东流。
    (6) 如果需要大量的注释才能解释清楚某段代码的作用,那么就应该对这些代码进行整理,比如,如果需要分别对一个函数的15个列表进行解释,那么就应该将该函数拆分成更小的代码块,每隔分别只处理较小的几个列表。
    (7) 过时的注释还不如没有注释,因此修改某段代码后,一定要检查相关注释,并对其做出适当的修改以保证其仍然能够准确描述代码的功能。
    (8) 注释不是越多越好,弄得整篇代码都是注释,却很少看到代码,这样就曲解了注释的意思了。

    Python 中的特殊注释

    比如:Python有一行声明python编码格式的单行注释。这行注释指定文件为 utf-8 编码。这行特殊注释只能放在文件的第一行或者开头.

    #_*_coding:utf-8_*_
    
    • 1

    还有一种是说明脚本语言是python的,是要用/usr/bin下面的程序(工具)python,这个解释器,来解释python脚本,来运行python脚本的。

    #!/usr/bin/python
    
    • 1

    1.2 规范命名变量:

    1.2.1 变量定义规则:

    (1) 变量名只能是字母,数字或者下划线的任意组合
    (2) 变量名的第一个字符不能是数字
    (3) 关键字不能生成变量名

    1.2.2 变量规范命名注意事项:

    (1) 变量名不能过长
    (2) 变量名词不达意思
    (3) 变量名为中文,拼音

    1.2.3 总体命名规则:

    (1) 尽量单独使用小写字母‘l’,大写字母‘O’等容易混淆的字母。
    (2) 模块命名尽量短小,使用全部小写的方式,可以使用下划线。
    (3) 包命名尽量短小,使用全部小写的方式,不可以使用下划线。
    (4) 类的命名使用CapWords的方式,模块内部使用的类采用_CapWords的方式。
    (5) 异常命名使用CapWords+Error后缀的方式。
    (6) 全局变量尽量只在模块内有效,类似C语言中的static。实现方法有两种,一是__all__机制;二是前缀一个下划线。
    (7) 函数命名使用全部小写的方式,可以使用下划线。
    (8) 常量命名使用全部大写的方式,可以使用下划线。
    (9) 类的属性(方法和变量)命名使用全部小写的方式,可以使用下划线。
    (10) 类的属性有3种作用域public、non-public和subclass API,可以理解成C++中的public、private、protected,non-public属性前,前缀一条下划线。
    (11) 类的属性若与关键字名字冲突,后缀一下划线,尽量不要使用缩略等其他方式。
    (12) 为避免与子类属性命名冲突,在类的一些属性前,前缀两条下划线。比如:类Foo中声明__a,访问时,只能通过Foo._Foo__a,避免歧义。如果子类也叫Foo,那就无能为力了。
    (13) 类的方法第一个参数必须是self,而静态方法第一个参数必须是cls。

    1.3 排版问题

    1.3.1 代码排版:

    (1) 缩进。4个空格的缩进(编辑器都可以完成此功能),不使用Tap,更不能混合使用Tap和空格。
    (2) 每行最大长度79,换行可以使用反斜杠,最好使用圆括号。换行点要在操作符的后边敲回车。
    (3) 类和top-level函数定义之间空两行;类中的方法定义之间空一行;函数内逻辑无关段落之间空一行;其他地方尽量不要再空行。

    1.3.2 文档排版:

    (1) 模块内容的顺序:模块说明和docstring—import—globals&constants—其他定义。其中import部分,又按标准、三方和自己编写顺序依次排放,之间空一行。
    (2) 不要在一句import中多个库,比如import os, sys不推荐。
    (3) 如果采用from XX import XX引用库,可以省略‘module.’,都是可能出现命名冲突,这时就要采用import XX。

    1.3.3 空格的使用:

    总体原则,避免不必要的空格
    (1) 各种右括号前不要加空格。
    (2) 逗号、冒号、分号前不要加空格。
    (3)函数的左括号前不要加空格。如Func(1)。
    (4) 序列的左括号前不要加空格。如list[2]。
    (5) 操作符左右各加一个空格,不要为了对齐增加空格。
    (6) 函数默认参数使用的赋值符左右省略空格。
    (7) 不要将多句语句写在同一行,尽管使用‘;’允许。
    (8) if/for/while语句中,即使执行语句只有一句,也必须另起一行。

    1.4 其他

    (1) 编码中考虑到其他python实现的效率等问题,比如运算符‘+’在CPython(Python)中效率很高,都是Jython中却非常低,所以应该采用.join()的方式。
    (2) 尽可能使用‘is’‘is not’取代‘==’,比如if x is not None 要优于if x。
    (3) 使用基于类的异常,每个模块或包都有自己的异常类,此异常类继承自Exception。
    (4) 异常中不要使用裸露的except,except后跟具体的exceptions。
    (5) 异常中try的代码尽可能少。比如:

    try:
    value = collection[key]
    except KeyError:
    return key_not_found(key)
    else:
    return handle_value(value)
    ***优于***
    try:
    # Too broad!
    return handle_value(collection[key])
    except KeyError:
    # Will also catch KeyError raised by handle_value()
    return key_not_found(key)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (6) 使用startswith() and endswith()代替切片进行序列前缀或后缀的检查。比如:

    if foo.startswith('bar'):
    ***优于***
    if foo[:3] == 'bar':
    
    • 1
    • 2
    • 3

    (7) 使用isinstance()比较对象的类型。比如

    if isinstance(obj, int):
    ***优于***
    if type(obj) is type(1):
    
    • 1
    • 2
    • 3

    (8) 判断序列空或不空,有如下规则

    if not seq:
    if seq:
    ***优于***
    if len(seq)
    if not len(seq)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (9) 字符串不要以空格收尾。
    (10) 二进制数据判断使用 if boolvalue的方式

    2. 软件开发目录规范的重要性

    2.1 为什么要设计好目录结构?

    我们设计一个层次清晰的目录结构,要达到以下两点:
    ① 可读性高: 不熟悉这个项目的代码的人,一眼就能看懂目录结构,知道程序启动脚本是哪个,测试目录在哪儿,配置文件在哪儿等等。从而非常快速的了解这个项目。
    ② 可维护性高: 定义好组织规则后,维护者就能很明确地知道,新增的哪个文件和代码应该放在什么目录之下。这个好处是,随着时间的推移,代码/配置的规模增加,项目结构不会混乱,仍然能够组织良好。

    2.2 目录组织方式

    假设你的项目名为foo, 我比较建议的最方便快捷目录结构这样就足够了:

    Foo/
    |-- bin/
    | |-- foo
    |
    |-- foo/
    | |-- tests/
    | | |-- init.py
    | | |-- test_main.py
    | |
    | |-- init.py
    | |-- main.py
    |
    |-- docs/
    | |-- conf.py
    | |-- abc.rst
    |
    |-- setup.py
    |-- requirements.txt
    |-- README

    其中:
    bin/: 存放项目的一些可执行文件,当然你可以起名script/之类的也行。
    foo/: 存放项目的所有源代码。①源代码中的所有模块、包都应该放在此目录。不要置于顶层目录。② 其子目录tests/存放单元测试代码; ③ 程序的入口最好命名为main.py。
    docs/: 存放一些文档。
    setup.py: 安装、部署、打包的脚本。
    requirements.txt: 存放软件依赖的外部Python包列表。
    README: 项目说明文件。
    除此之外,有一些方案给出了更加多的内容。比如LICENSE.txt,ChangeLog.txt文件等,我没有列在这里,因为这些东西主要是项目开源的时候需要用到。

    2.3 README项目说明文件

    2.3.1 为什么要写README文件?

    这个文件的目的是能简要描述该项目的信息,让读者快速了解这个项目。

    2.3.2 完整的README包含的内容

    (1) 它需要说明以下几个事项:

    软件定位,软件的基本功能。
    运行代码的方法: 安装环境、启动命令等。
    简要的使用说明。
    代码目录结构说明,更详细点可以说明软件的基本原理。
    常见问题说明。

    二,它包括了一下内容:

    项目和所有子模块和库的名称(对于新用户,有时不同命名会导致混乱)
    对所有项目,和所有子模块和库的描述
    如何使用 5-line code(如果是一个库)
    版权和许可信息(或阅读许可证)
    抓取文档指令
    安装、配置和运行程序的指导
    抓取最新代码和构建它们的说明(或快速概述和「阅读 Install」)
    作者列表或「Read AUTHORS」
    提交bug,功能要求,提交补丁,加入邮件列表,得到通知,或加入用户或开发开发区群的介绍
    其他联系信息(电子邮件地址,网站,公司名称,地址等)
    一个简短的历史记录(更改,替换或者其他)
    法律声明

    2.3.3 一个简单的范本

    当然,我们前期写的话,不必要那么麻烦,就写几个简单的必要的东西,比如法律声明啊,联系记录啊等等,就不必要写。

    ###########环境依赖 node v0.10.28+ redIs ~

    ###########部署步骤

    1. 添加系统环境变量
      export $PORTAL_VERSION=“production” // production, test, dev

    2. npm install //安装node运行环境

    3. gulp build //前端编译

    4. 启动两个配置(已forever为例)
      eg: forever start app-service.js
      forever start logger-service.js

    ###########目录结构描述 ├── Readme.md // help ├── app // 应用 ├── config // 配置 │ ├── default.json │
    ├── dev.json // 开发环境 │ ├── experiment.json //
    实验 │ ├── index.js // 配置控制 │ ├── local.json
    // 本地 │ ├── production.json // 生产环境 │ └── test.json
    // 测试环境 ├── data ├── doc // 文档 ├── environment
    ├── gulpfile.js ├── locales ├── logger-service.js // 启动日志配置
    ├── node_modules ├── package.json ├── app-service.js //
    启动应用配置 ├── static // web静态资源加载 │ └── initjson │
    └── config.js // 提供给前端的配置 ├── test ├── test-service.js └──
    tools

    ###########V1.0.0 版本内容更新

    1. 新功能 aaaaaaaaa
    2. 新功能 bbbbbbbbb
    3. 新功能 ccccccccc
    4. 新功能 ddddddddd

    2.3.4 规范的README文件怎么写?

    对于常用windows的同学们,怎么写README呢?
    需要使用MarkDown

    这个网站可以直接编写MarkDown语言
    Markdown官方教程

    2.4 关于requirements.txt和setup.py

    2.4.1 setup.py

    一般来说,用setup.py来管理代码的打包、安装、部署问题。业界标准的写法是用Python流行的打包工具setuptools来管理这些事情。这种方式普遍应用于开源项目中。不过这里的核心思想不是用标准化的工具来解决这些问题,而是说,一个项目一定要有一个安装部署工具,能快速便捷的在一台新机器上将环境装好、代码部署好和将程序运行起来。

    安装环境、部署代码、运行程序这个过程全是手动完成,遇到的问题:
    ①安装环境时经常忘了最近又添加了一个新的Python包,结果一到线上运行,程序就出错了。
    ②Python包的版本依赖问题,有时候我们程序中使用的是一个版本的Python包,但是官方的已经是最新的包了,通过手动安装就可能装错了。
    ③如果依赖的包很多的话,一个一个安装这些依赖是很费时的事情。
    ④新同学开始写项目的时候,将程序跑起来非常麻烦,因为可能经常忘了要怎么安装各种依赖。
    setup.py可以将这些事情自动化起来,提高效率、减少出错的概率。"复杂的东西自动化,能自动化的东西一定要自动化。"是一个非常好的习惯。

    一个例子:

    from setuptools import setup
    # Metadata goes in setup.cfg. These are here for GitHub's dependency graph.
    setup(
        name="Flask",
        install_requires=[
            "Werkzeug >= 2.0",
            "Jinja2 >= 3.0",
            "itsdangerous >= 2.0",
            "click >= 8.0",
            "importlib-metadata >= 3.6.0; python_version < '3.10'",
        ],
        extras_require={
            "async": ["asgiref >= 3.2"],
            "dotenv": ["python-dotenv"],
        },
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    当然,简单点自己写个安装脚本(deploy.sh)替代setup.py也未尝不可。

    2.4.2 requirements.txt

    这个文件存在的目的是:
    ①方便开发者维护软件的包依赖。将开发过程中新增的包添加进这个列表中,避免在 setup.py 安装依赖时漏掉软件包。
    ②方便读者明确项目使用了哪些Python包。

    这个文件的格式是每一行包含一个包依赖的说明,通常是flask>=0.10这种格式,要求是这个格式能被pip识别,这样就可以简单的通过 pip install -r requirements.txt来把所有Python包依赖都装好了。

    2.4.2.1 生成方式

    (1) pip freeze > requirements.txt
      这种方式是会在当前路径下生成一个requirements.txt文件,该文件中则会记录当前python环境下所以拥有的所有包,以及包的版本。可以看作把pip list这个命令展现的所有东西记录下啦。这种方式速度很快,但是requirements.txt文件包含的包是当前环境所有的包,如果你当前项目没有用到的包也会被包括下来。
    (2) pipreqs 目录路径
    这种方式需要先通过pip install 安装pipreqs模块。
    pip install pipreqs
    定位到你所在项目的根目录下,执行
    pipreqs ./ --encoding=utf8
    这里加了–encoding=utf8是为了避免乱码。这样也会在当前目录下生成requirements.txt文件,不过这个文件中包含的包版本就是当前项目中所用到的所有包的版本信息了。

    2.4.2.2 载入方式

    我们从github上克隆了某个项目,我们可以根据它的requirements.txt配置环境,推荐采用conda新建一个环境之后再使用以下命令。
    pip install -r requirements.txt

    2.5 关于配置文件的使用方法

    注意,在上面的目录结构中,没有将conf.py放在源码目录下,而是放在docs/目录下
    很多项目对配置文件的使用做法是:
    配置文件写在一个或多个python文件中,比如此处的conf.py。
    项目中哪个模块用到这个配置文件就直接通过import conf这种形式来在代码中使用配置。
    这种做法我不太赞同:
    这让单元测试变得困难(因为模块内部依赖了外部配置)
    另一方面配置文件作为用户控制程序的接口,应当可以由用户自由指定该文件的路径。
    程序组件可复用性太差,因为这种贯穿所有模块的代码硬编码方式,使得大部分模块都依赖conf.py这个文件。
    所以,我认为配置的使用,更好的方式是,
    模块的配置都是可以灵活配置的,不受外部配置文件的影响。
    程序的配置也是可以灵活控制的。
    能够佐证这个思想的是,用过nginx和mysql的同学都知道,nginx、mysql这些程序都可以自由的指定用户配置。
    所以,不应当在代码中直接import conf来使用配置文件。上面目录结构中的conf.py,是给出的一个配置样例,不是在写死在程序中直接引用的配置文件。可以通过给main.py启动参数指定配置路径的方式来让程序读取配置内容。当然,这里的conf.py你可以换个类似的名字,比如settings.py。或者你也可以使用其他格式的内容来编写配置文件,比如settings.yaml之类的。

  • 相关阅读:
    Completed 404 NOT_FOUND,Whitelabel Error Page
    【升级版学生信息管理系统&员工工资信息管理系统】+文件操作+更多细节
    168-203-javajvm-垃圾收集器
    Django(三)接口自动化平台HttpRunnerManager本地部署
    基于FPGA的智能小车系统
    软件设计不是CRUD(7):低耦合模块设计实战——组织机构模块(中)
    IC front-end design engineer
    比较两个DataFrame的Python神器DataComPy
    基于cortex-M3,M4下硬件层、驱动层的解耦软件框架设计
    掌动智能浅析Web自动化测试的重要性
  • 原文地址:https://blog.csdn.net/weixin_45928096/article/details/125420870