Python编码规范

本文提供的 Python 编码规范基于 Python 主要发行版本的标准库。Python 编码规范的指导原则主要用于提升代码的可读性,使得在大量的 Python 代码中保持一致。许多项目有自己的编码规范,在出现规范冲突时,项目自身的规范优先。

Python 编码规范最常使用的是 PEP8 编码规范。

PEP8 编码规范中文版 PDF 下载地址:https://pan.baidu.com/s/1ZhrIODN1h1ccdXcPHbuDVQ

百度网盘提取码:o8oe

1. 命名

  • 文件名
  • 全部小写,可使用下划线。

  • 使用简短的小写的名字,如果下划线可以改善可读性可以加入,例如:mypackage。

  • 模块
  • 使用简短的小写的名字,如果下划线可以改善可读性可以加入,例如:如mymodule。

  • 总是使用首字母大写单词串。如MyClass。内部类可以使用额外的前导下划线。

  • 函数和方法
  • 函数名应该为小写,可以用下划线风格单词以增加可读性。如:myfunction,my_example_function。

  • 函数和方法的参数
  • 总使用“self”作为范例方法的第一个参数。总使用“cls”作为类方法的第一个参数。

    如果一个函数的参数名称和保留的关键字冲突,通常使用一个后缀下划线好于使用缩写或奇怪的拼写。

  • 全局变量
  • 对于from M import *导入语句,如果想阻止导入模块内的全局变量可以使用旧有的规范,在全局变量上加一个前导的下划线。

    *注意*:应避免使用全局变量

  • 变量
  • 变量名全部小写,由下划线连接各个单词。如color = WHITE,this_is_a_variable = 1。

    *注意*:

    1.不论是类成员变量还是全局变量,均不使用 m 或 g 前缀。

    2.私有类成员使用单一下划线前缀标识,多定义公开成员,少定义私有成员。

    3.变量名不应带有类型信息,因为Python是动态类型语言。如 iValue、names_list、dict_obj 等都是不好的命名。

  • 常量
  • 常量名所有字母大写,由下划线连接各个单词。例如:MAX_OVERFLOW、TOTAL。

  • 异常
  • 以“Error”作为后缀。
  • 前导后缀下划线
  • 1.一个前导下划线:表示变量或者方法非公有。

    2.一个后缀下划线:避免关键字冲突。

    3.两个前导下划线:当命名一个类属性引起名称冲突时使用。

    4.两个前导和后缀下划线:内部有特殊用途的对象或者属性,例如__init__或者__file__。绝对不要创造这样的名字,而只是使用它们。

2. 缩进

使用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,
   ...
}

3. 分号

不要在行尾加分号, 也不要用分号将两条命令放在同一行。

