Google Python风格指南

       
八天,八百二十海里。法国首都、河南新乡、永清、霸州、文安、大城、柳州、孟村、盐山、四川庆云、无棣、营口、博兴、临淄、青州、昌乐、厦门、昌邑、平度、胶州、胶南、南京,多少个省二十两个县。

临别赠言

请务必保持代码的一致性

假使你正在编制代码,
花几分钟看一下科普代码,然后决定风格.即便它们在具备的算术操作符两边都采取空格,那么您也应有那样做.
倘若它们的评释都用标记包围起来, 那么您的笺注也要这样.

制订风格指南的意在让代码有规可循,这样人们就足以小心于”你在说怎么着”,而不是”你在怎么说”.我们在此处给出的是大局的业内,
不过本地的正规同样重要.假如你加到一个文件里的代码和原来代码黯然失神,它会让读者不知所措.制止这种情况.

        就这一句话,在自家脑子里来来回回晃荡了众多年。

空格

Tip
依照专业的排版规范来使用标点两边的空格

括号内不要有空格.

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]

在二元操作符两边都充裕一个空格, 比如赋值(=), 相比(==, <, >, !=,
<>, <=, >=, in, not in, is, is not), 布尔(and, or, not).
至于算术操作符两边的空格该怎么拔取, 需要你协调非凡判断.
然则两侧务必要维持一致.

Yes: x == 1

No:  x<1

当’=’用于指示关键字参数或默认参数值时, 不要在其两侧使用空格.

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,
         }

       
饮食,既要卫生,又要补充体力。在身上的包里时刻准备着牛肉干、巧克力和几样水果,在经过的地点吃饭,只挑麦当劳肯德基德克士等快餐连锁,除了卫生能得到保险,更重要的是这个快餐店一般都有知道的大窗户,把车停在外面可以看收获,不用顾虑小偷。

生成器

Tip
按需使用生成器.

定义:
所谓生成器函数, 就是每当它执行两回变动(yield)语句, 它就回来一个迭代器,
这多少个迭代器生成一个值. 生成值后, 生成器函数的运转意况将被挂起,
直到下一次生成.
优点:
简化代码, 因为每一遍调用时, 局部变量和控制流的图景都会被保存.
比起五遍创造一多样值的函数, 生成器使用的内存更少.
缺点:
没有.
结论:
鼓励利用. 注目的在于生成器函数的文档字符串中动用”Yields:”而不是”Returns:”.
(译者注: 参看
注释
)

       
本次难忘的阅历给了自我很大的自信,也给了自我无数的启示。其中的一条就是,一定要身先士卒地去品味一些政工,但在小的地点绝不冒险。

列表推导 List Comprehensions

Tip
可以在简约情形下利用

定义:
列表推导(list comprehensions)与生成器表明式(generator
expression)提供了一种精简高效的方法来创设列表和迭代器, 而不必借助map(),
filter(), 或者lambda.
优点:
简短的列表推导可以比此外的列表创立方法更加清晰简单.
生成器表明式可以非凡飞速, 因为它们制止了创设整个列表.
缺点:
复杂的列表推导或者生成器表明式可能麻烦阅读.
结论:
适用于简单意况. 每个部分应该单独置于一行: 映射表明式, for语句,
过滤器表明式. 禁止多重for语句或过滤器表明式. 复杂气象下仍旧利用循环.

Yes:
  result = []
  for x in range(10):
      for y in range(5):
          if x * y > 10:
              result.append((x, y))

  for x in xrange(5):
      for y in xrange(5):
          if x != y:
              for z in xrange(5):
                  if y != z:
                      yield (x, y, z)

  return ((x, complicated_transform(x))
          for x in long_generator_function(parameter)
          if x is not None)

  squares = [x * x for x in range(10)]

  eat(jelly_bean for jelly_bean in jelly_beans
      if jelly_bean.color == 'black')

No:
  result = [(x, y) for x in range(10) for y in range(5) if x * y > 10]

  return ((x, y, z)
          for x in xrange(5)
          for y in xrange(5)
          if x != y
          for z in xrange(5)
          if y != z)

       
应急状态,基本配备,头盔、手套一样不缺,感冒药、创可贴、雨伞应有尽有,为防范意外,还带了一把英吉沙小刀。

老式的言语特征

Tip
尽可能采用字符串方法取代字符串模块. 使用函数调用语法取代apply().
使用列表推导, for循环取代filter(),map()以及reduce().

定义:
现阶段版本的Python提供了我们平日更爱好的替代品.
结论:
我们不选用不帮忙这一个特征的Python版本, 所以没理由不用新的模式.

Yes: words = foo.split(':')

     [x[1] for x in my_list if x[2] == 5]

     map(math.sqrt, data)    # Ok. No inlined lambda expression.

     fn(*args, **kwargs)

