这是我个人开发python代码的一些习惯风格,并不是所谓的“标准”要大家都认可遵循,以及读者有更好的点子,欢迎反馈建议,共同进步。

  1. 代码风格,除了团队开发时极个别需要强制规范的。大部分规则我们往往只能给予建议,而无法给予行动。因为有些规则也谈不上绝对正确,只是说大家风格尽量统一能提高彼此代码可读性,减少一些不必要的麻烦。
  2. 对于后文描述的内容,读者有看不明白的地方可以先跳过
    1. 特别还没有太多开发经验的萌新,不要纠结细节或一口吃个胖子,先消化几条直观易懂的就行
    2. 看不明白代表这个目前还不是你的痛点,或许只有等哪天自己亲自踩过坑了,才愿意从内心真正去接纳改变
  3. 当然,更鼓励虚心学习的读者,对遇到不懂和质疑的,能积极提出来,跟我们、身边的朋友一同探讨。这样的学习者进步是最快的。

TODO 应该有些工具可以暴力修改风格,统一成一套。就”不存在”代码风格太多的问题了~~

1 Python风格

1.1 基础

1.1.1 Ctrl + Alt + L

在PyCharm,可以用Ctrl + Alt + L,对代码格式化。

1.1.2 PEP8

剩下还有下图这种下划线提示的,鼠标移过去,会提示你的代码有什么瑕疵,一般是一些PEP8规范。
扩展资料:code4101的Python代码风格 - 图1

关于PEP8,至少要了解命名规范:
① 变量、函数、模块、包名:lower_with_under。模块、包名尽量不用下划线,除非确实极大提高可读性。
② 类名 CapWords
③ 常量 CAPS_WITH_UNDER

大家平时在opencv-python、pyqt等库见到不遵循这种命名规范,是因为那些库原来是用c++开发的,迁移到python保留了原来c++的名称规范。

注意代码逻辑的拆分,每个函数功能尽量控制在80行内,否则建议拆成多个逻辑的功能组件。

1.1.3 Alt + Enter

很多下划线问题,都可以把鼠标光标停留在上面,按Alt + Enter,不少问题IDE会帮忙自动修正,或者弹出多种参考修正方案的菜单。

1.2 注释

1.2.1 #!/usr/bin/env python3

每个文件开头,统一下述格式的模板,建议使用搜狗输入法等方式来配置宏

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # @Author : 陈坤泽
  4. # @Email : 877362867@qq.com
  5. # @Data : 2020/06/19 15:36

后面这段是个人习惯,不一定要照抄:

  1. from pyxllib.xl import *
  2. if __name__ == '__main__':
  3. with TicToc(__name__):
  4. pass

1.2.2 文档字符串

多勤写文档字符串、文档测试
详见【Python】一、Python基础_代号4101的专栏-CSDN博客 3.3、3.4的内容。

更具体地,建议使用reST格式:
coding style - What is the standard Python docstring format? - Stack Overflow
pycharm中写法:
Specify types with docstrings | PyCharm

1.2.3 代码步骤中标注层次结构

编号格式使用常见的论文章节编号格式,来展现顺序、层级,编号后加一个空格,不要用其他间隔符。
(这个技巧适用于所有编程语言的开发)
image.png

1.3 字符串

勤用f字符串

2 谷歌Python风格指南(中文版)

建议完整阅读一遍中文版后,再看我笔记总结。

中文版:Python语言规范 — Google 开源项目风格指南 (笔者整理时是v2.59版。)
英文原版:styleguide | Style guides for Google-originated open-source projects
TODO 我自己还没读英文原版

2.1 Python语言规范

内容 概述、补充、我的建议 示例代码(TODO 示例不够清晰明了,正反例不足;可以做一套视频详细讲解;格式高亮;根据读者受众不同举特定易理解的业务功能代码;) 备注
1、运行pylint检查代码 PyCharm涵盖pylint的功能,多关注IDE的警告提示,尽可能修复即可。 可以这样注释来抑制告警
# Bad Idea… pylint: disable=redefined-builtin
2、仅对包和模块使用导入 为了代码清晰度是非常有益的,不过目前我的代码根本没遵守,经常把函数直接导进来用了。

