谷歌开源项目风格指南---python代码规范

1. 不要使用分号

永远不要在代码的末尾使用分号,也不要用分号将两条命令放在一行

a = 4 ; b = 5
print(a, b)

这样的代码可以被执行,但是建议你不要这样做

2. 每行不超过80个字符

一行代码的长度过长,影响阅读,因此建议每行代码长度不要超过80个字符,对于那些确实很长的语句,可以采用适当的方法换行

2.1 长字符串

text = "如何保证代码质量,是一个永恒的话题。开发人员必须时刻与自身的懒惰对抗,人们总是不由自主的写出烂代码," \
       "并总是能找到一个合理的接口为自己开脱,最常用的理由是先保证项目上线,优化的事情以后再说"

使用反斜杠的确可以连接一个长字符串,但谷歌的风格指南里建议你使用使用圆括号来实现隐式行连接

text = ("如何保证代码质量,是一个永恒的话题。开发人员必须时刻与自身的懒惰对抗,人们总是不由自主的写出烂代码"
        "并总是能找到一个合理的接口为自己开脱,最常用的理由是先保证项目上线,优化的事情以后再说")
print(text)

2.2 参数很多的函数

def test(name, age, _class, score, address, phone, 
         id_no, card):
    pass

不论是定义函数还是调用函数时,如果参数太多导致一行代码字符串数量过多时都可以用这种方法规避。

3. 要过度使用括号

除非是用于实现行连接, 否则不要在返回语句或条件语句中使用括号

3.1 函数返回多个结果时不需要使用括号

def test(x):
    return x, x**2

函数返回多个结果时会自动封包成元组,不需要使用括号, 下面的操作毫无必要

def test(x):
    return (x, x**2)

3.2 条件语句

a = 5
b = 4

if a == 5 and b == 4:
    pass

下面的代码是错误的使用

a = 5
b = 4

if (a == 5 and b == 4):
    pass

4. 缩进

一次缩进4个空格,注意不要使用tab来代替空格,更不能tab和空格混合使用

5. 空行

函数或者类之间要空两行。方法定义之间,类定义与第一个方法之间空一行, 函数和方法内部,你可以根据自己的习惯在你觉得合适的位置上空一行

def func1():
    pass


def func2():
    pass


class Stu:

    def __init__(self, name):
        self.name = name

    def run(self):
        print('{name} is running'.format(name=self.name))

6. 空格

  1. 括号内不要有空格
  2. 不要在逗号, 分号, 冒号前面加空格, 但应该在它们后面加(除了在行尾).
  3. 参数列表, 索引或切片的左括号前不应加空格
  4. 在二元操作符两边都加上一个空格, 比如赋值(=), 比较(==, <, >, !=, <>, <=, >=, in, not in, is, is not), 布尔(and, or, not)
  5. 不要用空格来垂直对齐多行间的标记

下面是一些错误使用空格的示例

print( 1 )      # 括号里有空格
print(1, 4 ,5)  # 逗号前面加空格
a = {'a' :1}    # 分号前面加空格

print(a ['a'])  # 做括号前面加空格
a=5             # 二元操作符两边应该加空格, 正确的做法是 a = 5
a = {
    'a'   : 1,              # 为了让冒号对齐强行加空格是不正确的
    'name': 'python'
}

7. Shebang

大部分python文件都不必以#!作为文件第一行的开始,程序的主文件才有必要使用#!/usr/bin/python2或者 #!/usr/bin/python3开始, #!先用于帮助内核找到Python解释器, 但是在导入模块时, 将会被忽略。

8. 注释

8.1 模块

每个文件应该包含一个许可样板. 根据项目使用的许可(例如, Apache 2.0, BSD, LGPL, GPL), 选择合适的样板

8.2 函数和方法

一个函数必须要有文档字符串, 除非它满足以下条件:

  1. 外部不可见
  2. 非常短小
  3. 简单明了

文档字符串应该包含函数做什么, 以及输入和输出的详细描述. 通常, 不应该描述”怎么做”, 除非是一些复杂的算法. 文档字符串应该提供足够的信息, 当别人编写代码调用该函数时, 他不需要看一行代码, 只要看文档字符串就可以了. 对于复杂的代码, 在代码旁边加注释会比使用文档字符串更有意义.

关于函数的几个方面应该在特定的小节中进行描述记录, 这几个方面如下文所述. 每节应该以一个标题行开始. 标题行以冒号结尾. 除标题行外, 节的其他内容应被缩进2个空格.

Args:
列出每个参数的名字, 并在名字后使用一个冒号和一个空格, 分隔对该参数的描述.如果描述太长超过了单行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

8.3 类里的注释