No:  words = string.split(foo, ':')

     map(lambda x: x[1], filter(lambda x: x[2] == 5, my_list))

     apply(fn, args, kwargs)

       
意外状况也不是某些都未曾。路过安庆分界的时候,车扎胎了。我推了两公里找到一个加油站,向工作人士借用了一盆水,找到破洞用指导的简练工具补好,继续上路。

Tip
运用模块的任何径名来导入每个模块

优点:
制止模块名顶牛. 查找包更容易.
缺点:
安排代码变难, 因为你必须复制包层次.
结论:
持有的新代码都应有用全体包名来导入每个模块.

应该像下边这样导入:

# Reference in code with complete name.
import sound.effects.echo

# Reference in code with just module name (preferred).
from sound.effects import echo

       
但说到底,坚韧不拔下来了。没有患病、没有受伤、没有丢车、也未曾超越原先的预算。

空行

Tip
五星级定义之间空两行, 方法定义之间空一行

一品定义之间空两行, 比如函数或者类定义. 方法定义,
类定义与第一个艺术之间, 都应该空一行. 函数或措施中,
某些地方假如你认为适当, 就空一行.

       
算一算在温馨早就渡过三十年的人生经验里,要说疯狂,要说冒险,这是头一件。用情侣们的话来说,就是足以拿来吹一辈子牛。

字符串

Tip
就是参数都是字符串, 使用%操作符或者格式化方法格式化字符串.
不过也不可能同仁一视, 你需要在+和%里面可以判定.

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)

制止在循环中用+和+=操作符来累加字符串. 由于字符串是不可变的,
这样做会创立不必要的暂时对象, 并且导致二次方而不是线性的运转时间.
作为代表方案, 你可以将每个子串插足列表, 然后在循环截止后用 .join
连接列表. (也得以将各种子串写入一个 cStringIO.StringIO 缓存中.)

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

在同一个文书中, 保持利用字符串引号的平等性.
使用单引号’或者双引号”之一用以引用字符串, 并在同等文件中沿用.
在字符串内得以动用其余一种引号, 以防止在字符串中使用.
GPyLint已经插手了这一检查.

(译者注:GPyLint疑为笔误, 应为PyLint.)

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

为多行字符串使用三重双引号”“”而非三重单引号’‘’.
当且仅当项目中使用单引号’来引用字符串时,
才可能会动用三重’‘’为非文档字符串的多行字符串来标识引用.
文档字符串必须使用三重双引号”“”. 但是要留意, 平时用隐式行连接更清晰,
因为多行字符串与程序其他一些的缩进形式不一致.

Yes:
    print ("This is much nicer.\n"
           "Do it this way.\n")

No:
      print """This is pretty ugly.
  Don't do this.
  """

       
大事大胆。人生本来就是一场历险,没有怎么指望是不可能落实的,没有什么苦难是不可以制服的,勇敢地去品尝,坚强地去面对。

默认迭代器和操作符

Tip
要是类型协助, 就使用默认迭代器和操作符. 比如列表, 字典及文件等.

定义:
容器类型, 像字典和列表, 定义了默认的迭代器和关系测试操作符(in和not in)
优点:
默认操作符和迭代器简单疾速, 它们一直表述了操作, 没有额外的措施调用.
使用默认操作符的函数是通用的. 它能够用于匡助该操作的此外类型.
缺点:
你没法通过翻阅情势名来区别对象的门类(例如, has_key()意味着字典).
然则这也是优点.
结论:
万一类型辅助, 就应用默认迭代器和操作符, 例如列表, 字典和文件.
内建类型也定义了迭代器方法. 优先考虑这么些主意, 而不是这么些再次回到列表的方法.
当然,这样遍历容器时,你将无法修改容器.

Yes:  for key in adict: ...
      if key not in adict: ...
      if obj in alist: ...
      for line in afile: ...
      for k, v in dict.iteritems(): ...

No:   for key in adict.keys(): ...
      if not adict.has_key(key): ...
      for line in afile.readlines(): ...

       
直到自己自己买了山地车和各类骑行装备,在香港郊区拉练了大半年,将这八百海里的计划排上日程的时候,这句话成了我的行动指南。

Lambda函数

Tip
适用于单行函数

定义:
与话语相反, lambda在一个表达式中定义匿名函数. 常用来为 map()
filter() 之类的高阶函数定义回调函数或者操作符.
优点:
方便.
缺点:
比当地函数更难阅读和调试. 没有函数名代表堆栈跟踪更难精晓.
由于lambda函数平日只含有一个表明式, 因而其表达能力有限.
结论:
适用于单行函数. 如若代码超越60-80个字符, 最好或者定义成常规(嵌套)函数.

对于普遍的操作符,例如乘法操作符,使用 operator
模块中的函数以替代lambda函数. 例如, 推荐应用 operator.mul , 而不是
lambda x, y: x * y .

       
前边规划路线,我在网上搜罗了装有能找到的攻略、游记。打开地图,沿着国道和省道一英里一海里地测量,在各种路过的镇子、村庄、岔路口画上标识。为了防备迷路,我专门换了一部屏幕大的智能手机,提前下载好首都、广东、江西有所途经地区的领航地图。

