PEP8--Python编码风格参考(二)

2017年08月31日 原创
关键词: python
摘要 PEP8--Python编码风格参考(二)

Whitespace in Expressions and Statements

Avoid extraneous whitespace in the following situations:

  • Immediately inside parentheses, brackets or braces.

    Yes: spam(ham[1], {eggs: 2})
    No:  spam( ham[ 1 ], { eggs: 2 } )
    
  • Between a trailing comma and a following close parenthesis.

    Yes: foo = (0,)
    No:  bar = (0, )
    
  • Immediately before a comma, semicolon, or colon:

    Yes: if x == 4: print x, y; x, y = y, x
    No:  if x == 4 : print x , y ; x , y = y , x
    
  • However, in a slice the colon acts like a binary operator, and should have equal amounts on either side (treating it as the operator with the lowest priority). In an extended slice, both colons must have the same amount of spacing applied. Exception: when a slice parameter is omitted, the space is omitted.

    Yes:

    ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
    ham[lower:upper], ham[lower:upper:], ham[lower::step]
    ham[lower+offset : upper+offset]
    ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
    ham[lower + offset : upper + offset]
    

    No:

    ham[lower + offset:upper + offset]
    ham[1: 9], ham[1 :9], ham[1:9 :3]
    ham[lower : : upper]
    ham[ : upper]
    
  • Immediately before the open parenthesis that starts the argument list of a function call:

    Yes: spam(1)
    No:  spam (1)
    
  • Immediately before the open parenthesis that starts an indexing or slicing:

    Yes: dct['key'] = lst[index]
    No:  dct ['key'] = lst [index]
    
  • More than one space around an assignment (or other) operator to align it with another.

    Yes:

    x = 1
    y = 2
    long_variable = 3
    

    No:

    x             = 1
    y             = 2
    long_variable = 3
    

 

在表达式和语句中的空白字符

在以下的情况避免额外的空白字符:

  • 在紧接着的大/中/小括号中:

    Yes: spam(ham[1], {eggs: 2})
    No:  spam( ham[ 1 ], { eggs: 2 } )
    
  • 在结尾的逗号和右括号之间。

    Yes: foo = (0,)
    No:  bar = (0, )
    
  • 在紧接着的逗号、分号和冒号之间。

    Yes: if x == 4: print x, y; x, y = y, x
    No:  if x == 4 : print x , y ; x , y = y , x
    
  • 但是在切片中,冒号是作为一个二进制运算符,所以两端应该有相同的空白(把他当做优先级最低的运算符)。 在一个扩展的切片中,两个冒号必须应用相同的空白。例外:当一个切片中的参数被省略了,空白也应该省略。

    Yes:

    ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
    ham[lower:upper], ham[lower:upper:], ham[lower::step]
    ham[lower+offset : upper+offset]
    ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
    ham[lower + offset : upper + offset]
    

    No:

    ham[lower + offset:upper + offset]
    ham[1: 9], ham[1 :9], ham[1:9 :3]
    ham[lower : : upper]
    ham[ : upper]
    
  • 在一个左括号紧接着参数列表的函数调用中:

    Yes: spam(1)
    No:  spam (1)
    
  • 在一个左括号紧接着参数列表的切片或者索引中:

    Yes: dct['key'] = lst[index]
    No:  dct ['key'] = lst [index]
    
  • 为了排列整齐,在赋值语句或其他语句中使用多个空格。

    Yes:

    x = 1
    y = 2
    long_variable = 3
    

    No:

    x             = 1
    y             = 2
    long_variable = 3
    

Other Recommendations

  • Avoid trailing whitespace anywhere. Because it's usually invisible, it can be confusing: e.g. a backslash followed by a space and a newline does not count as a line continuation marker. Some editors don't preserve it and many projects (like CPython itself) have pre-commit hooks that reject it.

  • Always surround these binary operators with a single space on either side: assignment (=), augmented assignment (+=, -= etc.), comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not), Booleans (and, or, not).

  • If operators with different priorities are used, consider adding whitespace around the operators with the lowest priority(ies). Use your own judgment; however, never use more than one space, and always have the same amount of whitespace on both sides of a binary operator.

    Yes:

    i = i + 1
    submitted += 1
    x = x*2 - 1
    hypot2 = x*x + y*y
    c = (a+b) * (a-b)
    

    No:

    i=i+1
    submitted +=1
    x = x * 2 - 1
    hypot2 = x * x + y * y
    c = (a + b) * (a - b)
    
  • Don't use spaces around the = sign when used to indicate a keyword argument or a default parameter value.

    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)
    
  • Function annotations should use the normal rules for colons and always have spaces around the -> arrow if present. (See Function Annotations below for more about function annotations.)

    Yes:

    def munge(input: AnyStr): ...
    def munge() -> AnyStr: ...
    

    No:

    def munge(input:AnyStr): ...
    def munge()->PosInt: ...
    
  • When combining an argument annotation with a default value, use spaces around the = sign (but only for those arguments that have both an annotation and a default).

    Yes:

    def munge(sep: AnyStr = None): ...
    def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
    

    No:

    def munge(input: AnyStr=None): ...
    def munge(input: AnyStr, limit = 1000): ...
    
  • Compound statements (multiple statements on the same line) are generally discouraged.

    Yes:

    if foo == 'blah':
        do_blah_thing()
    do_one()
    do_two()
    do_three()
    

    Rather not:

    if foo == 'blah': do_blah_thing()
    do_one(); do_two(); do_three()
    
  • While sometimes it's okay to put an if/for/while with a small body on the same line, never do this for multi-clause statements. Also avoid folding such long lines!

    Rather not:

    if foo == 'blah': do_blah_thing()
    for x in lst: total += x
    while t < 10: t = delay()
    

    Definitely not:

    if foo == 'blah': do_blah_thing()
    else: do_non_blah_thing()
    
    try: something()
    finally: cleanup()
    
    do_one(); do_two(); do_three(long, argument,
                                 list, like, this)
    
    if foo == 'blah': one(); two(); three()
    

