Python风格指南

PEP 8风格指南

PEP是Python Enhancement Proposal的缩写,一般翻译为“Python加强提案”。每一个PEP都是一份为Python社区提供的指导Python往更好的方向发展的技术文档,其中的第8号加强提案(PEP 8)是针对Python语言编订的代码风格指南。尽管咱们能够在保证语法没有问题的前提下随意书写Python代码,可是在实际开发中,采用一致的风格书写出可读性强的代码是每一个专业的程序员应该作到的事情,也是每一个公司的编程规范中会提出的要求,这些在多人协做开发一个项目(团队开发)的时候显得尤其重要。咱们能够从Python官方网站的PEP 8连接中找到该文档,下面咱们对该文档的关键部分作一个简单的总结。html

分号

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

行长度

每行不超过80个字符。git

例外:程序员

  • 长的导入模块语句
  • 注释里的URL
  • 不要使用反斜杠链接行

Python会将圆括号,中括号花括号中的行隐式的链接起来 , 你能够利用这个特色. 若是须要, 你能够在表达式外围增长一对额外的圆括号。github

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

括号

宁缺毋滥的使用括号。
除非是用于实现行链接, 不然不要在返回语句或条件语句中使用括号。 不过在元组两边使用括号是能够的。api

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)

空格的使用

  • 使用空格来表示缩进而不要用制表符(Tab)。这一点对习惯了其余编程语言的人来讲简直以为不可理喻,由于绝大多数的程序员都会用Tab来表示缩进,可是要知道Python并无像C/C++或Java那样的用花括号来构造一个代码块的语法,在Python中分支和循环结构都使用缩进来表示哪些代码属于同一个级别,鉴于此Python代码对缩进以及缩进宽度的依赖比其余不少语言都强得多。在不一样的编辑器中,Tab的宽度多是二、4或8个字符,甚至是其余更离谱的值,用Tab来表示缩进对Python代码来讲多是一场灾难。
  • 和语法相关的每一层缩进都用4个空格来表示。
  • 除了首行以外的其他各行都应该在正常的缩进宽度上再加上4个空格。
  • 函数和类的定义,代码先后都要用两个空行进行分隔。
  • 在同一个类中,各个方法之间应该用一个空行进行分隔。
  • 二元运算符的左右两侧应该保留一个空格,并且只要一个空格就好。

标识符命名

PEP 8倡导用不一样的命名风格来命名Python中不一样的标识符,以便在阅读代码时可以经过标识符的名称来肯定该标识符在Python中扮演了怎样的角色(在这一点上,Python本身的内置模块以及某些第三方模块都作得并非很好)。app

  • 变量、函数和属性应该使用小写字母来拼写,若是有多个单词就使用下划线进行链接。
  • 类中受保护的实例属性,应该以一个下划线开头。
  • 类中私有的实例属性,应该以两个下划线开头。
  • 类和异常的命名,应该每一个单词首字母大写。
  • 模块级别的常量,应该采用全大写字母,若是有多个单词就用下划线进行链接。
  • 类的实例方法,应该把第一个参数命名为self以表示对象自身。
  • 类的类方法,应该把第一个参数命名为cls以表示该类自身。

表达式和语句

在Python之禅(可使用import this查看)中有这么一句名言:“There should be one-- and preferably only one --obvious way to do it.”,翻译成中文是“作一件事应该有并且最好只有一种确切的作法”,这句话传达的思想在PEP 8中也是无处不在的。编程语言

  • 采用内联形式的否认词,而不要把否认词放在整个表达式的前面。例如if a is not b就比if not a is b更容易让人理解。
  • 不要用检查长度的方式来判断字符串、列表等是否为None或者没有元素,应该用if not x这样的写法来检查它。
  • 就算if分支、for循环、except异常捕获等中只有一行代码,也不要将代码和if、for、except等写在一块儿,分开写才会让代码更清晰。
  • import语句老是放在文件开头的地方。
  • 引入模块的时候,from math import sqrt比import math更好。
  • 若是有多个import语句,应该将其分为三部分,从上到下分别是Python标准模块、第三方模块和自定义模块,每一个部份内部应该按照模块名称的字母表顺序来排列。

注释

确保对模块, 函数, 方法和行内注释使用正确的风格。

块注释和行注释

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

# 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, 他只是不知道你的代码要作什么。

文档字符串

Python有一种独一无二的的注释方式: 使用文档字符串。