属性 properties

Tip
访问和装置数据成员时, 你通常会采纳简便, 轻量级的访问和设置函数.
提议用属性(properties)来代表它们.

定义:
一种用于包装情势调用的格局. 当运算量不大,
它是取得和安装属性(attribute)的正规模式.
优点:
因此免去简单的性能(attribute)访问时显式的get和set方法调用, 可读性进步了.
允许懒惰的统计. 用Pythonic的章程来维护类的接口. 就性能而言,
当直接访问变量是在理的, 添加访问方法就呈现琐碎而无心义.
使用性质(properties)可以绕过这一个问题.
未来也足以在不破坏接口的事态下将造访方法加上.
缺点:
特性(properties)是在get和set方法讲明后指定,
这亟需使用者在接下去的代码中注意:
set和get是用于属性(properties)的(除了用 @property
装饰器创制的只读属性). 必须继续自object类.
可能藏匿比如操作符重载之类的副功效. 继承时可能会令人困惑.
结论:
您平时习惯于选用访问或安装方法来拜访或设置数据, 它们简单而轻量.
不过我们指出你在新的代码中动用属性. 只读属性应该用 @property 装饰器
来创建.

假定子类没有覆盖属性, 那么属性的继续可能看起来不分明.
因而使用者必须保证走访方法直接被调用,
以保证子类中的重载方法被属性调用(使用模板方法设计情势).

Yes: import math

     class Square(object):
         """A square with two properties: a writable area and a read-only perimeter.

         To use:
         >>> sq = Square(3)
         >>> sq.area
         9
         >>> sq.perimeter
         12
         >>> sq.area = 16
         >>> sq.side
         4
         >>> sq.perimeter
         16
         """

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

         def __get_area(self):
             """Calculates the 'area' property."""
             return self.side ** 2

         def ___get_area(self):
             """Indirect accessor for 'area' property."""
             return self.__get_area()

         def __set_area(self, area):
             """Sets the 'area' property."""
             self.side = math.sqrt(area)

         def ___set_area(self, area):
             """Indirect setter for 'area' property."""
             self._SetArea(area)

         area = property(___get_area, ___set_area,
                         doc="""Gets or sets the area of the square.""")

         @property
         def perimeter(self):
             return self.side * 4

(译者注: 老实说, 我以为这段示例代码很不适宜, 有必要这么蛋疼吗?)

       
在临淄,因为暂时起意要去寻访古齐国的遗迹,在千头万绪的小村小路上迷失了样子,又赶上了一场大雨,惨不忍睹。

导入格式

Tip
各种导入应该占据一行

Yes: import os
     import sys

No:  import os, sys

导入总应该放在文件顶部, 位于模块注释和文档字符串之后,
模块全局变量和常量往日. 导入应该服从从最通用到最不通用的依次分组:

  1. 标准库导入
  2. 其三方库导入
  3. 应用程序指定导入

每种分组中, 应该依照每个模块的完整包路径按字典序排序, 忽略大小写.

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

       
回头想想,这个动机最早萌芽于二〇〇七年。那一年,我大三,在五遍高校协会的聚首上,认识了一个陕西同学,该君刚刚花四天时间完成了四遍从法国巴黎到拉巴斯的骑行。好四人围着,听她绘声绘色地讲旅途中的故事。

Python语言专业

       
你可以去见义勇为地去创业,不过认真评估协调的绝艺,盘点自己的资源,找出事业的风险点,做好最坏情状下的预案。

文件和sockets

Tip
在文书和sockets结束时, 显式的关门它.

除文件外, sockets或另外类似文件的靶子在尚未必要的意况下开辟,
会有成千上万副功用, 例如:

  1. 它们可能会耗费一定量的系统资源,如文件讲述符.如若这个资源在利用后并未当即归还系统,那么用于拍卖这多少个目标的代码会将资源消耗殆尽.
  2. 所有文件将会堵住对于文本的别样诸如移动、删除之类的操作.
  3. 唯有是从逻辑上关闭文件和sockets,那么它们如故可能会被其共享的顺序在潜意识中开展读或者写操作.只有当它们确实被关门后,对于它们尝试举行读或者写操作将会跑出卓殊,并使得问题很快显现出来.

再就是,幻想当文件对象析构时,文件和sockets会自动关闭,试图将文件对象的生命周期和文书的情事绑定在一齐的想法,都是不现实的.
因为有如下原因:

  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

Legacy AppEngine 中Python 2.5的代码如使用”with”语句, 需要添加
“from __future__ import with_statement”.

       
小处小心。小心并不可以驶得万年船,但足以让您翻船的几率降到最低。踏踏实实地计划协调的事业、爱情和人生,于平日中拿走幸福和震撼。