其他建议

  • 避免在代码尾部留下空白。因为这通常不可见,这会造成疑惑:例如一个反斜线后面跟着一个空格,新行就不会当做一个多行表达式。有些编辑器不会保存尾部空格,并且在许多项目(比如CPython)中有预提交钩子拒绝尾部空格。

  • 总是在二进制运算符的前后添加一个空格:赋值运算符(=)、带参数的赋值运算符(+=、-=等)、比较运算符(==、<、>、!=、<>、<=、>=、in、not in、is、is not)、布尔运算符(and、or、not)。

  • 如果同时使用不同优先级的运算符,则在优先级最低的运算符前后增加空格。根据你自己的判断去添加空格,永远不要添加多于一个的空格,并且运算符前后的空格要保持一致。

    Yes:

    i = i + 1
    submitted += 1
    x = x*2 - 1
    hypot2 = x*x + y*y
    c = (a+b) * (a-b)
    

    No:

    i=i+1
    submitted +=1
    x = x * 2 - 1
    hypot2 = x * x + y * y
    c = (a + b) * (a - b)
    
  • 在指明参数和默认参数时,= 号前后不能加空格。

    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)
    
  • 函数注释里的冒号适用一般的格式,并且如果有 -> 箭头出现,则应该在其两端留有空格。 (查看更多信息 Function Annotations )

    Yes:

    def munge(input: AnyStr): ...
    def munge() -> AnyStr: ...
    

    No:

    def munge(input:AnyStr): ...
    def munge()->PosInt: ...
    
  • 当同时使用参数注释和参数默认值时,应该在等号前后留有空格。 (只针对于同时有参数注释和默认参数的情况)。

    Yes:

    def munge(sep: AnyStr = None): ...
    def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
    

    No:

    def munge(input: AnyStr=None): ...
    def munge(input: AnyStr, limit = 1000): ...
    
  • 单行的复杂语句(多个语句在同一行)是不建议的。

    Yes:

    if foo == 'blah':
        do_blah_thing()
    do_one()
    do_two()
    do_three()
    

    Rather not:

    if foo == 'blah': do_blah_thing()
    do_one(); do_two(); do_three()
    
  • 在某些情况下,把if/for/while和少量代码的代码体放在一起是可以的。但是多语句的代码体千万不能这样做,同时也避免了代码的换行缩进等问题。

    尽量不:

    if foo == 'blah': do_blah_thing()
    for x in lst: total += x
    while t < 10: t = delay()
    

    一定不:

    if foo == 'blah': do_blah_thing()
    else: do_non_blah_thing()
    
    try: something()
    finally: cleanup()
    
    do_one(); do_two(); do_three(long, argument,
                                 list, like, this)
    
    if foo == 'blah': one(); two(); three()
    

When to use trailing commas

Trailing commas are usually optional, except they are mandatory when making a tuple of one element (and in Python 2 they have semantics for the print statement). For clarity, it is recommended to surround the latter in (technically redundant) parentheses.

Yes:

FILES = ('setup.cfg',)

OK, but confusing:

FILES = 'setup.cfg',

When trailing commas are redundant, they are often helpful when a version control system is used, when a list of values, arguments or imported items is expected to be extended over time. The pattern is to put each value (etc.) on a line by itself, always adding a trailing comma, and add the close parenthesis/bracket/brace on the next line. However it does not make sense to have a trailing comma on the same line as the closing delimiter (except in the above case of singleton tuples).

Yes:

FILES = [
    'setup.cfg',
    'tox.ini',
    ]
initialize(FILES,
           error=True,
           )

