分享

PEP8

 bitzhang 2020-05-01

代码布局

缩进

推荐的两种方式:

  • 垂直对齐:与(), [], {}对齐
  • 悬挂缩进

建议


# 垂直对齐,与外面(), [], {}对齐
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# 悬挂缩进,以行开头作为参考
foo = long_function_name(
    var_one, var_two,
    var_three, var_four)
    
    
# 为了和函数体区分开,采用更多的缩进
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

不建议

# 如果使用悬挂缩进而不是垂直对齐,第一行就不要有参数。
foo = long_function_name(var_one, var_two,
    var_three, var_four)

# 
def long_function_name(
    var_one, var_two, var_three,
    var_four):
    print(var_one)

括号结束的处理

  • 可以在最后一个元素后面
  • 可以和语句开头第一个字符对齐
  • 可以和上一行第一个不为空格的字符对齐
# 和上一行的第一个不为空格的字符对齐
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
    )
# 和语句开头的第一个字符对齐
my_list = [
    1, 2, 3,
    4, 5, 6,
]

if语句的缩进

# 无额外的缩进.
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# 添加一条注释来分隔if语句的两个部分
# supporting syntax highlighting.
if (this_is_one_thing and
    that_is_another_thing):
    # Since both conditions are true, we can frobnicate.
    do_something()

# 添加额外的缩进来分隔if语句的两个部分
if (this_is_one_thing
        and that_is_another_thing):
    do_something()

Tab还是空格

  • 推荐使用空格
  • 只有在兼容的时候使用Tab
  • Python 3不允许两者混合使用。
  • Python 2用选项-t 可以在混合使用的时候发出警告;使用选项-tt会发出错误。

单行的最大长度

79个字符。

对于docstring和注释:72个字符。

好处:

  • 在屏幕两边同时打开不同文件
  • 代码评审时,在屏幕两侧对比同一文件的不同版本

实践:

  • 一致就好
  • 能不使用反斜杠就不使用:用括号将内容括起来。
with open('/path/to/some/file/you/want/to/read') as file_1,      open('/path/to/some/file/being/written', 'w') as file_2:
    file_2.write(file_1.read())

换行到底在二元操作符前面还是后面?

# No: operators sit far away from their operands
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

# Yes: easy to match operators with operands
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

空行

  • 顶层的函数、类用两个空行分隔
  • 类内部的方法定义用一个空行分隔
  • 在函数内部使用空行表示逻辑的分块
  • 可以使用control-L作为换页符,貌似pycharm并不支持

文件编码

UTF-8

import语句

  • 分开使用
# Yes: 
import os
import sys

# No:
import sys, os

# OK:
from subprocess import Popen, PIPE

  • 在文件最上方:模块注释和docstring之后,globals和常量之前。顺寻应该是(每组之间用空行隔开):
    1. 标准库
    2. 相关的第三方库
    3. 本地库
  • 导入一个类时:
from myclass import MyClass
from foo.bar.yourclass import YourClass

# 如果类名有冲突
import myclass
import foo.bar.yourclass

# 使用时:"myclass.MyClass"  "foo.bar.yourclass.YourClass".
  • 尽量避免import mypkg.*

模块级的dunder name

模块级的dunder name,比如__all__, __version__, __author__应该放在import之前,但是在from __future__ import之后,因为这一句是强制要求在所有代码之前的。

"""This is the example module.

This module does stuff.
"""

from __future__ import barry_as_FLUFL

__all__ = ['a', 'b', 'c']
__version__ = '0.1'
__author__ = 'Cardinal Biggles'

import os
import sys

字符串的引号

单引号双引号都可以,那么选择一个,然后保持一致。

表达式和语句中的空格

一定不要这么做!

  • 括号内,紧挨括号的地方,不要加空格
# Yes: 
spam(ham[1], {eggs: 2})
foo = (0,)

# No:  
spam( ham[ 1 ], { eggs: 2 } )
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)
foo = (0, 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

其他的建议

  • 行尾不要有空格
  • 为下列二维操作符的前后各加一个空格:赋值、比较、booleans
  • 在同一句语句中,可以省略优先级较高的操作符之间的空格。二元操作符前后的数量一定要相等,一定不要超过一个。
  • 关键字参数和默认参数中的=前后不要加空格
# 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:
def munge(sep: AnyStr = None): ...
def munge() -> AnyStr: ...
def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
# No:
def munge(input: AnyStr=None): ...
def munge()->PosInt: ...
def munge(input: AnyStr, limit = 1000): ...

什么时候使用结尾的逗号

  • 只有一个元素的tuple。这种情况最好使用括号。
# Yes:
FILES = ('setup.cfg',)

# OK
FILES = 'setup.cfg',
  • 在一个值占一行,且右括号)]}单独占一行的情况,可以加一个多于的逗号在句尾。可以方便diff
# Yes:
FILES = [
    'setup.cfg',
    'tox.ini',
    ]