分号

Tip
无须在行尾加分号, 也并非用分号将两条命令放在同等行.

       
任什么人都得以像本人同一去长途骑行,或者可以去爬山,去跑马拉松,去一个陌生危险的位置,去做其余一件冒险的事务,但当你真正地去做的时候,收集所有能找到的音信,关注每一个分寸的环节,把发生风险的可能性降到最低。

语句

Tip
普通每个语句应该占据一行

但是, 借使测试结果与测试语句在一行放得下, 你也足以将它们放在同等行.
尽管是if语句, 唯有在未曾else时才能这样做. 特别地, 绝不要对 try/except
这样做, 因为try和except不可能放在同样行.

Yes:

  if foo: bar(foo)

No:

  if foo: bar(foo)
  else:   baz(foo)

  try:               bar(foo)
  except ValueError: baz(foo)

  try:
      bar(foo)
  except ValueError: baz(foo)

        骑单车,一个人,从迪拜到南京。

pylint

Tip
对您的代码运行pylint

定义:
pylint是一个在Python源代码中查找bug的工具.
对于C和C++这样的不那么动态的(译者注: 原文是less dynamic)语言,
这一个bug通常由编译器来捕获. 由于Python的动态特性, 有些警告或者不对.
不过伪告警应该很少.
优点:
可以捕获容易忽视的谬误, 例如输入错误, 使用未赋值的变量等.
缺点:
pylint不完美. 要动用其优势, 我们有时侯需要: a) 围绕着它来写代码 b)
抑制其报警 c) 革新它, 或者d) 忽略它.
结论:
确保对您的代码运行pylint.抑制不可靠的警告,以便可以将其他警告透显露来。
您可以由此设置一个行注释来避免告警. 例如:

dict = 'something awful'  # Bad Idea... pylint: disable=redefined-builtin

pylint警告是以一个数字编号(如 C0112 )和一个标志名(如 empty-docstring
)来标识的. 在编制新代码或更新已有代码时对报警举办临床,
推荐应用标志名来标识.

假若警告的标记名不够见名知意,那么请对其扩大一个详尽分解。

使用这种抑制模式的裨益是我们得以轻松查找抑制并记忆它们.

您能够采纳命令 pylint --list-msgs 来获取pylint告警列表. 你能够行使命令
pylint --help-msg=C6409 , 以得到有关特定消息的更多音讯.

比较于事先运用的 pylint: disable-msg , 本文推荐使用
pylint: disable .

要遏制”参数未采纳”告警, 你能够用””作为参数标识符,
或者在参数名前加”unused
”. 碰着不可能改变参数名的状态,
你可以由此在函数开始”提到”它们来清除告警. 例如:

    def foo(a, unused_b, unused_c, d=None, e=None):
        _ = d, e
        return a

        二〇一一年的不行冬季,我干了一件大事。

背景

Python 是
Google紧要的脚本语言。这本风格指南主要含有的是本着python的编程准则。
为支援读者可以将代码准确格式化,大家提供了针对 Vim的配置文件
。对于Emacs用户,保持默认设置即可。

       然后,我把以前总认为不能的想法兑现了。

威力过大的风味

Tip
避免采取这么些特色

定义:
Python是一种分外灵活的言语, 它为您提供了无数鲜艳的风味,
诸如元类(metaclasses), 字节码访问, 任意编译(on-the-fly compilation),
动态继承, 对象父类重定义(object reparenting), 导入黑客(import hacks),
反射, 系统内修改(modification of system internals), 等等.
优点:
有力的语言特征, 能让您的代码更紧凑.
缺点:
动用这么些很”酷”的特点万分诱人, 但不是相对必要.
使用奇技淫巧的代码将尤为难以阅读和调试. 开首容许还好(对原作者而言),
但当你回顾代码, 它们可能会比那一个稍长一点不过很直接的代码更加难以领悟.
结论:
在您的代码中防止这个特性.

       
最后三天,体力起先不支,原先规划的每天100海里不可以形成。只可以拖延了一天。骑到最终,感觉温馨已经不会走路了。

TODO注释

Tip
为临时代码应用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是”将来做某事”的样式,
那么请保管您包含了一个点名的日子(“二零零六年十二月缓解”)或者一个一定的风波(“等到持有的客户都得以拍卖XML请求就移除这个代码”).

        提前多少个月一样样准备,就为了这几天。

Tip
一旦一个类不继续自此外类, 就显式的从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

继承自 object 是为了使属性(properties)正常办事,
并且这样可以维护你的代码, 使其不受Python
3000的一个奇特的机密不兼容性影响. 这样做也定义了一些与众不同的方法,
这一个艺术实现了目标的默认语义, 包括
__new__, __init__, __delattr__, __getattribute__, __setattr__, __hash__, __repr__, and __str__
.

       
“我从没在这种小事上铤而走险。”所以,我成功了一件看似无法的大冒险。

