Python代码规范

一、目标

统一代码风格、命名规范,增强代码可读性和可维护性,供日常开发工作中时参考,以提高团队协作的开发效率。

以下编程规范以Python为标准,其他语言可参考变通

二、编程规约

每一条规范前有【强制】或【推荐】的标签,可以针对性地根据自己需求遵守规范内容

2.1 项目规约

  1. 【强制】文件结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|- main (存放启动项目入口文件)
|- app(存放和运行时前后端、数据库交互的控制器文件)
|- model
|- data (存放数据、数据集处理等文件)
|- model.py (存放模型调用、API接口、本地模型训练等文件)
|- deploy (存放模型部署相关文件)

|- scripts
|- internal (存放内部api处理文件)
- routers.py (路由模块)
- dependencies.py (依赖项模块)
|- external (存放外部处理文件)
- scrapy.py (比如爬虫模块)
- example2.py (比如API依赖项模块)
|- xx.sh (启停或其他脚本)
| - ...(其他分类api
|- config (存放参数配置文件)
|- static (存放静态资源文件)
|- utils (存放三方、工具、常量、异常等公用模块)
|- log (存放临时日志文件)
|- model
|- train

- app.py
- README.md
- requirements.txt
- .env.example(存放API的token、密码等私密KEY)
  1. 【推荐】pylint代码检查

官方网站:https://www.pylint.org/

Pylint 是一个检查违反 PEP 8 规范和常见错误的库。它在一些流行的编辑器和 IDE 中都有集成,也可以单独从命令行运行。 Pylint主要的功能就是用于编码风格的检验,默认情况下 Pylint 会以 PEP-8为标准,如果写的代码不符合 PEP-8编码规范,它就会给你报错。可以通过修改 pylint 的配置文件,修改它检查的方式,从而使它遵守其他的编码规范,比如可以强行让变量名函数名都变成驼峰命名法。

前期项目demo的本地开发时不强制使用PYLINT等工具,但dev版本分支上传前需要检查

使用 Pylint 方便团队形成统一的编码规范。

1
2
3
4
5
6
pip install pylint

是一个Python代码分析工具,它分析 Python 代码中的错误,查找不符合代码风格标准
(Pylint 默认使用的代码风格是 PEP 8

和有潜在问题的代码。比如检查一行代码的长度,变量名是否符合命名标准,一个声明过的接口是否被真正实现等等。

参考:Python代码规范:企业级代码静态扫描-代码规范、逻辑、语法、安全检查,以及代码规范自动编排(1)

  1. 【强制】Github
  • 项目使用Github,前期封闭开发中切记不要将【private】改为【public】
  • 相关代码文件或issue可以在飞书上记录

分支规范

我们制定分支规范,意在实现以下目标:

  1. 减少沟通成本:开发者可以很清晰地知道需要修改的代码位于哪个分支。
  2. 减少 bug 隐患:避免因分支合并导致 bug。
  3. 维护线上稳定:通过一定的流程规范,保证线上代码安全。
  4. 灵活:支持多版本同时开发、同时发布。
  5. 简洁:用最少的分支解决问题,避免反复创建、合并分支,节约操作时间。

主分支: master

主分支(master)用于存放最新的稳定版本。

标签的命名规范为:release-v版本号-日期(如 release-v0.0.1-20231010)

demo开发期 master 一般不动

开发分支: dev

dev 分支用于存放最新代码(可能包含未测试的代码),只有需要正式发布时才会合并到 prod 分支。

命名方式:dev-name,name为每个人的ID

构建分支: test

dev开发完毕后,同步至此分支,用于测试环境下的项目部署和功能调试

此外有些项目需要预编译(压缩、优化)才能发布上线,还需要build分支

生产分支: prod

经过开发与测试环境的检测通过后,合并到prod分支部署上线

  • master分支定期和prod进行同步

  • 所有prod环境下的push和merge都要经过review

流程:

  • 长期存在的就是 master 和 dev 分支
  • 开发前,使用git pull/rebase等命令同步线上分支,避免改动冲突或丢失
  • 根据自己的需要建立对应的分支dev-xx, 开发完成后合并到 test 分支
  • 测试完成后没有问题 则合并到 dev 分支, 临时分支只留作本地使用
  • 新功能发布需要从 dev 分支合并相应功能到 prod分支,生产环境下进行测试
  • 测试结束后,合并进入 master 分支,再进行上线的部署发布

参考:

https://www.cnblogs.com/scajy/p/11514436.html

2.2 常量定义

  • 不要使用中文拼音,尽量不要使用数字
  • 尽量命名语义化
  • 应该避免的名称
  • 单字符名称, 除了计数器和迭代器
  • 包/模块名中的连字符(-)
  • 双下划线开头并结尾的名称(Python保留, 例如__init__)

img

2.3 编码风格

主要参考

行长度
  • 每行不超过 120 个字符
  • 例外:
    • 长的导入模块语句
    • 注释里的URL
  • 不要使用反斜杠连接行
括号

宁缺毋滥的使用括号

除非是用于实现行连接, 否则不要在返回语句或条件语句中使用括号. 不过在元组两边使用括号是可以的.

缩进

用4个空格来缩进代码

绝对不要用tab, 也不要tab和空格混用. 对于行连接的情况, 你应该要么垂直对齐换行的元素(见 行长度 部分的示例), 或者使用4空格的悬挂式缩进(这时第一行不应该有参数):

2.4 模块调用

Python

Python的模块可以简单理解为一个python文件。可以通过 import 语句导入。

一些规范:

  1. 模块名称要短、使用小写,并避免使用特殊符号,比如点(.) 和问号(?)
  2. 不推荐在模块名中使用下划线,而是使用子模块
  3. from modu import * 的代码较难阅读而且依赖独立性不足,通常使用 from modu import fun 或者 import module
1
2
3
4
5
6
7
8
9
#开源LIB
import pandas as pd
import requests
import torch
import uvicorn

#项目文件
from dataset.fetch import FetchDataFromTurecoDB
from model.assistant.Web3NewsAssistant import Web3NewsAssistantBase

2.5 注释

python注释也有自己的规范,可以起到一个备注的作用,团队合作的时候,个人编写的代码经常会被多人调用,为了让别人能更容易理解代码的通途,使用注释是非常有效的。

在说规范之前我们有必要先看以下Python的注释有哪些:

  • 单行注释
  • 多行注释
  • 特殊注释

单行注释

以 # 开头, # 右边的所有东西都被当做说明文字,而不是真正要执行的程序,只起到辅助说明作用

示例代码如下:

1
2
# 这是第一个单行注释
print("he1lo python")

为了保证代码的可读性, # 后面建议先添加一个空格,然后再编写相应的说明文字

多行注释(块注释)

如果注释信息很多,一行无法显示,就可以使用多行注释:要在 Python 程序中使用多行注释,可以用一对连续的 三个 引号(单引号和双引号都可以)

所有类和函数都要有符合规范的注释,来说明其功能与参数

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
"""
这是一个多行注释
在多行注释之间,可以写很多很多的内容
....

"""

print("hello python")

class Web3NewsAssistantModule(Web3NewsAssistantBase):
"""
Creator: rylynn
Date: 2023/9/10
Descr:
生成web3新闻推送模块,基于当前环境的所有数据源
Based on all data source, generate following daily report
"""

def __init__(self, api_key=os.environ["OPENAI_API_KEY"], debug_mode=False):
self.news_num = 10
self._db_conn = FetchDataFromTurecoDB(
tunnel=os.getenv('IS_TUNNEL', True) == "True")

def read_data_from_csv(self, dataset: str) -> bool:
"""
:function: 获取本地CSV格式的加载数据
:parameter: - dataset
local data from other source fetched
:return: - bool
"""
try:
self.ori_dataset = pd.read_csv(dataset, encoding='utf-8', sep=',')
except Exception as e:
logging.warning(e)

return True

特殊注释

  1. 必须是文件的第一行
  2. 必须以#!开头
  3. #!/usr/bin/env python告诉 LINUX/UNIX 去找到 python 的翻译器
1
2
#!/usr/bin/env python
# -*- coding: utf-8 -*-

2.6 函数调用

推荐使用 “with”语句管理文件接口

1
2
3
with open("hello .txt") as hello_file.
for line in hello file:
print line
接口风格规范

2.7 异常处理

在基础模块的编写中,尽量使用try/except机制来捕捉异常,提高debug效率

基础使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import traceback

try:
x = a + 1
print('> 成功结束')
except (NameError, ZeroDivisionError) as err:
print('> 触发异常')
# err 异常对象
print(err)
# 详细异常信息
print(traceback.format_exc())
else:
x += 1
print('> 成功执行 会处罚此操作')
finally:
# 如果 finally 子句中包含一个 return 语句,则返回值将来自 finally 子句的某个 return 语句的返回值,
# 而非来自 try 子句的 return 语句的返回值。
print('> 无论是否触发异常都会执行此句')

注:一般我们会省略掉else和finally,视情况加入

Raise 语句

1
2
3
4
# raise 语句允许强制发生指定的异常
raise NameError('HiThere')
# raise 如果你需要确定是否引发了异常但不打算处理它
raise