最近给apache 下的libcloud 项目提交了几个bug和patch,然后看到了一遍不错的指引,详细包括了编码规范及提交工作流程。
见链接:https://libcloud.readthedocs.io/en/latest/development.html#contributing-bigger-changes
翻译一下。
- 代码风格
遵循PEP8规范
使用四个空格缩紧
每行最长为79个字符
确保已经编辑好的文件不包含任何尾随的空格
可以通过flake8检测代码规范
flake8 libcloud/edited_file.py
(单个文件) 和tox -e lint(整个项目)
最重要的是,遵循你编辑的文件样式风格和现有代码中的样式风格保持一致。
- 预提交钩子检测
为了更容易遵守风格指南,项目提供了一个git预提交钩子,它会自动检查修改后的Python文件是否违反风格指南。
在项目代码的根目录下安装:
ln -s contrib/pre-commit.sh .git/hooks/pre-commit安装好之后,这个工具会在提交之前会自动检测代码的风格,如果违反代码风格,这个提交将会被丢弃。
- 代码约束
- 导入模块顺序
标准库
第三方库
本地库
例子,每一类模块用空行区分:
import sys
import base64
import paramiko
from libcloud.compute.base import Node, NodeDriver
from libcloud.compute.providers import Provider
- 函数和方法顺序
模块中的函数和类中的方法应按遵循以下顺序:
- “公共”函数/方法
- “私有”函数/方法(下划线为前缀的方法)
- “内部”方法(前缀和后缀双下划线)
例子:
class Unicorn(object):
def __init__(self, name='fluffy'):
self._name = name
def make_a_rainbow(self):
pass
def _get_rainbow_colors(self):
pass
def __eq__(self, other):
return self.name == other.name
驱动程序类上的方法应按遵循以下顺序:
- 标准api的方法
- 扩展方法
- “私有”方法(下划线为前缀的方法)
- “内部”方法(前缀和后缀双下划线)
功能相似的方法应该被划分在一块,并一个接一个的定义
例子:
class MyDriver(object):
def __init__(self):
pass
def list_nodes(self):
pass
def list_images(self):
pass
def create_node(self):
pass
def reboot_node(self):
pass
def ex_create_image(self):
pass
def _to_nodes(self):
pass
def _to_node(self):
pass
def _to_images(self):
pass
def _to_image(self):
pass
遵循这种顺序能够使得阅读和生产api文档更容易,而且可读性更强。
优先使用关键字代替常规参数
为了更好的可读性和理解的代码,关键字比常规参数优先。
# Good
some_method(public_ips=public_ips, private_ips=private_ips)
# Bad
some_method(public_ips, private_ips)
- 不要滥用** kwargs
应该始终在函数或方法签名中显式声明参数,并且只有在有有效的用例时才使用** kwargs和* args。
使用** kwargs某些时候是违反Python的“显式优于隐式”的,并且可能使API变得含糊不清。最重要的是,它使许多有用的东西,如程序性API内省变得不可靠。
可以用添加默认参数替代。
# Good
def my_method(self, name, description=None, public_ips=None):
pass
# Bad
def my_method(self, name, **kwargs):
description = kwargs.get('description', None)
public_ips = kwargs.get('public_ips', None)
- 返回字典时,记录其结构
Python的动态性非常有用,但是如果以错误的方式使用它,它也可能使得API消费者很难理解正在发生什么以及返回什么类型的值。
如果有一个函数或一个方法返回一个字典,确保在文档里记录返回的字典包含哪些键。
- 在检查是否提供或定义变量时,优先使用“is not None”
检测一个变量的存在性,优先使用if foo is not None替代if foo。
如果你使用if foo的防水,很容易犯一个错误,就是一个有效的值也可以是false(例如数字0)。
例子
class SomeClass(object):
def some_method(self, domain=None):
params = {}
if domain is not None:
params['Domain'] = domain