4. 行长度

  • 每行不超过80个字符,其中长的导入模块语句和注释里的URL除外
  • 不要使用反斜杠连接行。Python会将圆括号, 中括号和花括号中的行隐式的连接起来
  • 正确: 
    foo_bar(self, width, height, color='black', design=None, x='foo',
            emphasis=None, highlight=0)
    
  • 如果一个文本字符串在一行放不下, 可以使用圆括号来实现隐式行连接:
  • x = ('这是一个非常长非常长非常长非常长 '
        '非常长非常长非常长非常长非常长非常长的字符串')
    
    在注释中,如果必要,将长的URL放在一行上。
    # See details at
    # http://www.example.com/us/developer/documentation/api/content/v2.0/csv_file_specification.html
    

    5. 括号

    宁缺毋滥的使用括号,除非是用于实现行连接, 否则不要在返回语句或条件语句中使用括号,不过在元组两边使用括号是可以的。

    正确: 
    if foo:
        bar()
    while x:
        x = bar()
    if x and y:
        bar()
    if not x:
        bar()
    return foo
    for (x, y) in dict.items(): ...
         
    错误:
    if (x):
        bar()
    if not(x):
        bar()
    return (foo)
    

    6. 空行

    顶级定义之间空2行,比如函数或者类定义。方法定义之间空1行。

    方法定义、类定义与第一个方法之间,都应该空1行。函数或方法中,某些地方觉得合适就空一行。

    7. 空格

    • 按照标准的排版规范来使用标点两边的空格,括号内不要有空格,按照标准的排版规范来使用标点两边的空格
    • 正确: spam(ham[1], {eggs: 2}, [])
      错误: spam( ham[ 1 ], { eggs: 2 }, [ ] )	
      
    • 不要在逗号、分号、冒号前面加空格,但应该在它们后面加(除了在行尾)
    • 正确:
      if x == 4:
          print x, y
          x, y = y, x
      错误: 
      if x == 4 :
          print x , y
          x , y = y , x
      
    • 参数列表,索引或切片的左括号前不应加空格
    • 正确: spam(1)
      错误: spam (1)
      正确: dict['key'] = list[index]
      错误: dict ['key'] = list [index]
      
    • 在二元操作符两边都加上一个空格,比如赋值(=)、比较(==, <, >, !=, <>, <=, >=, in, not in, is, is not)、布尔(and, or, not)。至于算术操作符两边的空格该如何使用,需要你自己好好判断,不过两侧务必要保持一致。
    • 正确: x == 1
      错误: x<1
      
    • 当’='用于指示关键字参数或默认参数值时,不要在其两侧使用空格。
    • 正确: def complex(real, imag=0.0): return magic(r=real, i=imag)
      错误: def complex(real, imag = 0.0): return magic(r = real, i = imag)
      
    • 不要用空格来垂直对齐多行间的标记,因为这会成为维护的负担(适用于:, #, =等)
    • 正确: 
      foo = 1000  # 注释
      long_name = 2  # 注释不需要对齐
      dictionary = {
          "foo": 1,
          "long_name": 2,
          }
      
      错误: 
      foo       = 1000  # 注释
      long_name = 2     # 注释不需要对齐
      dictionary = {
          "foo"      : 1,
          "long_name": 2,
          }
      

    8. 注释

    • 文档字符串
    • Python有一种注释方式: 使用文档字符串。文档字符串是包、模块、类或函数里的第一个语句。这些字符串可以通过对象的__doc__成员被自动提取,并且被pydoc所用。

      对文档字符串的惯例是使用三重双引号"""。

    • 函数和方法:
    • 每节应该以一个标题行开始,标题行以冒号结尾。除标题行外, 节的其他内容应被缩进2个空格。

      列出每个参数的名字,并在名字后使用一个冒号和一个空格,分隔对该参数的描述.如果描述太长超过了单行80字符,使用2或者4个空格的悬挂缩进(与文件其他部分保持一致)。描述应该包括所需的类型和含义。如果一个函数接受foo(可变长度参数列表)或者**bar (任意关键字参数), 应该详细列出foo和**bar。

      Returns: (或者 Yields: 用于生成器)

      描述返回值的类型和语义。 如果函数返回None, 这一部分可以省略。

      Raises:

      列出与接口有关的所有异常。

      def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
          """Fetches rows from a Bigtable.
      
          Retrieves rows pertaining to the given keys from the Table instance
          represented by big_table.  Silly things may happen if
          other_silly_variable is not None.
      
          Args:
              big_table: An open Bigtable Table instance.
              keys: A sequence of strings representing the key of each table row
                  to fetch.
              other_silly_variable: Another optional variable, that has a much
                  longer name than the other args, and which does nothing.
      
          Returns:
              A dict mapping keys to the corresponding table row data
              fetched. Each row is represented as a tuple of strings. For
              example:
      
              {'Serak': ('Rigel VII', 'Preparer'),
               'Zim': ('Irk', 'Invader'),
               'Lrrr': ('Omicron Persei 8', 'Emperor')}
      
              If a key from the keys argument is missing from the dictionary,
              then that row was not found in the table.
      
          Raises:
              IOError: An error occurred accessing the bigtable.Table object.
          """
          pass
      
    • 类应该在其定义下有一个用于描述该类的文档字符串。如果你的类有公共属性(Attributes),那么文档中应该有一个属性(Attributes)段,并且应该遵守和函数参数相同的格式。

      class SampleClass(object):
          """
          Summary of class here.
      
          Longer class information....
          Longer class information....
      
          Attributes:
              likes_spam: A boolean indicating if we like SPAM or not.
              eggs: An integer count of the eggs we have laid.
          """
      
          def __init__(self, likes_spam=False):
              """Inits SampleClass with blah."""
              self.likes_spam = likes_spam
              self.eggs = 0
      
          def public_method(self):
              """Performs operation blah."""
      
    • 块注释和行注释
    • 最需要写注释的是代码中那些技巧性的部分。如果你在下次 代码审查 的时候必须解释一下,那么你应该现在就给它写注释。对于复杂的操作,应该在其操作开始前写上若干行注释。对于不是一目了然的代码,应在其行尾添加注释。为了提高可读性,注释应该至少离开代码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 iff i is a power of 2
      

      9. 类 class

      如果一个类不继承自其它类,就显式的从object继承,嵌套类也一样。

      class SampleClass(object):
          pass
      
      
      class OuterClass(object):
          class InnerClass(object):
              pass
      
      
      class ChildClass(ParentClass):
          """Explicitly inherits from another class already."""
      

      继承自 object 是为了使属性正常工作,并且这样可以保护你的代码,使其不受Python 3000的一个特殊的潜在不兼容性影响。这样做也定义了一些特殊的方法(魔法方法),这些方法实现了对象的默认语义,包括 new, init, delattr, getattribute, setattr, hash, repr, and str。

      10. 导入 import

      每个导入应该独占一行

      正确: 
      import os
      import sys
      错误: 
      import os, sys
      

      导入总应该放在文件顶部,位于模块注释和文档字符串之后,模块全局变量和常量之前。

      导入应该按照从最通用到最不通用的顺序分组:

      • 标准库导入
      • 第三方库导入
      • 应用程序指定导入

      每种分组都应该根据每个模块的完整包路径按字典序排序,忽略大小写。

      import foo
      from foo import bar
      from foo.bar import baz
      from foo.bar import Quux
      from Foob import ar
      

      11. 语句

      通常每个语句应该独占一行。不过,如果测试结果与测试语句在一行放得下,也可以将它们放在同一行。

      如果是if语句,只有在没有else时才能这样做。 特别地,绝不要对 try/except 这样做, 因为try和except不能放在同一行。

      正确:
      if foo: bar(foo)
        
      错误:
      if foo: bar(foo)
      else:   baz(foo)
      
      try:               bar(foo)
      except ValueError: baz(foo)
      

下一章:阿里数据库规范

阿里 MySQL 数据库规范:一、建表规约 1.【强制】表达是与否概念的字段,必须使用 is_xxx的方式命名,数据类型是 unsigned tinyint( 1表示是,0表示否)。2.【强制】表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。