文档字符串是包, 模块,类或函数里的第一个语句。
这些字符串能够经过对象的__doc__成员被自动提取, 而且被pydoc所用。(你能够在你的模块上运行pydoc试一把, 看看它长什么样)。
咱们对文档字符串的惯例是使用三重双引号"""( PEP-257 )。

一个文档字符串应该这样组织:

  • 首先是一行以句号,问号或惊叹号结尾的概述(或者该文档字符串单纯只有一行)。
  • 接着是一个空行。
  • 接着是文档字符串剩下的部分,它应该与文档字符串的第一行的第一个引号对齐。
函数文档字符串

一个函数必需要有文档字符串,除非它知足如下条件:

  • 外部不可见
  • 很是短小
  • 简单明了

文档字符串应该包含函数作什么以及输入和输出的详细描述。一般不该该描述“怎么作”,除非是一些复杂的算法。

文档字符串应该提供足够的信息,当别人编写代码调用该函数的时候,它不须要看一行代码,只要看文档字符串就够了。对于复杂的代码,在代码旁边加注释会比文档字符串更有意义。
函数的文档字符串格式,将函数按照参数、返回值、抛出异常等信息分小节进行描述,每小节应该以一个标题行开始,标题行以冒号结尾,除标题行外, 节的其余内容应被缩进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."""

惯例

“惯例”这个词指的是“习惯的作法,常规的办法,一向的作法”,与这个词对应的英文单词叫“idiom”。因为Python跟其余不少编程语言在语法和使用上仍是有比较显著的差异,所以做为一个Python开发者若是不能掌握这些惯例,就没法写出“Pythonic”的代码。下面咱们总结了一些在Python开发中的惯用的代码。

让代码既能够被导入又能够被执行。

if __name__ == '__main__':

用下面的方式判断逻辑“真”或“假”。

if x:
if not x:

好的代码:

name = 'Safe'
pets = ['Dog', 'Cat', 'Hamster']
owners = {'Safe': 'Cat', 'George': 'Dog'}
if name and pets and owners:
    print('We have pets!')

很差的代码:

if name != '' and len(pets) > 0 and owners != {}:
    print('We have pets!')

善于使用in运算符。

if x in items: # 包含
for x in items: # 迭代

好的代码:

name = 'Safe Hammad'
if 'H' in name:
    print('This name has an H in it!')

很差的代码:

name = 'Safe Hammad'
if name.find('H') != -1:
    print('This name has an H in it!')

不使用临时变量交换两个值。

a, b = b, a

用序列构建字符串。

好的代码

chars = ['S', 'a', 'f', 'e']
name = ''.join(chars)
print(name) # Safe

很差的代码

chars = ['S', 'a', 'f', 'e']
name = ''
for char in chars:
    name += char
print(name) # Safe

EAFP 优于 LBYL

“It's Easier to Ask for Forgiveness than Permission.”
“Look Before You Leap”

好的代码

d = {'x': '5'}
try:
    value = int(d['x'])
except (KeyError, TypeError, ValueError):
    value = None

很差的代码

d = {'x': '5'}
if 'x' in d and \
    isinstance(d['x'], str) and \
    d['x'].isdigit():
    value = int(d['x'])
else:
    value = None

使用enumerate进行迭代。

好的代码

fruits = ['orange', 'grape', 'pitaya', 'blueberry']
for index, fruit in enumerate(fruits):
    print(index, ':', fruit)

很差的代码

fruits = ['orange', 'grape', 'pitaya', 'blueberry']
index = 0
for fruit in fruits:
    print(index, ':', fruit)
    index += 1

用生成式生成列表。

好的代码

data = [7, 20, 3, 15, 11]
result = [num * 3 for num in data if num > 10]
print(result)  # [60, 45, 33]

很差的代码

data = [7, 20, 3, 15, 11]
result = []
for i in data:
    if i > 10:
        result.append(i * 3)
print(result)  # [60, 45, 33]

用zip组合键和值来建立字典。

好的代码

keys = ['Safe', 'Bob', 'Thomas']
values = ['Hammad', 'Builder', 'Engine']
d = dict(zip(keys, values))
print(d) # {'Bob': 'Builder',
         #  'Safe': 'Hammad',
         #  'Thomas': 'Engine'}

很差的代码

keys = ['Safe', 'Bob', 'Thomas']
values = ['Hammad', 'Builder', 'Engine']
d = {}
for i, key in enumerate(keys):
    d[keys] = values[i]
print(d) # {'Bob': 'Builder',
         #  'Safe': 'Hammad',
         #  'Thomas': 'Engine'}

参考

PEP8风格指南

谷歌开源项目风格规范

惯例

相关文章
相关标签/搜索