PEP8-python编码规范(下)

1.结尾逗号

结尾的逗号通常是可选的,除了在构成一个元素的元组时是强制性需要的(在Python 2 中,它们对 print 语句有语义)。为了清晰起见,建议将后者用括号括起来(在技术上是多余的)。

Yes:
FILES = ('setup.cfg',)
下面的容易混淆:
FILES = 'setup.cfg',

模式是将每个值(等等)单独放在一行上,总是添加一个逗号,并在下一行添加右括号/括号/括号。然而,在同一行中使用逗号作为结束分隔符是没有意义的(除了在上面的单例元组

中)。

Yes:
FILES = [
  'setup.cfg',
  'tox.ini',
  ]
initialize(FILES,
     error=True,
        )
No:
FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)

 

2.注释

与代码功能相矛盾的注释比没有注释更糟糕。当代码发生变化时,一定要优先保证注释的更新!(尤其是函数中的参数和注释)

注释应该是完整的句子。第一个单词应该大写,除非它是以小写字母开头的标识符(千万不要改变标识符的大小写!)

块注释通常由完整句子组成的一个或多个段落组成,每个句子以句号结尾。

在多句注释中,除了最后一句之外,在一个句子结束后应该使用两个空格。

来自非英语国家的 Python 程序员:请用英语写你的注释,除非你有 120%的把握知道代码不会被不懂你语言的人读到。

(1)块注释

块注释通常适用于跟随它们的一些(或所有)代码,并且缩进到与代码相同的级别。块注释的每一行都以#和单个空格开头(除非它是注释内部缩进的文本)。

(2)内联注释

有节制的使用内联注释。

内联注释是与语句同一行的注释。内联注释应该与语句至少分隔两个空格。它们应该从一个#和一个空格开始。

如果代码陈述了明显的内容,那么内联注释是不必要的,实际上内联注释还会分散注意力。不要这样做:

x = x + 1    # Increment x
但有时候,这样也是有用的:
x = x + 1    # Compensate for border

(3)文档注释

所有公共模块、函数、类和方法需要编写文档字符串。非公共方法不需要编写文档字符串,但是应该有一个注释来描述该方法的功能。这个注释应该出现在def 行之后。

PEP 257 描述了良好的文档字符串约定。最重要的是,结束多行文档字符串的""" 应该单独在一行上

"""Return a foobang
Optional plotz says to frobnicate the bizbaz first.
"""

 对于一个行文档字符串,请保持结束注释时的 """ 在同一行。

 

3.命名约定

Python 库的命名约定有点混乱,因此我们永远不会得到完全一致的命名约定——不过,下面是当前推荐的命名标准。应该根据这些标准编写新的模块和包(包括第三方框架),但如果现有库具有不同的风格,则首选内部一致性。

以下命名风格通常是可以区分的:

b (单个小写字母)
B (单个大写字母)

lowercase
lower_case_with_underscores
UPPERCASE
UPPER_CASE_WITH_UNDERSCORES
CapitalizedWords (或 CapWords, 或 CamelCase -- 之所以这样命名是因
为它的字母看起来很凹凸不平)。这有时也被称为 StudlyCaps

注意:当在 CapWords 中使用首字母缩略词时,需要将首字母缩略词大写。因此HTTPServerError 比 HttpServerError 好。

mixedCase (与 CapitalizedWords 不同的是首字母小写!)
Capitalized_Words_With_Underscores (不推荐)

还有一种风格是使用一个简短的唯一前缀将相关名称组合在一起。这在 Python中并不常用,但为了完整起见,本文提到了这一点。例如,os.stat()函数返回一个元组,其项通常具有 st_mode、st_size、st_mtime 等名称。(这样做是为了强调与 POSIX 系统调用 struct 的字段的对应关系,这有助于程序员熟悉这些字段。)

(1)需要避免的命名

千万不要把“l”(小写字母 el)、“O”(大写字母 oh)或“I”(大写字母 eye)作为单个字符变量名。在某些字体中,这些字符与数字 1 和 0 没有区别。当你想用“l”的时候,用“L”代替

模块应该用简短的、全小写的名称。如果能提高可读性,可以在模块名中使用下划线。Python 包也应该用简短的、全小写的名称,尽管不鼓励使用下划线。

当用 C 或 c++编写的扩展模块有一个附带的 Python 模块,该模块提供更高级别的接口(例如,更面向对象的接口)时,C/ c++模块有一个前导下划线(例如_socket)。

(2)异常命名

因为异常应该是类,所以这里使用类命名约定。但是,您应该在异常名称上使用后缀“Error”(如果异常实际上是一个错误)。

(3)全局变量命名

具有 from M import * 设计的模块应该使用__all__机制来防止导出全局变量,或者使用旧的将这些全局变量加上下划线作为前缀的惯例(您可能希望这样做以表明这些全局变量是“模块非公共的”)。

(4)函数和变量命名

函数名应该是小写的,需要用下划线分隔单词以提高可读性。变量名与函数名遵循相同的约定。

(5)函数和方法参数

使用 self 作为第一个参数来实例化方法。

使用 cls 作为类方法的第一个参数

如果函数参数的名称与保留关键字冲突,通常最好是附加一个单后置下划线,而不是使用缩写或拼写错误。因此 class_比 clss 好。(最好是使用同义词来避免这种冲突。)

(6)方法名和实例变量

 使用函数命名规则:必须用下划线分隔单词的小写字母来提高可读性。

仅对非公共方法和实例变量使用一个前导下划线,如_name。 

为了避免与子类的名称冲突,使用两个前导下划线来调用 Python 的名称管理规则,如__name。