initialize(FILES,
           error=True,
           )

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

注释

  • 不要与代码矛盾!改代码也要改注释
  • 块注释必须由完整的句子组成,用句号分隔
  • 最好用英文

行内注释

  • 不要太多
  • 与语句至少2个空格,#后跟一个空格。

文档字符串 docstrings

  • 为公共的模块、函数、类、方法写注释。
  • 格式
"""Return a foobang

Optional plotz says to frobnicate the bizbaz first.
"""

命名规范

让所有代码都遵守一致的命名规范很难的,可以要求新的模块和包遵守一定的规范,但已有的库可能不满足,这时候需要在内部保持一致。

首要原则

对外的接口,要反映usage,而不是implementation

常见的命名方式

  • b
  • B
  • lowercase
  • lower_case_with_underscores
  • UPPERCASE
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords
  • mixedCase
  • Capitalized_Words_With_Underscores
    Python中的一些约定
  • _single_leading_underscore
  • single_trailing_underscore_
  • __double_leading_underscore
  • __double_leading_and_trailing_underscore__

规范

  • 模块名:小写字母+下划线
  • 包名:小写字母。e.g. timeit
  • 类名:首字母大写。e.g. CapWords
  • 异常名:首字母大写+后缀Error
  • 函数名、变量名、方法名、实例变量名:小写+下划线
  • 参数名:小写+下划线;selfcls是约定;与关键字冲突的时候,在后面加_
  • 常量:大写字母+下划线

继承的设计

  • 公共属性不要以下划线开头。
  • 如果属性与关键字冲突,在结尾地方加一个下划线。
  • 对public的数据属性,最好不要getter/setter(accessor/mutator)方法。
  • 如果有属性不想让子类使用,则命名为__foo,触发python的命名转换。

公共接口和内部接口

  • 必须保证公共接口向后兼容,对内部接口则不用
  • 必须为公共借口提供文档
  • 使用all声明公共接口
  • 内部接口(包、模块、类、函数等)使用前缀_

编程建议

  • 不要依赖与具体的python实现(PyPy, Jython, IronPython, Cython)
  • 与单例对象(比如None)作比较的时候,用isis not,而不是==
  • 使用is not而不是not ... is
# Yes:
if foo is not None:
    ...
# No:
if not foo is None:
    ...
  • 使用def,而不是赋值+lambda
  • python2中,用raise ValueError('message')代替raise ValueError, 'message'。后者在python3是错误的。
  • expect指明异常类型,需要异常名时,使用as
try:
    foo()
expect Exception as exc:
    print(exc)
  • try语句块内的语句尽可能的少。
  • 局部使用的资源,使用with

自动格式化的工具

black

  • 基于python3,可以格式化python2的代码
  • 比较专制,遵守pep8/pycodestyle
  • 默认全部使用双引号(为啥?),可以用选项--skip-string-normalization来跳过,不更改字符串的引号。
  • 在列表最后项的末尾加了多于的逗号,
  • 去除不必要的反斜杠\
  • 过长的字符串不做处理
  • 列表太多太长时:一项独占一行
  • # fmt: off# fmt: on来标记不需要format的语句块,兼容yapf的标记。
  • make pycodestyle happy. PEP8的子集
  • 在二元操作符前换行
  • 切片:PEP8

如何处理折行?

1. 尝试不折行
2. 打开最外层括号,括号内内容单独一行
3. 还是不行,迭代打开括号
4. 如果内容没有括号,有逗号做分隔,尝试同一行
5. 放不下->每一项独占一行,并加一个多于的逗号。(便于diff)
# in:

def very_important_function(template: str, *variables, file: os.PathLike, debug: bool = False):
    """Applies `variables` to the `template` and writes to `file`."""
    with open(file, 'w') as f:
        ...

# out:

def very_important_function(
    template: str,
    *variables,
    file: os.PathLike,
    debug: bool = False,
):
    """Applies `variables` to the `template` and writes to `file`."""
    with open(file, "w") as f:
        ...

行的长度

默认88,为了让文件长度更短。
尺有所短寸有所长。

空行

遵循PEP8

  • 在函数前后插入一个空行,在模块级的类和函数的前后插入两个空行。
  • 类的docstrings后紧跟一个空行
  • 函数的docstrings后一般不跟空行,除非后面紧跟一个内部函数。

tailing commas

  • 在独占一行的时候添加

isort

自动给import语句排序。

静态代码分析工具

pycodestyle

原名pep8。参照PEP 8。定义了Error codes

Pylint
flake8

formatter

yapf
black
  • “骄傲”
autopep8
  • 使用pycodestyle来决定需要格式化的代码,修复它报的大部分格式问题。
  • 默认,只做空格空行的增减,不会修正 x == None。加选项--agressive
  • --select修正指定的错误/警告。
  • 可以作为模块使用。
  • PEP 8pycodestyle为导向

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多