from sound.effects.echo import EchoFilter
from sound.effects import echo
3、包:使用模块的全路径名来导入每个模块 个人非常支持,能避免掉很多不必要的坑,写起来并不会多花太多时间。但如果一开始架构不好,重构起来确实比使用相对路径的写法麻烦的多。 import .effects.echo
import sound.effects.echo
可以用一个脚本自动把包加到全局sitepackages索引,简化一定的部署工作量。
4、允许在控制流中使用异常,但必须小心 不要使用”except:”,一定要尽可能指明具体的错误类型。原文档提到的其他条件要都了解遵守。 try:
pass
except:
except ValueError:
pass
5、避免全局变量 如果需要, 全局变量应该仅在模块内部可用, 并通过模块级的公共函数来访问。 目前我自己这点也做的很不好
6、鼓励使用嵌套/本地/内部类或函数 def func():
def foo():

foo()
7、简单情况推荐使用列表推导(List Comprehensions) [x * x for x in range(10)]
8、尽量使用默认迭代器和操作符 for key in adict.keys(): …
for key in adict: …
9、按需使用yield生成器 注意在生成器函数的文档字符串中使用”yields:”而不是”returns:”.
10、lamda函数仅用于单行函数
11、条件表达式仅用于单行函数 x = 1 if cond else 2
12、鼓励使用默认参数值 def foo(a, b=None): 注意默认参数只在模块加载时求值一次. 如果参数是列表或字典之类的可变类型, 这可能会导致问题.
13、在类中可以用属性(properties)设置轻量级的访问、设置 class Square(object):

@property
def perimeter(self):
return self.side * 4
14、尽可能使用隐式False if len(users) == 0:
if not users:
15、不要使用过时的语言特性 words ~~=~~ string.split(foo, ~~’:’~~)
words = foo.split(‘:’)
16、推荐使用词法作用域(Lexical Scoping)简化代码 但要尽量避免写出让人迷惑的代码。我个人一般是结合第6点的嵌套函数使用。 image.png
17、如果好处很显然, 就明智而谨慎的使用装饰器
18、多线程不要依赖内建类型的原子性 优先使用Queue模块的 Queue 数据类型作为线程间的数据通信方式. (唔~~我自己也是偷懒没管这么多,都是用最简洁的代码来实现多线程~)
19、避免使用威力过大的特性 使用这些很”酷”的特性十分诱人, 但不是绝对必要.

2.2 Python风格规范

内容 概述、补充、我的建议 示例代码 备注
1、不要用分号 a=1; b=2;
a, b = 1, 2
2、行长度:每行不超过80个字符;不要使用反斜杠连接行 我自己也有用反斜杠连接行,要改
3、宁缺毋滥的使用括号 return (foo)
4、用4个空格来缩进代码 不要用tab
5、空行:顶级定义之间空两行, 方法定义之间空一行 个人推荐,用pycharm的格式化工具也可以自动完成:ctrl+shfit+alt+L
6、空格:按照标准的排版规范来使用标点两边的空格 用pycharm格式化工具可以保证规范性
7、Shebang:程序的main文件应该以#!/usr/bin/python3开始. 我自己有扩展变型
8、注释:文档字符串,块注释与行注释等 三重双引号;以一个标题行开始
9、类继承要显式使用object 这个为了代码简洁,我个人并没有遵循~
10、字符串:使用列表实现多字符串拼接 ①个人推荐用format、f字符串代替+、%操作
②推荐统一用单引号字符串
image.png
11、使用with等语句显式关闭文件和sockets 对于不支持使用”with”语句的类似文件的对象,使用 contextlib.closing()
12、TODO注释 在不方便识别作者的场景,要养成加上姓名的习惯 # TODO(Zeke) Change this to use relations.
13、每个导入应该独占一行 顺序分组,每个分组内再字典序排序:
①标准库导入
②第三方库导入
③应用程序指定导入
import os, sys
import os
import sys
14、通常每个语句应该独占一行
15、访问控制 对于琐碎又不太重要的访问函数, 你应该直接使用公有变量来取代它们
16、命名 单下划线前缀保护
双下划线前缀私有
17、Main 推荐使用if name == ‘main