Python风格规范

       
住宿,因为骑行的不二法门上大都是环亚得里亚海一线人口稠密的大中城市,没有荒山野岭,就每隔100千米左右的离开,挑选几家飞快酒店。人身安全是率先位,还会跟宾馆认同能否把车推到大堂或客房里。

Main

Tip
不怕是一个打算被当作脚本的文书,也相应是可导入的.并且简单的导入不应有导致这么些本子的主功效(mainfunctionality)被实施,
这是一种副成效. 主效能应该放在一个main()函数中.

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

def main():
      ...

if __name__ == '__main__':
    main()

装有的一等代码在模块导入时都会被执行. 要小心不要去调用函数,
创制对象或者举办这么些不应有在采用pydoc时实施的操作.

        这位同学向听众们总结说:“我尚未在那种小事上孤注一掷。”

默认参数值

Tip
适用于多数情状.

定义:
你能够在函数参数列表的末尾指定变量的值, 例如, def foo(a, b = 0): .
如若调用foo时只带一个参数, 则b被设为0. 比方带几个参数,
则b的值等于第二个参数.
优点:
您时常会遇上一些使用大量默认值的函数,
但偶尔(相比较少见)你想要覆盖这些默认值.
默认参数值提供了一种简单的法子来完成这件事,
你不需要为这一个罕见的不比定义大量函数. 同时,
Python也不帮忙重载方法和函数, 默认参数是一种”仿造”重载行为的粗略格局.
缺点:
默认参数只在模块加载时求值三次. 如果参数是列表或字典之类的可变类型,
这也许会造成问题. 就算函数修改了目的(例如向列表追加项),
默认值就被涂改了.
结论:
勉励选拔, 然则有如下注意事项:

不用在函数或方法定义中行使可变对象作为默认值.

Yes: def foo(a, b=None):
         if b is None:
             b = []

No:  def foo(a, b=[]):
         ...
No:  def foo(a, b=time.time()):  # The time the module was loaded???
         ...
No:  def foo(a, b=FLAGS.my_thing):  # sys.argv has not yet been parsed...
         ...

       
这么多年过去,这位同学的音容笑貌都模糊了,唯有一个细节记得清清楚楚。他讲到自己长日子骑车特别没劲,只可以戴上耳麦听音乐,但又怕因为听不到中途汽车喇叭响出事故,只带一侧的耳麦,永远留着一只耳朵收听马路的声响。

异常

Tip
同意接纳相当, 但必须小心

定义:
相当是一种跳出代码块的正常控制流来处理错误或者其他分外条件的形式.
优点:
好端端操作代码的控制流不会和错误处理代码混在一起. 当某种条件发出时,
它也允许控制流跳过三个框架. 例如, 一步跳出N个嵌套的函数,
而不必继续执行错误的代码.
缺点:
恐怕会导致令人纳闷的操纵流. 调用库时便于失去错误情况.
结论:
老大必须坚守特定条件:

  1. 像这样触发非常: raise MyException("Error message") 或者
    raise MyException . 不要使用多个参数的样式(
    raise MyException, "Error message" )或者过时的字符串十分(
    raise "Error message" ).

  2. 模块或包应该定义自己的特定域的不得了基类,
    那么些基类应该从内建的Exception类继承. 模块的相当基类应该称为”Error”.

    class Error(Exception):
        pass
  1. 永恒不要拔取 except: 语句来捕获所有特别, 也无须捕获 Exception
    或者 StandardError , 除非你打算重新触发该特别,
    或者您早已在此时此刻线程的最外层(记得仍旧要打印一条错误音讯).
    在老大这方面, Python相当宽容, except:
    真的会捕获包括Python语法错误在内的此外错误. 使用 except:
    很容易隐藏真正的bug.

  2. 尽量缩短try/except块中的代码量. try块的体积越大,
    期望之外的百般就越容易被触发. 这种情状下,
    try/except块将藏匿真正的错误.

  3. 运用finally子句来举行这多少个无论try块中有没有充裕都应有被实践的代码.
    这对于清理资源经常很有用, 例如关闭文件.
    当捕获分外时, 使用 as 而并非用逗号. 例如

try:
    raise Error
except Error as error:
    pass

嵌套 局部 内部类或函数

Tip
勉励使用嵌套/本地/内部类或函数

定义:
类可以定义在艺术, 函数或者类中. 函数可以定义在点子或函数中.
封闭区间中定义的变量对嵌套函数是只读的.
优点:
允许定义仅用于有效限制的工具类和函数.
缺点:
嵌套类或局项目标实例不可能体系化(pickled).
结论:
推荐使用.

访问控制

Tip
在Python中,
对于琐碎又不太重要的访问函数,你应有一向动用国有变量来替代它们,这样可以防止额外的函数调用开销.当添加更多效益时,
你可以用属性(property)来维系语法的同一性.

