如何正确遵守 Python 代码规范
前言
无规矩不成方圆,代码亦是如此,本篇文章将会介绍一些自己做项目时遵守的较为常用的 Python 代码规范。
命名
大小写
模块名写法:
module_name
包名写法:
package_name
类名:
ClassName
方法名:
method_name
异常名:
ExceptionName
函数名:
function_name
全局常量名:
GLOBAL_CONSTANT_NAME
全局变量名:
global_var_name
实例名:
instance_var_name
函数参数名:
function_parameter_name
局部变量名:
local_var_name
命名约定
- 函数名,变量名和文件名应该是描述性的,尽量避免缩写,除了计数器和迭代器、作为
try/except
中异常声明的e
以及作为with
语句中文件句柄的f
. - 用单下划线(_)开头表示变量或函数是 protected 的,不应该被外部访问(除了子类).
注释
函数和方法
一个函数必须要有文档字符串, 除非它满足以下条件:
- 外部不可见
- 非常短小
- 简单明了
文档字符串应该包含函数做什么,以及输入和输出的详细描述。通常,不应该描述“怎么做”,除非是一些复杂的算法。文档字符串应该提供足够的信息,当别人编写代码调用该函数时,他不需要看一行代码,只要看文档字符串就可以了。
文档字符串有多种风格,比如 Google 风格和 Numpy 风格,这里比较推荐 Numpy 风格的文档字符串,基本写法如下述代码所示:
def download_song(song_info: SongInfo, save_dir: str, quality=SongQuality.STANDARD) -> str:
""" download online music to local
Parameters
----------
song_info: SongInfo
song information
save_dir: str
directory to save the downloaded audio file
quality: SongQuality
song sound quality
Returns
-------
song_path: str
save path of audio file, empty string when the download fails
Raises
------
AudioQualityError:
thrown when the sound quality is illegal
"""
pass
块注释和行注释
最需要写注释的是代码中那些技巧性的部分,对于复杂的操作,应该在其操作开始前写上若干行注释。对于不是一目了然的代码, 应在其行尾添加注释。为了提高可读性, 注释应该至少离开代码2个空格。
# We use a weighted dictionary search to find out where i is in
# the array. We extrapolate position based on the largest num
# in the array and the array size and then do binary search to
# get the exact number.
if i & (i-1) == 0: # True if i is 0 or a power of 2.
另一方面,绝不要描述代码。假设阅读代码的人比你更懂 Python,他只是不知道你的代码要做什么.
# BAD COMMENT: increase i
i += 1
缩进
用 4 个空格来缩进代码,绝对不要用 tab, 也不要 tab 和空格混用。对于行连接的情况,你应该垂直对齐换行的元素, 或者使用 4 空格的悬挂式缩进(这时第一行不应该有参数)。
哎呦不错哦:
# 垂直对齐参数
foo = long_function_name(var_one, var_two,
var_three, var_four)
# 字典内垂直对齐
foo = {
"long_dictionary_key": value1 +
value2,
...
}
# 4 个空格的悬挂缩进,左括号后面没有参数
foo = long_function_name(
var_one,
var_two,
var_three,
var_four
)
# 字典内 4 个空格的悬挂缩进
foo = {
"long_dictionary_key":
long_dictionary_value,
...
}
那种事情不要啊:
# 左括号后不能携带参数
foo = long_function_name(var_one, var_two,
var_three, var_four
)
# 禁止 2 个空格的悬挂式缩进
foo = long_function_name(
var_one,
var_two,
var_three,
var_four
)
# 字典内需要使用悬挂缩进
foo = {
"long_dictionary_key":
long_dictionary_value,
...
}
编写代码时不能缩进过多的层级,一般不要超过 4 层,不然会造成阅读困难。Python 中的缩进一般来自于 if-else
、for
和 while
等语句块,一种减少缩进的方法是写 if
就不写 else
,比如:
def update_by_ids(self, entities: List[Entity]) -> bool:
""" update multi records
Parameters
----------
entities: List[Entity]
entity instances
Returns
-------
success: bool
whether the update is successful
"""
# 不满足条件直接返回默认值
if not entities:
return True
# 假设这是一堆很复杂的业务代码
db = self.get_database()
db.transaction()
# 创建 sql 语句
id_ = self.fields[0]
values = ','.join([f'{i} = :{i}' for i in self.fields[1:]])
sql = f"UPDATE {self.table} SET {values} WHERE {id_} = :{id_}"
self.query.prepare(sql)
return db.commit()
还可以使用 continue
来减少 for
和 while
中不必要的缩进:
bboxes = []
for contour in contours:
if cv.contourArea(contour) < 500:
continue
x, y, bw, bh = cv.boundingRect(contour)
bboxes.append([x, y, bw, bh])
换行
顶级定义之间空两行,比如函数或者类定义。 方法定义,类定义与第一个方法之间,都应该空一行。函数或方法中,某些地方要是你觉得合适,就空一行,比如在 if-else
、for
和 while
的后面空一行,以及在不同的逻辑之间加空行:
def download_cover(url: str, singer: str, album: str) -> str:
""" download online album cover
Parameters
----------
url: str
the url of online album cover
singer: str
singer name
album: str
album name
Returns
-------
save_path: str
save path of album cover, empty string when the download fails
"""
if not url.startswith('http'):
return ''
# request data
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
}
response = requests.get(url, headers=headers)
response.raise_for_status()
pic_data = response.content
# save album cover
return Cover(singer, album).save(pic_data)
导包
导入总应该放在文件顶部, 位于模块注释和文档字符串之后, 模块全局变量和常量之前。导入应该按照 标准库 —> 第三方包 —> 项目代码 的顺序导入:
# coding:utf-8
import base64
import json
from pathlib import Path
from typing import Union
import requests
from common.database.entity import SongInfo
from common.url import FakeUrl
from .crawler_base import CrawlerBase, MvQuality
函数长度
不对函数长度做硬性限制。但是若一个函数超过来40行,推荐考虑一下是否可以在不损害程序结构的情况下对其进行分解。因为即使现在长函数运行良好,几个月后可能会有人修改它并添加一些新的行为,这容易产生难以发现的 bug。保持函数的简练,使其更加容易阅读和修改。当遇到一些很长的函数时,若发现调试比较困难或是想在其他地方使用函数的一部分功能,不妨考虑将这个场函数进行拆分。
类型注释
公共的 API 需要注释
对于容易出现类型相关的错误的代码进行注释,比如下述代码的
image
可能为numpy
数组,也可能是Image
类型,究竟是哪种类型需要指明:def draw_bboxes(image: np.ndarray, bboxes: Union[np.ndarray, list], labels: List[str]) -> Image:
pass
可以使用行尾注释
# type:
:persons = [] # type: List[Person]
参数和返回值类型
当函数需要传入或者返回多个值,可以考虑将这些相关的值作为数据成员封装到一个类中。如下述代码所示,使用 python3.7 提供的 dataclass
可以十分方便地创建一个实体类,接着可以传入传出实体类的实例:
from dataclasses import dataclass
@dataclass
class SongInfo:
""" Song information """
file: str = None
title: str = None
singer: str = None
album: str = None
year: int = None
genre: str = None
duration: int = None
track: int = None
track_total: int = None
disc: int = None
disc_total: int = None
create_time: int = None
modified_time: int = None
class SongInfoReader(SongInfoReaderBase):
""" Song information reader """
def read(self, file: Union[str, Path]):
if not isinstance(file, Path):
file = Path(file)
tag = TinyTag.get(file)
file_ = str(file).replace('\\', '/')
title = tag.title or file.stem
singer = tag.artist or self.singer
album = tag.album or self.album
year = self.__get_year(tag, file)
genre = tag.genre or self.genre
duration = int(tag.duration)
track = self._parseTrack(tag.track or self.track)
track_total = int(tag.track_total or self.track_total)
disc = int(tag.disc or self.disc)
disc_total = int(tag.disc_total or self.disc_total)
create_time = int(file.stat().st_ctime)
modified_time = int(file.stat().st_mtime)
return SongInfo(
file=file_,
title=title,
singer=singer,
album=album,
year=year,
genre=genre,
duration=duration,
track=track,
track_total=track_total,
disc=disc,
disc_total=disc_total,
createTime=create_time,
modifiedTime=modified_time
)
class KuWoMusicCrawler:
""" Kuwo music crawler """
def get_song_url(self, song_info: SongInfo) -> str:
if not FakeUrl.isFake(song_info.file):
return song_info.file
# send request for play url
rid = KuWoFakeSongUrl.getId(song_info.file)
url = f'http://www.kuwo.cn/api/v1/www/music/playUrl?mid={rid}&type=convert_url3&br=128kmp3'
response = requests.get(url, headers=self.headers)
response.raise_for_status()
play_url = json.loads(response.text)['data']['url']
return play_url
千万不要图一时方便而把这些字段放在字典或者列表之中,然后作为函数的参数或者返回值。写时一时爽,重构火葬场。
写在最后
如果能遵守上述规范,相信代码不会那么容易散发出难闻的气味。关于更多代码规范,可以参见 Google 开源项目风格指南,本文也参考了指南的部分内容。如果想写出更加优雅的代码,可以阅读设计模式相关的书籍和博客,这里推荐《精通Python设计模式》和教程 Refactoring GURU,以上~~
如何正确遵守 Python 代码规范的更多相关文章
- 老李分享:pep8 python代码规范
老李分享:pep8 python代码规范 什么是PEPPEP是 Python Enhancement Proposal 的缩写,翻译过来就是 Python增强建议书 . PEP8 译者:本文基于 20 ...
- python代码规范与标准库参考
python代码规范与标准库参考 python代码规范参考文献: http://www.runoob.com/w3cnote/google-python-styleguide.html https:/ ...
- Python代码规范问题及解决
Python代码规范问题及解决 为了养成使用Python编程好习惯,尽量保证自己写的代码符合PEP8代码规范,下面是过程中报出的警告及解决方法,英文有些翻译不太准确见谅,会不断更新: PEP 8 只是 ...
- python代码规范以及函数注释规范
摘要 本文给出主Python版本标准库的编码约定.CPython的C代码风格参见PEP7.本文和PEP 257 文档字符串标准改编自Guido最初的<Python Style Guide&g ...
- Python代码规范之---代码不规范,亲人两行泪
任何语言的程序员,编写出符合规范的代码,是开始程序生涯的第一步! 关于代码规范 Python 官方提供有一系列 PEP(Python Enhancement Proposals) 文档 其中第 8 篇 ...
- Python代码规范(PEP8)问题及解决
转载:https://blog.csdn.net/Jason_Lewis/article/details/75386598 最近刚刚接触Python,为了养成好习惯,尽量保证自己写的代码符合PEP8代 ...
- Python代码规范
一:背景 用于规范化ocp python开发,对于使用python开发的程序使用统一的风格,便于代码的维护 二:python风格规范 分号:不要在行尾加分号,也不要用分号将两条命令放在同一行 括号:宁 ...
- python代码规范整理
规范参考源: 1.pep8(python代码样式规范):中文文档 https://blog.csdn.net/ratsniper/article/details/78954852 2.pep ...
- python代码规范 自动优化工具Black
自动优化工具Black 在众多代码格式化工具中,Black算是比较新的一个,它***的特点是可配置项比较少,个人认为这对于新手来说是件好事,因为我们不必过多考虑如何设置Black,让 Black 自己 ...
- 命令行下设置 PYTHONPATH 来正确运行Python代码
写Python程序,总要使用一些自己使用的库:在运行此类程序的时候,就需要先配置好 PYTHONPATH 环境变量:否则会导致找不到库错误. Windows下,可以写一个bat来简化配置: @ECHO ...
随机推荐
- 使用Portainer管理其他主机的docker应用有两种方式
官方文档地址:https://docs.portainer.io/v/ce-2.9/admin/environments/add/docker 第一种方式 是在其他主机的docker 启动中放开237 ...
- MySQL集群搭建(1)-主备搭建
数据库在任何业务中都是最重要的环节之一,这就对数据库架构提出的较高的要求.单点数据库永远不应该出现在生产环境,我们已经目睹过太多由于单点.备份缺失造成的损失,所以,搭建高可用 MySQL 集群是非常有 ...
- 迁移一个仓库到新的Gitlab
一般这种迁移,要注意旧仓库的提交历史等信息也要同步到新的仓库. 先使用如下命令克隆老的: git clone --bare git@gitlab.test1.com:f2e/test.git 新仓库创 ...
- Jenkins 中使用 Git Parameter 插件动态获取 Git 的分支
- C语言指针笔记01
int num = 90; 定义一个整型变量num int* ptr = # 定义一个整型指针变量ptr,指针变量ptr的类型取决于他所需要指向的变量,如这里,ptr要指向int类型变 ...
- 市面上erp软件那么多,为什么很多卖家选择定制erp?
为什么选择定制ERP?适合自己的才是最好的啊!就连头部ERP企业提供给用户的ERP系统,应该也没有不进行个性化定制的吧,匹配很重要!规模不同.行业不同.发展阶段不同.生产模式不同.管理理念不同,适用的 ...
- 如何通过执行SQL为低代码项目提速?
见多了SQL为代码开发提速,那么当低代码遇到SQL会擦出怎样的火花呢?本文将低代码和SQL结合进行介绍,让大家了解如何通过执行SQL为低代码项目提速. 背景 自从计算机诞生的一刻起,如何让计算机能够按 ...
- POJ2282 The Counting Problem(数位DP)
用dp[pos][val][cnt]表示状态,pos是数位,val是当前统计的数字,cnt是目前统计的目标数字的出现次数 注意状态的转移过程,统计数字0时前导0的影响. 1 #include<c ...
- JAVA员工名字 年龄 工资 工种
如题: 下面是我个人的写法 输出部分使用了 格式化输出 有兴趣的朋友可以了解一下: 解决的思路大致为: 创建一个对象数组--> 数组下标为0的数组中张三这个变量对应 String name; 2 ...
- IOC常用的创建对象方式
通过无参构造方法来创建 1.User.java public class User { private String name; public User() { System.out.println( ...