类应该在其定义下有一个用于描述该类的文档字符串. 如果你的类有公共属性(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."""

8.4 块注释和行注释

最需要写注释的是代码中那些技巧性的部分. 如果你在下次 代码审查 的时候必须解释一下, 那么你应该现在就给它写注释. 对于复杂的操作, 应该在其操作开始前写上若干行注释. 对于不是一目了然的代码, 应在其行尾添加注释.

# 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 if i is 0 or a power of 2.

为了提高可读性, 注释应该至少离开代码2个空格.

另一方面, 绝不要描述代码. 假设阅读代码的人比你更懂Python, 他只是不知道你的代码要做什么

9. 类

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

class Stu(object):
    pass

继承自 object 是为了使属性(properties)正常工作, 并且这样可以保护你的代码, 使其不受 PEP-3000 的一个特殊的潜在不兼容性影响. 这样做也定义了一些特殊的方法, 这些方法实现了对象的默认语义, 包括

__new__, __init__, __delattr__, __getattribute__, __setattr__, __hash__, __repr__, and __str__

10. 字符串

避免在循环中用+和+=操作符来累加字符串, 因为字符串是不可变对象,每一次的累加操作都会产生一个新的字符串,作为替代方案, 你可以将每个子串加入列表, 然后在循环结束后用 .join 连接列表,也可以将每个子串写入一个 cStringIO.StringIO 缓存中。

但对于简单的两个字符串之间的连接操作,直接使用+连接就可以,没有必要也使用%或者format方法格式化。

11. 文件和sockets

在文件和sockets结束时, 显式的关闭它,如果不关闭,会导致非常严重的问题,而且很难排查,你可以使用with关键字来管理文件

with open("hello.txt") as hello_file:
    for line in hello_file:
        print line

with语句块结束时,会帮你关闭文件

12. TODO注释

为临时代码使用使用TODO注释,或者标注将来做某件事情,TODO注释应包含括号括起来的作者的名字,在一个可选的冒号后面添加注释,解释要做什么,如果可以,请加上一个日期

# TODO(kwsy): 2020年1月10日完善Stu的细节
class Stu(object):
    pass

13. 导入格式

每个导入应该独占一行,不要在一行导入里导入多个模块
导入总应该放在文件顶部, 位于模块注释和文档字符串之后, 模块全局变量和常量之前. 导入应该按照从最通用到最不通用的顺序分组:

  1. 标准库导入
  2. 第三方库导入
  3. 应用程序指定导入
    每种分组中, 应该根据每个模块的完整包路径按字典序排序, 忽略大小写.
import foo
from foo import bar
from foo.bar import baz
from foo.bar import Quux
from Foob import ar

14. 语句

每个语句应该独占一行

15. 访问控制

这一点,可能重视封装的面向对象程序员看到这个可能会很反感,对于那些频繁访问使用又不太重要的属性,不妨使用公有属性来替代,这样就避免了添加大量set_xxx和get_xxx方法,我建议你使用property属性,既能像使用公有属性那样方便,又能保证属性的安全性。

16. 变量命名

16.1 应该避免的名称

  1. 单字符名称, 除了计数器和迭代器.
  2. 包/模块名中的连字符(-)
  3. 双下划线开头并结尾的名称(Python保留, 例如__init__)

16.2 命名约定

  1. 所谓”内部(Internal)”表示仅模块内可用, 或者, 在类内是保护或私有的.
  2. 用单下划线(_)开头表示模块变量或函数是protected的(使用from module import *时不会包含).
  3. 用双下划线(__)开头的实例变量或方法表示类内私有.
  4. 将相关的类和顶级函数放在同一个模块里. 不像Java, 没必要限制一个类一个模块.
  5. 对类名使用大写字母开头的单词(如CapWords, 即Pascal风格), 但是模块名应该用小写加下划线的方式(如lower_with_under.py). 尽管已经有很多现存的模块使用类似于CapWords.py这样的命名, 但现在已经不鼓励这样做, 因为如果模块名碰巧和类名一致, 这会让人困扰.

Python之父Guido推荐的规范

Type Public Internal
Modules lower_with_under _lower_with_under
Packages lower_with_under  
Classes CapWords _CapWords
Exceptions CapWords  
Functions lower_with_under() _lower_with_under()
Global/Class Constants CAPS_WITH_UNDER _CAPS_WITH_UNDER
Global/Class Variables lower_with_under _lower_with_under
Instance Variables lower_with_under _lower_with_under (protected) or __lower_with_under (private)
Method Names lower_with_under() _lower_with_under() (protected) or __lower_with_under() (private)
Function/Method Parameters lower_with_under  
Local Variables lower_with_under  

17. main

在Python中, pydoc以及单元测试要求模块必须是可导入的. 你的代码应该在执行主程序前总是检查 if __name__ == '__main__' , 这样当模块被导入时主程序就不会被执行.

def main():
      ...

if __name__ == '__main__':
    main()

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案