(译者注: 重视封装的面向对象程序员看到这些也许会很反感,
因为她俩直白被指引: 所有成员变量都必须是私房的! 其实,
这真的是有点麻烦啊.试着去领受Pythonic军事学吧)

单向, 假使访问更复杂, 或者变量的拜会开销很醒目, 那么您应该使用像
get_foo()set_foo() 这样的函数调用.
即便往日的代码行为容许通过性能(property)访问 ,
那么就不要将新的走访函数与特性绑定. 这样,
任何试图透过老艺术访问变量的代码就无奈运行,
使用者也就会发觉到复杂暴发了变化.

函数与艺术装饰器

Tip
假诺好处很醒目, 就明智而严俊的应用装饰器

定义:
用于函数及艺术的装饰器
(也就是@标记). 最普遍的装饰器是@classmethod 和@staticmethod,
用于将常规函数转换成类方法或静态方法. 可是,
装饰器语法也允许用户自定义装饰器. 特别地, 对于某个函数 my_decorator ,
上边的两段代码是一样的:

class C(object):
   @my_decorator
   def method(self):
       # method body ...
class C(object):
    def method(self):
        # method body ...
    method = my_decorator(method)

优点:
淡雅的在函数上点名一些转换. 该转换可能回落一些再一次代码,
保持已有函数不变(enforce invariants), 等.
缺点:
装饰器可以在函数的参数或重回值上实施此外操作,
这也许导致让人惊愕的隐藏行为. 而且, 装饰器在导入时执行.
从装饰器代码的挫败中回复更加无法.
结论:
只要好处很领会, 就明智而谨慎的利用装饰器.
装饰器应该遵从和函数一样的导入和命名规则.
装饰器的python文档应该明晰的表达该函数是一个装饰器.
请为装饰器编写单元测试.

防止装饰器自身对外场的倚重性(即不用借助于文件, socket, 数据库连接等),
因为装饰器运行时这多少个资源可能不可用(由 pydoc 或其余工具导入).
应该保证一个用卓有效用参数调用的装饰器在拥有意况下都是马到成功的.

装饰器是一种相当形式的”超级代码”. 参考后边关于 Main 的话题.

缩进

Tip
用4个空格来缩进代码

相对不用用tab, 也不用tab和空格混用. 对于行连接的意况,
你应当仍旧垂直对齐换行的要素(见 行长度 部分的演示),
或者应用4空格的悬挂式缩进(这时第一行不应有有参数):

Yes:   # Aligned with opening delimiter
       foo = long_function_name(var_one, var_two,
                                var_three, var_four)

       # Aligned with opening delimiter in a dictionary
       foo = {
           long_dictionary_key: value1 +
                                value2,
           ...
       }

       # 4-space hanging indent; nothing on first line
       foo = long_function_name(
           var_one, var_two, var_three,
           var_four)

       # 4-space hanging indent in a dictionary
       foo = {
           long_dictionary_key:
               long_dictionary_value,
           ...
       }

No:    # Stuff on first line forbidden
      foo = long_function_name(var_one, var_two,
          var_three, var_four)

      # 2-space hanging indent forbidden
      foo = long_function_name(
        var_one, var_two, var_three,
        var_four)

      # No hanging indent in a dictionary
      foo = {
          long_dictionary_key:
              long_dictionary_value,
              ...
      }

词法效能域 Lexical Scoping

Tip

推介应用

定义:
嵌套的Python函数可以引用外层函数中定义的变量, 然则不可知对它们赋值.
变量绑定的剖析是接纳词法效率域, 也就是依据静态的次第文本.
对一个块中的某个名称的其它赋值都会导致Python将对该名称的整个引用当做局部变量,
甚至是赋值前的处理. 假诺遇上global阐明, 该名称就会被作为全局变量.

一个施用那个特性的例子:

def get_adder(summand1):
    """Returns a function that adds numbers to a given number."""
    def adder(summand2):
        return summand1 + summand2

    return adder

(译者注: 这些例子有点古怪, 你应当这么使用那个函数:
sum = get_adder(summand1)(summand2) )
优点:
常见可以带来更为清晰, 优雅的代码.
尤其会让有经验的Lisp和Scheme(还有Haskell, ML等)程序员感到欣慰.
缺点:
唯恐造成令人迷惑的bug. 例如下面这个按照
PEP-0227
的例子:

i = 4
def foo(x):
    def bar():
        print i,
    # ...
    # A bunch of code here
    # ...
    for i in x:  # Ah, i *is* local to Foo, so this is what Bar sees
        print i,
    bar()

因此 foo([1, 2, 3]) 会打印 1 2 3 3 , 不是 1 2 3 4 .

(译者注: x是一个列表,
for循环其实是将x中的值依次赋给i.这样对i的赋值就隐式的发出了,
整个foo函数体中的i都会被用作局部变量, 包括bar()中的这个.
这或多或少与C++之类的静态语言仍旧有很大差异的.)
结论:
勉励使用.

