Python编程规范+最佳实践
<p><img src="https://img2024.cnblogs.com/blog/1060878/202403/1060878-20240318172023207-1742784129.jpg" alt="image" loading="lazy"></p><h1 id="前言">前言</h1>
<p>Python之禅是影响Python编程语言设计的19条原则,也是Python编码规范的核心理念。</p>
<ul>
<li>优美胜于丑陋(Python 以编写优美的代码为目标)</li>
<li>明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)</li>
<li>简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)</li>
<li>复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)</li>
<li>扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套)</li>
<li>间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)</li>
<li>可读性很重要(优美的代码是可读的)</li>
<li>即便假借特例的实用性之名,也不可违背这些规则(这些规则至高无上)</li>
<li>不要包容所有错误,除非你确定需要这样做(精准地捕获异常,不写 except:pass 风格的代码)</li>
<li>当存在多种可能,不要尝试去猜测</li>
<li>而是尽量找一种,最好是唯一一种明显的解决方案(如果不确定,就用穷举法)</li>
<li>虽然这并不容易,因为你不是 Python 之父(这里的 Dutch 是指 Guido )</li>
<li>做也许好过不做,但不假思索就动手还不如不做(动手之前要细思量)</li>
<li>如果你无法向人描述你的方案,那肯定不是一个好方案;反之亦然(方案测评标准)</li>
<li>命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召)</li>
</ul>
<p>PEP8是Python官方推出的编码规范。本规范以PEP8 编码规范作为出发点编写。<br>
编码规范的第一原则是 <strong>提升代码的可读性</strong>,如果项目是从零开始请遵守该编码规范,如果不是,在项目和该规范出现冲突时,项目自身的规范优先。每一种规范分为强制和推荐。强制是必须遵守的规范,推荐是最佳实践。</p>
<h1 id="代码布局">代码布局</h1>
<p><strong>强制 :</strong></p>
<ol>
<li>统一使用4个空格缩进</li>
<li>单行最大长度为100</li>
<li>行数超过规定,建议用小括号()将多行内容连接起来,而不推荐使用反斜杠\进行连接。</li>
<li>不要在代码末尾加分号,也不要用分号将两条命令写在同一行</li>
<li>空行使用:
<ol>
<li>函数之间用两个空行隔开</li>
<li>类之间用两个空行隔开</li>
<li>类中方法用一个空行隔开</li>
<li>函数中不同逻辑代码块之间可适当插入空行</li>
</ol>
</li>
<li>空格使用:
<ol>
<li>在二元运算符两边都要有空格。二元运算包括:算术(+ - * / ** )、赋值(=,+=,-=)、比较( ==, <, >, !=, in, not in, is, is not)、逻辑运算(and or not)、位运算(& | ! ~ >> <<)</li>
<li>函数关键字参数=两侧不需要空格。例: <code>res = func(name="Tom")</code></li>
<li>逗号后面要加空格,但是如果后面是小括号则不用。例:<code>List=</code></li>
<li>冒号前不加空格,冒号后要加空格。但是切片里前后都不可加空格 。例:<code>Dict = {key: value}</code></li>
<li>不要为对齐赋值语句而使用的额外空格</li>
</ol>
</li>
</ol>
<p><strong>推荐 :</strong></p>
<ol>
<li>import或函数存在续行的情况,遵守挂行缩进对齐</li>
</ol>
<ul>
<li>第一行不应该有参数</li>
<li>使用缩进以区分自己是续行</li>
</ul>
<p>推荐:</p>
<pre><code>def ats_import_dataset(
db: Session = Depends(deps.get_db),
ats_import: schemas.AtsDataSetImport,
current_user: models.User = Depends(deps.get_current_active_user),
controller_client: ControllerClient = Depends(deps.get_controller_client),
background_tasks: BackgroundTasks,
) -> Dict:
</code></pre>
<ol start="2">
<li>在二元运算符之前应该换行,而不是在运算符之后换行</li>
</ol>
<p>推荐:</p>
<pre><code>income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)
</code></pre>
<p>不推荐:</p>
<pre><code>income = (gross_wages +
taxable_interest +
(dividends - qualified_dividends) -
ira_deduction -
student_loan_interest)
</code></pre>
<h1 id="命名">命名</h1>
<p><strong>强制 :</strong></p>
<ol>
<li>普通变量使用下划线分隔命名法,即蛇形命名法。例: <code>max_value</code></li>
<li>变量名和Python关键字冲突,在变量末尾追加下划线。例: <code>type_</code></li>
<li>避免使用双下划线开头并结尾的名称,该命令方式为python保留字。例:<code>__ init__ 用于类初始化</code></li>
<li>常量用全大写字母,用下划线连接。例:<code>MAX_VALUE</code></li>
<li>函数名遵循蛇形命名法。例:<code>def get_user_info</code></li>
<li>类名使用驼峰命名法。例:<code>class ControllerService</code></li>
<li>类中私有属性和方法用单下划线开头。该定义仅为君子约定,实例化可以访问。例:<code>def _previate_fun()</code></li>
<li>类中不希望被继承的变量用双下划线开头。例:<code>__user_info</code></li>
<li>模块应该用简短全小写的名字,如果为了提升可读性,下划线也是可以用的。</li>
<li>包使用简短全小写的名字,但不建议用下划线。</li>
</ol>
<p><strong>推荐 :</strong></p>
<ol>
<li>使用枚举值、常量值替换没有意义的数字</li>
<li>永远不要使用l(小写的L),O(大写的O),I(大写的I)作为单字符变量名,这些字符无法区分</li>
<li>描述性强。在可接受的长度范围内,变量名所指向的内容描述越精确越好。尽量不要用那些过于宽泛的词来作变量名 。推荐:last_login_datetime 不推荐:datetime</li>
<li>尽量短。命名尽量不超过5个单词。变量名要结合代码情景和上下文,可以通过函数名,类名,模块名自解释。</li>
</ol>
<pre><code>def get_expired_vip_users:
pass
# 返回过期vip用户数
# 推荐
users_count = 0
# 不推荐
expired_vip_users_count = 0
</code></pre>
<ol start="5">
<li>配型匹配</li>
</ol>
<ul>
<li>使用is,has, allow等开头来命名表示boolean类型。例:<code>is_vip、has_error、all_empty</code></li>
<li>使用_id结尾,length/count开头结尾的单词标示int类型。例:<code>user_id,host_id,max_length,users_count</code></li>
</ul>
<h1 id="函数">函数</h1>
<p><strong>强制:</strong></p>
<ol>
<li>函数设计要尽量短小,100行是一个参考值</li>
<li>一个函数只做一件事,保证函数语句粒度的一致性</li>
<li>禁止使用可变类型作为函数参数默认值,有惨痛教训 一个 Python Bug 干倒了估值 1.6 亿美元的公司</li>
</ol>
<pre><code># 明令禁止
List = []
def fun(num, arr=List)
pass
</code></pre>
<p><strong>推荐 :</strong></p>
<ol>
<li>要将 self 作为实例方法的的第一个参数。</li>
<li>要将 cls 作为类静态方法的第一个参数。</li>
<li>如果函数的参数名和已有的关键词冲突,参考变量名和关键字重复的处理方法</li>
<li>函数圈复杂度可以衡量函数逻辑,圈复杂度不应该超过10(大概10个if else)</li>
</ol>
<h1 id="模块导入">模块导入</h1>
<p><strong>强制:</strong></p>
<ol>
<li>一个import语句导入一个模块</li>
</ol>
<p>推荐:</p>
<pre><code>import os
import sys
</code></pre>
<p>不推荐:</p>
<pre><code>import sys, os
</code></pre>
<ol start="2">
<li>导入总是位于文件的顶部,在模块注释和文档字符串之后,在模块的全局变量与常量之前</li>
<li>导入应该按照以下顺序分组,每一组导入之间加入空行:</li>
</ol>
<ul>
<li>标准库导入</li>
<li>相关第三方库导入</li>
<li>本地应用/库特定导入</li>
</ul>
<ol start="4">
<li>推荐使用绝对路径导入</li>
<li>禁止使用通配符的导入,污染命名空间。例: <code>from module import *</code></li>
</ol>
<p><strong>推荐 :</strong></p>
<ol>
<li>如果导入的模块名较长,使用 as 给模块重命名。例:<code>from moudle import module_fun as mf</code></li>
</ol>
<h1 id="注释">注释</h1>
<p><strong>强制:</strong></p>
<ol>
<li>代码块注释使用 #, 接口注释使用 """</li>
<li>块注释应该至少离开代码2个空格,#后面跟一个空格</li>
<li>公共函数,重要的函数必须写接口注释</li>
<li>逻辑复杂,难以理解,脚本代码等情况必须要有注释</li>
<li>代码修改要同步更新注释</li>
</ol>
<p><strong>推荐:</strong></p>
<ol>
<li>待实现的功能用 TODO 注释</li>
<li>不要用注释描述代码,而是说清楚功能或逻辑</li>
</ol>
<h1 id="真值判断">真值判断</h1>
<p><strong>推荐:</strong></p>
<ol>
<li>对于容器类型来说,判断空值不要用长度等于0,可以使用空序列布尔值为False这个属性来判断。</li>
</ol>
<p>python 中 0、None、空字符串、空列表、空元组、空字典、空集合、空集合,都可以视为False,if 判断可以视为False。其他情况都为True。<br>
推荐:</p>
<pre><code>user_list = []
if not user_list:
pass
if user_list:
pass
</code></pre>
<p>不推荐:</p>
<pre><code>user_list = []
if len(user_list):
pass
if not len(user_list):
pass
</code></pre>
<ol start="2">
<li>布尔类型的判断,推荐使用 if value</li>
</ol>
<pre><code>greeting = True
推荐: if greeting:
不推荐: if greeting is True:
不推荐: if greeting == True:
</code></pre>
<ol start="3">
<li>对空值None的判断优先使用None为False属性,需要显示判断逻辑更加清晰时可以使用 is 关键字。例:<code>if value is None</code>。</li>
<li>空值判断使用 if value is not None 而不是 if not value is None</li>
</ol>
<h1 id="错误捕获">错误捕获</h1>
<p><strong>强制 :</strong></p>
<ol>
<li>对于无法预知错误类型,使用异常捕获来保证流程正常。如网络请求</li>
<li>异常捕获的颗粒度是行,对大段代码进行 try-catch,这是不负责任的表现</li>
<li>异常捕获时分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。</li>
<li>异常不要用来做流程控制,条件控制</li>
<li>捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之。禁止在捕获错误之后使用pass不处理。如果不想处理它或不能处理,请将该异常抛给它的调用者。</li>
<li>禁止在 finally 块中使用 return,finally 是最后执行的代码,return会覆盖正常流程的return</li>
</ol>
<p><strong>推荐 :</strong></p>
<ol>
<li>存在if else 嵌套复杂的逻辑时,使用try except优化</li>
<li>可以通过预先检查进行规避时,推荐不要异常捕获来处理</li>
<li>错误的返回推荐异常类而不是变量</li>
</ol>
<h1 id="工程结构">工程结构</h1>
<p><strong>推荐 :</strong><br>
Python项目没有其他语言如Java的Maven来构造固定的工程文件目录,结合Python社区推荐的工程目录制定推荐结构如下:</p>
<pre><code>sample_project
├── readme.md
├── docs
│ ├── api.yml
│ └── public_read.md
├── requirements.txt
├── app
│ ├── __init__.py
│ ├── core.py
│ └── helpers.py
├── config
│ ├── mysql.py
├── deploy
│ ├── __init__.py
│ └── run.sh
├── db
│
├── utils
│
└── tests
├── __init__.py
└── test_basic.py
</code></pre>
<p>readme.md: 项目说明文件<br>
docs: 存放项目文档,包括功能详细说明,api docs等<br>
requirements.txt: 存放软件依赖的外部Python包列表<br>
app: 存放项目接口主要代码的目录<br>
config: 存放项目配置文件,如mysql,redis等配置项<br>
deploy:存放部署文件,部署脚本等。如docker-compse.yml文件<br>
db: 存放数据库相关代码<br>
utils: 存放项目工具代码,通常和业务无关<br>
tests: 存放单元测试文件</p><br><br>
来源:https://www.cnblogs.com/goldsunshine/p/18080982
頁:
[1]