格式规范
检查工具:pylint、flake8
依赖安装:
pip install flake8
pip install pylint
pip install flake8-docstrings
pip install flake8-import-order
检查命令:
pylint -r y ${PROJECT}
flake8 —docstring-convention google —import-order-style google —application-import-names app ${PROJECT}
风格规范:Google Style
参考链接:https://github.com/google/styleguide/blob/gh-pages/pyguide.md#382-modules
编程规范
1、主逻辑判断放外层
说明:能写一层就不写多层,方便阅读和代码逻辑扩展。
示例:
if user_name:if password:print("yes")else:raise Exception("No password")else:raise Exception("No user_name")
修正:
if not user_name:raise Exception("No user_name")if not password:raise Exception("No password")print("yes")
2、避免hard_code,统一管理常量
示例:
def my_func(var_a, var_b, max_bytes=510241024):"""docstring..."""
修正:
# Constants我们把常量写在一个Module中def my_func(var_a, var_b, max_bytes=Constants.MAX_BYTES):"""docstring..."""
3、移除不必要的常量,不要hard code
说明:如果不存在引用该常量,那么就舍弃掉。
示例:
class Constant:"""docstring..."""MODULE_REDIS = "single_redis"MODULE_APOLLO = "apollo"MODULE_LOG = "log" # 这里的MODULE_LOG只是纯粹为了和上述保持一致
修正:不需要形式上的统一,没有使用到就移除掉
class Constant:"""docstring..."""MODULE_REDIS = "single_redis"MODULE_APOLLO = "apollo"
4、文档说明,要句子不要短语
说明:句子以大写字母开头,以实心点结尾。
示例:
def get_logger(name):"""logger instance"""
修正:
def get_logger(name):"""Get a logger with the specified name."""
5、文档说明,不要口语化
说明:句子不要以We开头,不要无效的修饰。
示例:
def get_logger(name):"""Here we just want to get a logger instance with specified name."""
修正:
def get_logger(name):"""Get a logger with the specified name."""
6、任务拆解,每个MR只做一件事
说明:如果有多件事需要做,可以在代码中留TODO,以备下一个MR提交。
7、变量精简,减少重复赋值
示例:
host, port = db_uri.split(",")date_base = MySql(host,port)class MySql:"""docstring..."""def init(self, host, port):"""docstring"""self.host = hostself.port = port
修正:
date_bae = MySql(db_uri)class MySql:"""docstring..."""def init(self, db_uri):"""docstring..."""self.host, self.port = db_uri.split(",")
8、函数原子,模块原子
说明:一个函数干一件事,一个模块干一个事。
示例:
class ConfigCenter:"""docstring..."""... ...def log_socket_uri(self):"""docstring..."""section = Constant.SECTION_LOGentry = Constant.ENTRY_LOG_SOCKET_URIsocket_uri = self.config.get(section, entry)host, port = socket_uri.split(",")if int(port) < 0 or int(port) > 65536:raise ValueError("invalid port")return host, port
修正:配置中心的任务就是读配置,不需要做配置拆解,也不需要做拆解之后的校验
class ConfigCenter:"""docstring..."""... ...def log_socket_uri(self):"""docstring..."""section = Constant.SECTION_LOGentry = Constant.ENTRY_LOG_SOCKET_URIsocket_uri = self.config.get(section, entry)return socket_uri
9、避免为了形式好看而注释,让代码一目了然
说明:除非代码相对冗长且不可拆解,否则,不需要形式上的注释。
示例:
def get_instance():"""docstring..."""# for log consolelog_console = config.log_console# for log filelog_dir = config.log.file_diros.makedirs(log_dir, exist_ok=True)# for log socketsocket_uri = config.log_socket_uri
修正:不要好看,让代码自己一目了然,可以以空行间隔
def get_instance():"""docstring..."""log_console = config.log_consolelog_dir = config.log.file_diros.makedirs(log_dir, exist_ok=True)socket_uri = config.log_socket_ur
10、不要做变量的重复性赋值和拆解
说明:避免重复性劳动,或者没必要的打包
示例:
class ConfigCenter:"""docstring..."""... ...def redis_config(self):"""docstring..."""...redis_uri = self.config.get(Constant.SECTION_REDIS, Constant.ENTRY_REDIS_URI)return {Constant.REDIS: redis_uri}
修正:不必要的打包不要打
class ConfigCenter:"""docstring..."""... ...def redis_config(self):"""docstring..."""...redis_uri = self.config.get(Constant.SECTION_REDIS, Constant.ENTRY_REDIS_URI)return redis_uri
11、description 需要写完整的句子
说明:description 不能是单个词或短语,而应该为完整的句子
示例:
class UserUpdate(BaseModel): # pylint: disable=too-few-public-methods"""Body of Person PATCH requests."""username: Optional[str] = Field(None,max_length=20,description="username")
…
修正:
class UserUpdate(BaseModel): # pylint: disable=too-few-public-methods"""Body of Person PATCH requests."""username: Optional[str] = Field(None,max_length=20,description="The name of user.")
12、每行只导入一个 module
说明:在导入module时,应该每行只导入一个
示例:
from app.schemas.user import UserCreate, UserRead, UserUpdate
修正:
from app.schemas.user import UserCreatefrom app.schemas.user import UserReadfrom app.schemas.user import UserUpdate
13、函数注释使用有实际意义的 docstrings
说明:函数注释不能太过简略,要能够表达实际意义
示例:
class UniversalCacheInterface(ABC):"""Universal Cache Interface."""@abstractmethoddef get(self, key, *args):"""Get Method, Force to Implements."""
修正:
class UniversalCacheInterface(ABC):"""Universal Cache Interface."""@abstractmethoddef get(self, key):"""Return value for key in cache, force subclass to implement.""
14、interface 的方法定义中不宜使用 *args 参数
说明:在 interface 定义的方法中不宜出现 *args,因为需要精准把控方法接收的参数。
示例:
class ClusterRedis(UniversalCacheInterface):def __init__(self, conn_list, redis_password):"""Constructs a redis cluster instance."""self.instance = RedisCluster(startup_nodes=conn_list,decode_responses=True,password=redis_password)def get(self, key, *args):
…
修正:
def get(self, key):"""Return value for key."""return self.instance.get(key)
如果需要某个参数,例如ttl,可以逐个添加:
def set(self, key, value, ttl=None):"""Set key-value in redis cluster."""return self.instance.set(key, value, ex=ttl)
