3 Python样式规则
3.1 分号
行尾不能加分号, 也不能用分号将两条命令放在同一行.
3.2 行长度
每行不超过80个字符。
下列情况除外:
(1)长导入语句。
(2)URL、路径名或注释中的长标志。
(3)不包含空格的长字符串模块级常量,不便跨行拆分,如URL或路径名。
(4)Pylint禁用注释。(例如:#pylint:disable=无效名称)
3.3 括号
谨慎使用括号。
在元组周围可以使用括号。但千万不要在返回语句或条件语句中使用它们,除非将括号用于隐含的行继续或表示元组。
3.4 缩进
使代码块缩进4个空格。
切勿使用tab, 也不要tab和空格混用.
3.5 空行
顶级定义之间有两个空行,例如函数定义或者类定义。方法定义之间以及类定义与第一个方法之间有一个空白行。函数或方法中,根据需要使用单个空白行,你觉得合适, 就空一行。
3.6 空格
括号,方括号或大括号内不要使用空格.
Yes: spam(ham[1], {eggs: 2}, []) No: spam( ham[ 1 ], { eggs: 2 }, [ ] ) |
逗号、分号或冒号前不要使用空格。逗号、分号或冒号后使用空格,行尾除外。
Yes: if x == 4: print(x, y) x, y = y, x No: if x == 4 : print(x , y) x , y = y , x |
开始参数列表、索引或切片的左括号/括号前不要使用空格。
在二元操作符两边都加上一个空格, 比如赋值(=)(例外:当存在类型注释时,在默认参数值的周围使用空格。), 比较(==, <, >, !=, <>, <=, >=, in, not in, is, is not), 布尔值(and, or, not).
不要用空格来垂直对齐多行间的标记, 这会成为维护的负担(适用于:,#,=,等)
3.7 Shebang
大多数.py文件不需要以#!开头。只有被直接执行的文件中才有必要加入#!.
3.8 注释和文档字符串
3.8.1文档字符串
对文档字符串的惯例是使用三重双引号""". 一个文档字符串应该这样组织: 首先是一行以句号, 问号或惊叹号结尾的概述(或者该文档字符串单纯只有一行). 接着是一个空行. 接着是文档字符串剩下的部分, 它应该与文档字符串的第一行的第一个引号对齐.
3.8.2模块
每个文件应该包含一个许可样板. 根据项目使用的许可(例如, Apache 2.0, BSD, LGPL, GPL), 选择合适的样板.
3.8.3函数和方法
下文所指的函数,包括函数, 方法, 以及生成器.
一个函数必须要有文档字符串, 除非它满足以下条件:
(1)外部不可见
(2)非常短小
(3)简单明了
文档字符串应该包含函数做什么,以及输入和输出的详细描述.通常,不描述"怎么做",除非是一些复杂的算法.
关于函数的几个方面应该在特定的小节中进行描述记录.每节应该以一个标题行开始.标题行以冒号结尾.除标题行外,节的其他内容应被缩进2个空格.
Args:
列出每个参数的名字,并在名字后使用一个冒号和一个空格,分隔对该参数的描述.如果描述太长超过了单行80字符,使用2或者4个空格的悬挂缩进(与文件其他部分保持一致).描述应该包括所需的类型和含义.如果一个函数接受*foo(可变长度参数列表)或者**bar (任意关键字参数),应该详细列出*foo和**bar.
Returns(或者Yields:用于生成器):
描述返回值的类型和语义. 如果函数返回None, 这一部分可以省略.
Raises:
列出与接口有关的所有异常.
3.8.4类
类应在描述该类的类定义下具有一个文档字符串。如果类具有公共属性,采用与函数Args小节相同的格式 。
3.8.5块和内联注释
最需要写注释的是代码中那些技巧性的部分.如果你在下次代码审查的时候必须解释一下,那么应该马上写注释.对于复杂的操作,应该在其操作开始前写上注释.对于不是一目了然的代码,应在其行尾添加注释.
为了提高可读性,这些注释应与代码之间以注释字符开始至少2个空格#,然后在注释文本本身之前至少留1个空格。
3.9 类
类无需显式继承object(除非与Python 2兼容)。
Modern: class SampleClass: pass
class OuterClass:
class InnerClass: pass |
3.10 字符串
使用format方法或%运算符来格式化字符串,即使参数都是字符串。在+和%(或format)之间做出选择。
避免使用+和+=运算符在循环中累积字符串。由于字符串是不可变的,这将创建不必要的临时对象,并导致二次而不是线性的运行时间。
Yes: items = ['<table>'] for last_name, first_name in employee_list: items.append('<tr><td>%s, %s</td></tr>' % (last_name, first_name)) items.append('</table>') employee_table = ''.join(items) No: employee_table = '<table>' for last_name, first_name in employee_list: employee_table += '<tr><td>%s, %s</td></tr>' % (last_name, first_name) employee_table += '</table>' |
3.11 文件和sockets
在文件和sockets结束时, 显式的关闭它.
除文件外,sockets或其他类似文件的对象在没有必要的情况下打开,会有很多弊端。
管理文件的首选方法是使用with语句:
with open("hello.txt") as hello_file: for line in hello_file: print(line) |
对于不支持该with语句的类文件对象,请使用 contextlib.closing():
import contextlib
with contextlib.closing(urllib.urlopen("http://www.python.org/")) as front_page: for line in front_page: print(line) |
3.12 TODO注释
为临时代码使用TODO注释, 这是一种短期解决方案.
TODO注释应该在所有开头处包含”TODO”字符串,之后用括号括起来的你的名字,email地址或其它标识符.然后是一个可选的冒号.接着必须有一行注释,解释要做什么.主目的是要有一个一致的TODO格式,可以搜索该格式以了解如何获取更多详细信息。写了TODO注释并不保证写的人会亲自解决问题.但当你写了一个TODO注释,请注上你的名字.
# TODO(kl@gmail.com): Use a "*" here for string repetition. # TODO(Zeke) Change this to use relations. |
如果TODO采用的格式是“在将来某个日期做某事”,请确保输入的日期非常明确(“ 2009年11月之前修复”)或特定的事件(“当所有客户端都可以处理XML响应时删除此代码”)。”)。
3.13 导入格式
每个导入应该独占一行。
Yes: import os import sys from typing import Mapping, Sequence No: import os, sys |
导入总应该放在文件顶部,位于模块注释和文档字符串之后,模块全局变量和常量之前.导入应该按照从最通用到最不通用的顺序分组:
Python将来的导入语句。例如:
from __future__ import absolute_import from __future__ import division from __future__ import print_function |
Python标准库导入。例如:
import sys |
第三方模块或软件包的导入。例如:
import tensorflow as tf |
代码存储库子包导入。例如:
from otherproject.ai import mind |
不推荐使用:特定于应用程序的导入,属于与此文件相同的顶级子程序包的一部分。例如:
from myproject.backend.hgwells import time_machine |
3.14 语句
通常每行只有一条语句。如果测试结果与测试语句在一行放得下,你也可以将它们放在同一行.如果是if语句,只有在没有else时才能这样做.
特别,不能对 try/except 这样做,因为try和except不能放在同一行.
No:
if foo: bar(foo) else: baz(foo)
try: bar(foo) except ValueError: baz(foo)
try: bar(foo) except ValueError: baz(foo) |
3.15 访问控制
如果访问器函数比较琐碎,则应使用公共变量而不是访问器函数,以避免Python中函数调用的额外开销。
如果访问更为复杂,或者访问变量的成本很高,则应使用函数调用(遵循命名准则),例如get_foo()和set_foo()。
3.16 命名
函数名称,变量名称和文件名应具有描述性;避开缩写。特别是,不要使用项目外部读者不清楚或不熟悉的缩写,也不要通过删除单词中的字母来缩写。
始终使用.py文件扩展名。切勿使用破折号。
3.17 Main
在Python中, pydoc以及单元测试要求模块必须是可导入的.代码应该在执行主程序前总是检查 if __name__ == '__main__' , 这样当模块被导入时主程序就不会被执行.
使用absl时,请使用app.run:
from absl import app ...
def main(argv): # process non-flag arguments ...
if __name__ == '__main__': app.run(main) |
否则,请使用:
def main(): ...
if __name__ == '__main__': main() |
3.18 函数长度
长函数有时是合适的,因此对函数长度没有硬性限制。但如果函数超过40行,请考虑是否可以在不损害程序结构的情况下将其分解。
3.19 类型注释
3.19.1一般规则
至少注释您的公共API。
运用判断力在一方面的安全性和清晰度与另一方面的灵活性之间取得良好的平衡。
注释易于发生类型相关错误(先前的错误或复杂性)的代码。
注释难以理解的代码。
从类型角度来看,对代码变得稳定时,请对其进行注释。在许多情况下,您可以在成熟的代码中注释所有功能,而不会失去太多的灵活性。
3.19.2换行
注释后,许多功能签名将变成“每行一个参数”。
def my_method(self, first_var: int, second_var: Foo, third_var: Optional[Bar]) -> int: ... |
如果函数名称,最后一个参数和返回类型的组合太长,请在新行中缩进。
def my_method( self, first_var: int) -> Tuple[MyLongType1, MyLongType1]: ... |
当返回类型与最后一个参数不在同一行时,首选方法是在新行上将参数缩进,并将右括号与对齐def。
def my_method( self, other_arg: Optional[MyLongType] ) -> Dict[OtherLongType, MyLongType]: ... |
最好不要破坏类型。但是,有时它们太长而不能放在一行上,尝试保持子类型不间断。
def my_method( self, first_var: Tuple[List[MyLongType1], List[MyLongType2]], second_var: List[Dict[ MyLongType3, MyLongType4]]) -> None: ... |
如果单个名称和类型太长,请考虑为该类型使用别名。
Yes: def my_function( long_variable_name: long_module_name.LongTypeName, ) -> None: ... No: def my_function( long_variable_name: long_module_name. LongTypeName, ) -> None: ... |
3.19.3转发声明
如果需要使用尚未定义的同一模块中的类名,例如,如果需要类声明中的类,或者使用下面定义的类,请使用字符串作为类名。
class MyClass:
def __init__(self, stack: List["MyClass"]) -> None: |
3.19.4默认值
对于同时具有类型注释和默认值的参数,‘=’仅在空格周围使用。
Yes: def func(a: int = 0) -> int: ... No: def func(a:int=0) -> int: ... |
3.19.5键入变量
如果内部变量的类型很难或无法推断,则可以通过两种方式指定其类型。
(1)# type:在行尾使用注释
a = SomeUndecoratedFunction() # type: Foo |
(2)与函数参数一样,在变量名称和值之间使用冒号
a: Foo = SomeUndecoratedFunction() |