No:

FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)

何时使用尾部的逗号

尾部的逗号通常是可选的,除了在创建只有一个元素的元组时。为了更加明确,建议在创建元组时使用括号。

Yes:

FILES = ('setup.cfg',)

可以使用,但是会造成疑惑:

FILES = 'setup.cfg',

在结尾使用不必要的逗号,通常在版本控制系统的使用中有所帮助,当一个包含值,参数或者导入项的列表会被多次扩展的时候。这个模式让每个值在一行,并且在每行末尾加上逗号,最后的括号单独在下一行。然而在同一行使用尾部逗号当做结束符是没有意义的(除了上述的情况)。

Yes:

FILES = [
    'setup.cfg',
    'tox.ini',
    ]
initialize(FILES,
           error=True,
           )

No:

FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)

Comments

Comments that contradict the code are worse than no comments. Always make a priority of keeping the comments up-to-date when the code changes!

Comments should be complete sentences. If a comment is a phrase or sentence, its first word should be capitalized, unless it is an identifier that begins with a lower case letter (never alter the case of identifiers!).

If a comment is short, the period at the end can be omitted. Block comments generally consist of one or more paragraphs built out of complete sentences, and each sentence should end in a period.

You should use two spaces after a sentence-ending period.

When writing English, follow Strunk and White.

Python coders from non-English speaking countries: please write your comments in English, unless you are 120% sure that the code will never be read by people who don't speak your language.

Block Comments

Block comments generally apply to some (or all) code that follows them, and are indented to the same level as that code. Each line of a block comment starts with a # and a single space (unless it is indented text inside the comment).

Paragraphs inside a block comment are separated by a line containing a single #.

Inline Comments

Use inline comments sparingly.

An inline comment is a comment on the same line as a statement. Inline comments should be separated by at least two spaces from the statement. They should start with a # and a single space.

Inline comments are unnecessary and in fact distracting if they state the obvious. Don't do this:

x = x + 1                 # Increment x

But sometimes, this is useful:

x = x + 1                 # Compensate for border

Documentation Strings

Conventions for writing good documentation strings (a.k.a. "docstrings") are immortalized in PEP 257.

  • Write docstrings for all public modules, functions, classes, and methods. Docstrings are not necessary for non-public methods, but you should have a comment that describes what the method does. This comment should appear after the def line.

  • PEP 257 describes good docstring conventions. Note that most importantly, the """ that ends a multiline docstring should be on a line by itself, e.g.:

    """Return a foobang
    
    Optional plotz says to frobnicate the bizbaz first.
    """
    
  • For one liner docstrings, please keep the closing """ on the same line.

注释

与代码冲突的注释不如不加注释。记得总是在代码更新的时候同时更新注释。

注释应该是完整的句子。如果注释是短语和句子,首字母应该大写,除非它是首字母小写的特殊词语(永远不要修改这种特殊词语的大小写)。

如果注释很短,句号可以省略。块状注释通常包含由完整句子构成的一个或多个段落,每个句子结尾都应该有句号。

你应该在句子结尾的句号后添加两个空格。

注释是英文时,应该遵循 Strunk and White.

非英语国家的Python程序员:请用英语写你的注释,除非你120%确定你的代码不会被其他国家的人阅读。

块状注释

块状注释通常在描述的代码之前,并且和代码的缩进保持一致。块状注释的每一行都应该以#号和一个空格开头(除非是在注释里缩进了的文字)。

在块状注释中,段落通过以#号开头的空行进行分隔。

行间注释

尽量少的使用行间注释。

一个行间注释是和代码在同一行的表达式。行间注释和代码之间至少间隔2个空格。行间注释以#号和一个空格开头。

行间注释不是必要的,并且在代码是很明显的时候,行间注释是烦人的。不要写以下的行间注释:

x = x + 1                 # Increment x

但是下面的情况,行间注释是很有用的:

x = x + 1                 # Compensate for border

文档字符串

为了写出好的文档,PEP 257提供了固定的规定。

  • 对所有公共模块、函数、类和方法都加上文档字符串。对于非公共的方法,文档字符串不是很有必要,但是你仍然需要写上注释来描述这个方法做了什么。这种注释应该在 def 的下一行。

  • PEP 257 描述了良好的文档字符串规定。 需要特别注意, 在最后一行的多行文档字符串中,"""需要作为结尾的单独一行。例如:

    """Return a foobang
    
    Optional plotz says to frobnicate the bizbaz first.
    """
    
  • 对于一行的文档字符串,需要保持结尾的"""在同一行。

Naming Conventions

The naming conventions of Python's library are a bit of a mess, so we'll never get this completely consistent -- nevertheless, here are the currently recommended naming standards. New modules and packages (including third party frameworks) should be written to these standards, but where an existing library has a different style, internal consistency is preferred.