线程

Tip
不要借助内建档次的原子性.

虽说Python的内建品种例如字典看上去拥有原子操作,
可是在某些情状下它们依旧不是原子的(即:
如果__hash____eq__被实现为Python方法)且它们的原子性是靠不住的.
你也无法仰望原子变量赋值(因为这些反过来依赖字典).

先行利用Queue模块的 Queue 数据类型作为线程间的数量通信情势. 此外,
使用threading模块及其锁原语(locking primitives).
精晓条件变量的方便使用方法, 这样你就可以运用 threading.Condition
来取代低级其它锁了.

规格表明式

Tip
适用于单行函数

定义:
规则表明式是对于if语句的一种更加简易的句法规则. 例如:
x = 1 if cond else 2 .
优点:
比if语句更加简明和方便.
缺点:
比if语句难于阅读. 假若表明式很长, 难于固定条件.
结论:
适用于单行函数. 在其余意况下,推荐应用完整的if语句.

注释

Tip
担保对模块, 函数, 方法和行内注释使用科学的风骨 文档字符串

Python有一种独一无二的的注释情势: 使用文档字符串. 文档字符串是包, 模块,
类或函数里的第一个语句. 这一个字符串可以透过对象的doc成员被机关提取,
并且被pydoc所用. (你可以在您的模块上运行pydoc试一把, 看看它长什么).
大家对文档字符串的规矩是运用三重双引号”“”(
PEP-257
). 一个文档字符串应该如此社团: 首先是单排以句号,
问号或惊讶号结尾的概述(或者该文档字符串单纯只有一行). 接着是一个空行.
接着是文档字符串剩下的有些, 它应该与文档字符串的首先行的首先个引号对齐.
底下有更多文档字符串的格式化规范.
模块

各样文件应该包含一个许可样板. 遵照项目拔取的准许(例如, Apache 2.0, BSD,
LGPL, GPL), 选拔合适的样板.
函数和章程

下文所指的函数,包括函数, 方法, 以及生成器.

一个函数必须要有文档字符串, 除非它满意以下标准:

  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