Python 用类名来处理这些名称:如果类 Foo 有一个名为__a 的属性,Foo.__a 就不能访问它。(坚持不懈的用户仍然可以通过调用 Foo._Foo__a 获得访问权限。)通常,双前导下划线应该仅用于避免名称与设计为子类的类中的属性冲突。一般而言,属性名或方法名有两个前导下划线,那么默认是希望自己内部调用,而外部不需要调用。 

(7)常量

常量通常在模块级别上定义,用所有大写字母和下划线分隔单词。示例包括MAX_OVERFLOW 和 TOTAL。 

 

总结:

公共属性应该没有前导下划线。

如果公共属性名与保留关键字冲突,则在属性名后面附加一个下划线。这比缩写或错误的拼写更可取。(然而,尽管有这个规则,“cls”是任何已知为类的变量或参数的首选拼写,特别是类方法的第一个参数)。 

如果您的类是要被子类化的,并且您有不希望子类使用的属性,请考虑使用双前导下划线和无尾随下划线来命名它们。 

任何向后兼容保证只适用于公共接口。

(8)公共和内部接口

文档化的接口被认为是公共的,所有未文档化的接口都应该假定为内部接口。 

为了更好地支持内省,模块应该使用__all__属性显式地声明其公共 API 中的名称。将__all__设置为空列表表示该模块没有公共 API。即使正确设置了__all__,内部接口(包、模块、类、函数、属性或其他名称)仍然应该以一个前导下划线作为前缀。

如果任何包含名称空间(包、模块或类)被认为是内部的,那么接口也被认为是内部的。

 

4.编程建议

(1)不依赖 CPython 的高效实现就地字符串连接的语句形式+ = b 或 a = a +b。这种优化即使在 CPython 中也是脆弱的(它只适用于某些类型),并且在不使用 refcount 的实现中根本不存在。在库的性能敏感部分,应该使用.join()表单。这将确保连接在各个变量之间的线性时间内发生。 

(2)像 None 这样的单例比较操作应该使用 is 或 not 来做,永远不要用等式运算符。 

(3)注意写 if x ,当你真正的意思 if x is not None -- e.g 测试一个默认为 None 的变量或参数是否被设置为其他值时。另一个值可能有一个类型(比如容器),在布尔上下文中可能为 false ! 

(4)使用 is not 操作,而不是 not ... is 是多少。虽然这两个表达式在功能上是相同的,但前者更易于阅读,更受青睐。 

Yes:
if foo is not None:
No:
if not foo is None:


(5)在实现具有丰富比较的排序操作时,最好实现所有的六个操作(__eq__、__ne__、__lt__、__le__、__gt__、__ge__),而不是依赖其他代码来执行特定的比较操作。 

(6)始终使用 def 语句,而不是将 lambda 表达式直接绑定到标识符的赋值语句

Yes:

def f(x): return 2*x
No:
f = lambda x: 2*x

(7)当将捕获的异常绑定到名称时,最好使用 Python 2.6 中添加的显式名称绑定语法:

try:
  process_data()
except Exception as exc:
  raise DataProcessingFailedError(str(exc))

(8)对于所有 try/except 子句,将 try 子句限制为所需代码的绝对最小数量。同样,这可以避免掩盖 bug。

Yes:

try:

  value = collection[key]
except KeyError:
  return key_not_found(key)
else:
  return handle_value(value)
No:
try:
# Too broad!
return handle_value(collection[key])
except KeyError:
# Will also catch KeyError raised by handle_value()
return key_not_found(key) 

 

(9)上下文管理器应该通过单独的函数或方法调用,只要它们做的不是获取和释放资源:

Yes:

with conn.begin_transaction():
  do_stuff_in_transaction(conn)
No:
with conn:
  do_stuff_in_transaction(conn) 

后一个示例没有提供任何信息来表明__enter__和__exit__方法在执行事务后关闭连接之外的其他操作。在这种情况下,明确是很重要的。 

 

(10)在返回语句中保持一致。函数中的所有返回语句都应该返回表达式,或者它们都不应该返回表达式。如果任何 return 语句返回一个表达式,任何没有返回值的 return 语句都应该显式地将其声明为 return None,并且在函数末尾应该显示显式的 return 语句(如果可达的话)。

Yes:
def foo(x):
  if x >= 0:
    return math.sqrt(x)
  else:
    return None
def bar(x):
  if x < 0:
    return None
  return math.sqrt(x)
No:
def foo(x):
  if x >= 0:
    return math.sqrt(x)
def bar(x):
if x < 0:
  return
return math.sqrt(x) 

 

(11)使用.startswith()和.endswith()代替字符串切片检查前缀或后缀。startswith()和 endswith()更干净,更不容易出错: 


Yes: if foo.startswith('bar'):
No: if foo[:3] == 'bar':


(12)对象类型比较应该始终使用 isinstance(),而不是直接比较类型。
Yes: if isinstance(obj, int):
No: if type(obj) is type(1): 

 

(13)当检查对象是否为字符串时,请记住它可能也是 unicode 字符串!在 Python 2中,str 和 unicode 有一个公共基类 basestring,因此您可以这样做:

if isinstance(obj, basestring): 

注意,在 Python 3 中,unicode 和 basestring 不再存在(只有 str), bytes对象不再是某种字符串(而是整数序列)。 


(14)对于序列(字符串、列表、元组),使用空序列为假的事实。
Yes: if not seq:
if seq:

No: if len(seq):
if not len(seq:

 

(15)不要使用==将布尔值与真值或假值进行比较。

Yes:

if greeting:

No:

if greeting==True:

Worse:

if greeting is True:


 

 



 

posted @ 2019-08-02 19:41  cymx66688  阅读(336)  评论(0编辑  收藏  举报