Python Code Style

本文翻译自Google文档

http://google-styleguide.googlecode.com/svn/trunk/pyguide.html#Python_Style_Rules

说明

本文风格是Google内部的Python代码风格。

1. 分号

如果一句代码有多行,不要在每行后面使用分号 ,并且不要通过分号把多句代码放一行

2. 行长度

80

3.圆括号

节约使用(意思能不使用不使用,除非有特殊用途),举例

Yes: if foo:
         bar()
     while x:
         x = bar()
     if x and y:
         bar()
     if not x:
         bar()
     return foo
     for (x, y) in dict.items(): ...

No:  if (x):
         bar()
     if not(x):
         bar()
     return (foo)

4. 缩进

4个字符

5. 空白行

在两个最高成的定义之间使用两个空白行,在不同的方法之间使用一个空白行

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

圆括号,方括号之前没有空格

Yes: spam(1)

No:  spam (1)

Yes: dict['key'] = list[index]

No:  dict ['key'] = list [index]

 不用在等号两边使用空格,当在区分参数和设置默认值的时候

Yes: def complex(real, imag=0.0): return magic(r=real, i=imag)

No:  def complex(real, imag = 0.0): return magic(r = real, i = imag)

 不要使用空格做额外的对齐操作

Yes:
  foo = 1000  # comment
  long_name = 2  # comment that should not be aligned

  dictionary = {
      'foo': 1,
      'long_name': 2,
  }

No:
  foo       = 1000  # comment
  long_name = 2     # comment that should not be aligned

  dictionary = {
      'foo'      : 1,
      'long_name': 2,
  }

7. Shebang Line (组织行 )

大部分.py文件不需要以#!开头,除了主文件(即入口文件)要以#!/usr/bin/python开头。

这个开头是用来给内核找python的解释器用的。

8. 注释,文档说明

Modules 应该加上license文件

Functions and methods 应该加上注释除非满足如下条件

1.外部不可见

2.非常短

3.很明显

如下,注释模板来自bigtable

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

 类的注释

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."""

 内部块注释

# 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. 类

如果一个类不继承其它的类,应该明确的继承object,就算是内部类也应该这么做

Yes: class SampleClass(object):
         pass


     class OuterClass(object):

         class InnerClass(object):
             pass


     class ChildClass(ParentClass):
         """Explicitly inherits from another class already."""

No: class SampleClass:
        pass


    class OuterClass:

        class InnerClass:
            pass

 这样可以避免一些潜在错误。

10 字符串

使用格式化方法或者%操作符来格式化字符串,即便你知道所有的参数都是字符串,判断好什么时候该用+,什么时候用%.

Yes: x = a + b
     x = '%s, %s!' % (imperative, expletive)
     x = '{}, {}!'.format(imperative, expletive)
     x = 'name: %s; score: %d' % (name, n)
     x = 'name: {}; score: {}'.format(name, n)

No: x = '%s%s' % (a, b)  # use + in this case
    x = '{}{}'.format(a, b)  # use + in this case
    x = imperative + ', ' + expletive + '!'
    x = 'name: ' + name + '; score: ' + str(n)

 避免在循环中使用+,和+=,因为string 是不可以变的,这样会创建出很多不必要的临时对象。相反,在循环结束是用''.join 来将一个list变成string(或者将每个子字符串写进io.BytesIO 缓冲区)

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>'

 坚持使用一种风格的字符串符号

Yes:
  Python('Why are you hiding your eyes?')
  Gollum("I'm scared of lint errors.")
  Narrator('"Good!" thought a happy Python reviewer.')

No:
  Python("Why are you hiding your eyes?")
  Gollum('The lint. It burns. It burns us.')
  Gollum("Always the great lint. Watching. Watching.")

11. 文件和套接字

但处理完file 和socket 的时候一定记得关闭它们。

file, socket或者类文件的对象不必要的打开有下面这个缺点。

1. 消耗系统有限的资源,例如文件句柄。

2. 占用文件会阻止其它的的操作,例如移动和删除。

3. 他们在整个程序都是全局共享的,关闭会导致读和写出现异常。

虽然在对象离开生命周期的时候会自动销毁,但是不建议通过这种方式来处理。下面是原因

1. 首先这种方式没有保证,因为不同Python实现使用不同的内存管理技术,比如说延迟内存回收。

2. 没有预料到的应用会保持这些对象更加长的时间不被处理。

首选的方法管理文件是使用"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

 Pythone 2.5 可以使用 "with" 语句

from __future__ import with_statement

12 TODO 注释的使用

为暂时的,短期的解决方案,不够完美的代码使用TODO来标识。

13 Import的格式

不同的import 应该在不同行

Yes: import os
     import sys

No:  import os, sys

 引入比较多的时候应该分组,分组规则如下

1. 标准库

2. 第三方库

3. 应用级别的库

14 属性访问控制

但一个属性访问比较复杂或者,应该使用方法调用类似get_foo(), 和set_foo()来代替的。

15 命名

module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_CONSTANT_NAME, global_var_name, instance_var_name, function_parameter_name, local_var_name.

 Guido推荐的命名方式

Packageslower_with_under 
Moduleslower_with_under_lower_with_under
ClassesCapWords_CapWords
ExceptionsCapWords 
Functionslower_with_under()_lower_with_under()
Global/Class ConstantsCAPS_WITH_UNDER_CAPS_WITH_UNDER
Global/Class Variableslower_with_under_lower_with_under
Instance Variableslower_with_under_lower_with_under (protected) or __lower_with_under (private)
Method Nameslower_with_under()_lower_with_under() (protected) or __lower_with_under() (private)
Function/Method Parameterslower_with_under 
Local Variableslower_with_under 

16 Main

在Python, pydoc 和unit test 需要可被导入的modules,在执行你的程序之前,你的代码总应该做检查,这样可以避免你的module被引入的时候main 程序不被调用

def main():
      ...

if __name__ == '__main__':
    main()

17 最后的话

保持一致性。

如果你在编辑代码,花一点时间去其他代码看看它们的风格,和他们保持一致。