类应该在其定义下有一个用以描述该类的文档字符串.
假如您的类有集体性质(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."""

块注释和行注释

最急需写注释的是代码中这些技巧性的局部. 假若您在下次
代码审查
的时候必须解释一下, 那么你应当现在就给它写注释. 对于复杂的操作,
应该在其操作起来前写上多少行注释. 对于不是侦破的代码,
应在其行尾添加注释.

# 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

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

一面, 永不要描述代码. 倘若阅读代码的人比你更懂Python,
他只是不亮堂你的代码要做什么.

# BAD COMMENT: Now go through the b array and make sure whenever i occurs
# the next element is i+1

行长度

Tip
每行不抢先80个字符

例外:

  1. 长的导入模块语句
  2. 诠释里的URL

永不接纳反斜杠连接行.

Python会将 圆括号,
中括号和花括号中的行隐式的连接起来

, 你可以使用这么些特点. 即便需要, 你能够在表明式外围扩展一对额外的圆括号.

Yes: foo_bar(self, width, height, color='black', design=None, x='foo',
             emphasis=None, highlight=0)

     if (width == 0 and height == 0 and
         color == 'red' and emphasis == 'strong'):

假诺一个文本字符串在一行放不下, 能够行使圆括号来落实隐式行连接:

x = ('This will build a very long long '
     'long long long long long long string')

在诠释中,假设必要,将长的URL放在一行上。

Yes:  # See details at
      # http://www.example.com/us/developer/documentation/api/content/v2.0/csv_file_name_extension_full_specification.html

No:  # See details at
     # http://www.example.com/us/developer/documentation/api/content/\
     # v2.0/csv_file_name_extension_full_specification.html

留意下面例子中的元素缩进; 你可以在本文的 缩进 部分找到解释.

参考:

括号

Tip
宁缺毋滥的拔取括号

唯有是用以落进行连接, 否则不用在回去语句或标准语句中使用括号.
不过在元组两边使用括号是可以的.

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)

Python 解析器

Tip
大部分.py文件不必以#!作为文件的先河. 依照
PEP-394
, 程序的main文件应当以#!/usr/bin/python2或者 #!/usr/bin/python3开始.

(译者注: 在处理器科学中,
Shebang
(也称为Hashbang)是一个由井号和叹号构成的字符串行(#!),
其出现在文书文件的率先行的前六个字符. 在文件中留存Shebang的情事下,
类Unix操作系统的主次载入器会分析Shebang后的内容,
将这个内容作为解释器指令, 并调用该指令,
并将载有Shebang的文本路径作为该解释器的参数. 例如,
以指令#!/bin/sh最先的文件在实施时会实际调用/bin/sh程序.)
#!先用于扶持内核找到Python解释器, 可是在导入模块时, 将会被忽略.
因而唯有被一贯实施的文书中才有必不可少参预#!.

全局变量

Tip
避免全局变量

定义:
概念在模块级的变量.
优点:
偶尔有用.
缺点:
导入时或许更改模块行为, 因为导入模块时会对模块级变量赋值.
结论:
防止使用全局变量, 用类变量来代替. 但也有一对不一:

  1. 剧本的默认选项.
  2. 模块级常量. 例如: PI = 3.14159. 常量应该全大写, 用下划线连接.
  3. 偶然用全局变量来缓存值或者当作函数重返值很有用.
  4. 一旦急需, 全局变量应该仅在模块内部可用,
    并通过模块级的共用函数来访问.

True or False的求值

Tip
尽量使用隐式false

定义:
Python在布尔上下文中会将一些值求值为false. 按简单的直觉来讲,
就是具备的”空”值都被认为是false. 因而0, None, [], {}, “”
都被认为是false.
优点:
利用Python布尔值的规范语句更易读也更不错犯错. 大部分气象下, 也更快.
缺点:
对C/C++开发人员来说, 可能看起来有点怪.
结论:
尽可能使用隐式的false, 例如: 使用 if foo: 而不是 if foo != []: .
可是还是有部分注意事项需要你难以忘怀:

  1. 世世代代不要用==或者!=来相比较单件, 比如None. 使用is或者is not.

  2. 只顾: 当你写下 if x: 时, 你实在表示的是 if x is not None . 例如:
    当你要测试一个默认值是None的变量或参数是否被设为另外值.
    这一个值在布尔语义下可能是false!

  3. 世世代代不要用==将一个布尔量与false相比. 使用 if not x: 代替.
    假如你需要区分false和None, 你应该用像 if not x and x is not None:
    这样的语句.

  4. 对于系列(字符串, 列表, 元组), 要注意空系列是false. 因而
    if not seq: 或者 if seq:if len(seq):if not len(seq):
    要更好.

  5. 拍卖整数时, 使用隐式false可能会得不偿失(即不小心将None当做0来处理).
    你可以将一个已知是整型(且不是len()的回来结果)的值与0相比.

Yes: if not users:
         print 'no users'

     if foo == 0:
         self.handle_zero()

     if i % 10 == 0:
         self.handle_multiple_of_ten()


No:  if len(users) == 0:
         print 'no users'

     if foo is not None and not foo:
         self.handle_zero()

     if not i % 10:
         self.handle_multiple_of_ten()
  1. 只顾‘0’(字符串)会被当做true.

命名

Tip
module_name, package_name, ClassName, method_name,
ExceptionName,function_name, GLOBAL_VAR_NAME, instance_var_name,
function_parameter_name, local_var_name.

应当制止的称谓

  1. 单字符名称, 除了计数器和迭代器.
  2. 包/模块名中的连字符(-)
  3. 双下划线先河并最后的称呼(Python保留, 例如init)

取名约定

  1. 所谓”内部(Internal)”表示仅模块内可用, 或者, 在类内是维护或个体的.
  2. 用单下划线(_)起头表示模块变量或函数是protected的(使用import *
    from时不会蕴藏).
  3. 用双下划线(__)初步的实例变量或情势表示类内私有.
  4. 将有关的类和一等函数放在同一个模块里. 不像Java,
    没必要限制一个类一个模块.
  5. 对类名使用大写字母起首的单词(如CapWords,即Pascal风格),不过模块名应当用小写加下划线的情势(如lower_with_under.py).
    即便已经有很多现存的模块使用类似于CapWords.py这样的命名,但近期早就不鼓励这样做,因为一旦模块名刚刚和类一致,
    这会令人困扰.
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

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

导入

Tip
仅对包和模块使用导入

定义:
模块间共享代码的录用机制.
优点:
命名空间管理约定分外简单. 每个标识符的源都用一种同等的主意提醒.
x.Obj表示Obj对象定义在模块x中.
缺点:
模块名仍可能争论. 有些模块名太长, 不太方便.
结论:
使用 import x 来导入包和模块.

使用 from x import y , 其中x是包前缀, y是不带前缀的模块名.

使用 from x import y as z, 假使六个要导入的模块都叫做z或者y太长了.

例如, 模块 sound.effects.echo 可以用如下格局导入:

    from sound.effects import echo
    ...
    echo.EchoFilter(input, output, delay=0.7, atten=4)

导入时不用接纳相对名称. 即便模块在同一个包中, 也要接纳完整包名.
这能帮助你避免无意间导入一个包五回.

  • Python是一种对代码风格很依赖的言语,从缩进就能见到这点,Python强调易于理解。目前在担负代码重构的办事,为了统一我们的代码风格,制订规范,学习了一下网上这份Google的Python风格指南。

  • 原稿地址:
    http://google-styleguide.googlecode.com/svn/trunk/pyguide.html