Overriding Principle

Names that are visible to the user as public parts of the API should follow conventions that reflect usage rather than implementation.

命名规范

Python标准库的命名规范是有一点混乱的,所以对于命名规范我们不能得出完全的一致。The naming conventions of Python's library are a bit of a mess, so we'll never get this completely consistent -- 然而,这里有一些当今推荐的命名规范。新的模块和包(第三方框架)需要遵循这些规范,但是如果项目之前就存在了其他的规范,最好遵守该项目的规范。

最重要的原则

能让用户看到的API的公共模块的名称需要遵循名称能够反映用法而不是反映实现的规则。

Descriptive: Naming Styles

There are a lot of different naming styles. It helps to be able to recognize what naming style is being used, independently from what they are used for.

The following naming styles are commonly distinguished:

  • b (single lowercase letter)

  • B (single uppercase letter)

  • lowercase

  • lower_case_with_underscores

  • UPPERCASE

  • UPPER_CASE_WITH_UNDERSCORES

  • CapitalizedWords (or CapWords, or CamelCase -- so named because of the bumpy look of its letters [4]). This is also sometimes known as StudlyCaps.

    Note: When using abbreviations in CapWords, capitalize all the letters of the abbreviation. Thus HTTPServerError is better than HttpServerError.

  • mixedCase (differs from CapitalizedWords by initial lowercase character!)

  • Capitalized_Words_With_Underscores (ugly!)

There's also the style of using a short unique prefix to group related names together. This is not used much in Python, but it is mentioned for completeness. For example, the os.stat() function returns a tuple whose items traditionally have names like st_mode, st_size, st_mtime and so on. (This is done to emphasize the correspondence with the fields of the POSIX system call struct, which helps programmers familiar with that.)

The X11 library uses a leading X for all its public functions. In Python, this style is generally deemed unnecessary because attribute and method names are prefixed with an object, and function names are prefixed with a module name.

In addition, the following special forms using leading or trailing underscores are recognized (these can generally be combined with any case convention):

  • _single_leading_underscore: weak "internal use" indicator. E.g. from M import * does not import objects whose name starts with an underscore.

  • single_trailing_underscore_: used by convention to avoid conflicts with Python keyword, e.g.

    Tkinter.Toplevel(master, class_='ClassName')
    
  • __double_leading_underscore: when naming a class attribute, invokes name mangling (inside class FooBar, __boo becomes _FooBar__boo; see below).

  • __double_leading_and_trailing_underscore__: "magic" objects or attributes that live in user-controlled namespaces. E.g. __init__, __import__ or __file__. Never invent such names; only use them as documented.

命名样式的分类

命名样式多种多样。正因为这样我们可以认出代码里使用了哪种命名样式,而与使用的语言无关。

下面的集中命名样式普遍具有辨识力:

  • b (单个字母小写)

  • B (单个字母大写)

  • lowercase 小写单词

  • lower_case_with_underscores 以下划线分割的小写单词

  • UPPERCASE 大写单词

  • UPPER_CASE_WITH_UNDERSCORES 以下划线分割的大写单词

  • CapitalizedWords (大写首字母,或驼峰标识)因为看起来像驼峰所以叫驼峰标识。

    注意:在驼峰标识里的缩写应该全部大写。所以HTTPServerError比HttpServerError要好。

  • mixedCase (首字母小写的驼峰标识)

  • Capitalized_Words_With_Underscores 以下划线分割的驼峰标识(很丑)

还有的在名称前面加上小而特殊的前缀来给名称分类。这通常不在Python中使用,为了完整性在这里提一下。比如, os.stat() 函数返回了一个元组,在这个元组里的元素通常类似于 st_mode, st_size, st_mtime 。 (这是为了强调这些字段是POSIX系统的系统调用结构体,这会帮助编程者习惯这些结构)

X11库在所有公共方法前都加了前缀的X。在Python中,这种命名方式通常被认为是不必要的,因为方法和属性的名称都有对象作为前缀,函数的名称有模块作为前缀。

额外的,下面使用前缀的下划线是被认可的特殊情况(可以与所有的命名规范组合):

  • _single_leading_underscore(单下划线前缀): 弱的私有变量标志符。比如 from M import * 不会导入这种对象。

  • single_trailing_underscore_(单下划线后缀): 用来方便的处理与Python关键字的变量的冲突。比如:

    Tkinter.Toplevel(master, class_='ClassName')
    
  • __double_leading_underscore(双下划线前缀): 当使用在类属性时,会触发名称修饰(在类FooBar,中__boo会变成_FooBar__boo)

  • __double_leading_and_trailing_underscore__(双下划线前缀和后缀): "魔术"对象或者属性,存在于用户名称空间。 比如 __init__, __import__ 或者 __file__。永远不要创建这种名字,只使用文档有提及的这种名字。