2.3 临别赠言

请务必保持代码的一致性

制定风格指南的目的在于让代码有规可循, 这样人们就可以专注于“你在说什么”, 而不是“你在怎么说”.

*3 【个人详细】命名范式

(前缀)分组/动作 + 含义 + (后缀)类型

220221周一16:14版本

  1. 功能分组,树结构形式的功能分组,类似目录结构那样,按层次前缀命名
    1. 示例:metric_det、metric_rec、metric_ocr
    2. 主要用于:函数。全局架构,命名对齐考虑。
  2. 类型放在后缀
    1. 示例1:jpg_file、png_file;jpg_num、png_num;print_mode
    2. 主要用于:变量。局部使用,顺口考虑。
    3. 注意:尽量完整易读,不要用_n表示整数,而是_cnt、_num

高维数组类命名规范

以item条目为例,
一维数组:items
二维数组:itemgroups,itemgroupd2
三维数组:itemgroupss,itemgroupd3
四维数组:itemgroupsss,itemgroupd4

items转成ndarray,ndim=1
itemgroups同理,转出来ndim=2,所以也可以写成后缀d2
以此类推,可以在group后无限累加s,也可以用d表示层数

*4 【个人详细】功能命名

新旧函数更替、多版本函数(后缀_v1、_v2)

类、函数有多个版本的时候,不要简单的加数字123,而是用v1、v2来更清晰的表明版本号
image.png

有可能的话,可以用一个不带v1、v2的作为通用接口名
旧的引使用,如果依赖上下文难修改,可以先改名增加后缀_v1;不带v后缀的直接引用新的v2的实现版本
image.png

测试类分组名(用途前缀)

用特定前缀表明用途的函数、类:
demo,演示功能用法,综合性的使用示例,也可能含有test、perf
debug
,检查功能有误问题,多用于自己代码的问题检查
test,测试运行效果,多用于标准库、三方库的问题检查
perf
,测试性能速度
doc_,文档类函数,一般用于流程介绍,并不适合直接整个函数运行

注意上述几种目的经常是交错复合使用的,一般用其最核心的目的来命名

get/open系列(动作前缀)

内存对象 推荐用途 示例 三方库命名
from_xxx 从什么类型初始化 xlcv.from_buffer
尽量不用前缀,比如read_from_buffer,
但已经写的就算了,不用改。

to_xxx 类型转换 Document.to_fitzdoc df.to_excel
get_xxx 计算值 get_device,get_encoding
gen_xxx 生成数据。
工厂函数工厂类可以写genfunc、gencls。
Labelme.gen_shape
CocoGtDict.gen_images

cvt


build_xxx 生成一个对象;复杂类构建中,一些组件的初始化
文件对象 推荐用途 示例 三方库命名
open 打开文件
Image.open
read 低级的读;
pyxllib统一规范read/write,
注意多文件场合要标记复数

cv2.imread
pd.read_excel
write 低级的写
cv2.imwrite
load 高级的读
(fitz)doc.load_page、json.load
save 高级的写
(PIL)im.save
create_ 生成目录
unpack、dump


函数行为精细控制(print_mode)

prt、verbose -> print_mode?


方便跟其他场景组合使用:print_mode、return_mode、run_mode

print_mode只修改是否输出提示信息,以及复杂度(int数值大小)或者模式(str类型),不涉及函数功能本身
return_mode会修改函数返回值的情况

有时候功能和输出会同步处理,此时可以用一个run_mode统一说明


print_mode有时候也可以传入一个logger来代替存储运行细节

其他

在每个模块,用四个_前缀的变量,来对代码进行分类整理
image.png


list默认变量名li,当然能用具体的业务逻辑规范命名最好:student_list
英语不太好有命名障碍的,推荐工具CODELF


Python中的下划线(译文)_chevalier - SegmentFault 思否
如果就想用标准库的名称命名变量,例如id、type,为了防止冲突,习惯加个后缀,例如id、type_。
前缀_,例如_id、_type。(改成前缀,前缀看着顺眼,后缀总是觉得很奇怪)

最好是具体含义加上后缀:student_id、data_type