PEP8 python编程规范

2021/10/30 python 共 6173 字,约 18 分钟

本文主要介绍python的编程规范

编码风格

Guido的一个重要观点是代码被读的次数远多于被写的次数。pep8主要用于保持编写的Python代码风格一致,但是很多三方库的大佬也没有完全遵守这套规范(可能是历史原因造成需要和之前的代码保持一致)。 但是绝大多数情况下,作为开发者一定要遵守这些规范,只有这样代码可读性更强也能为开源社区贡献自己的力量。
idea基本上能很大程度规范我们的代码,下面主要介绍代码的布局及规范的具体操作。

代码布局

缩进

每个缩进级别采用4个空格。

续行有两种方式:隐式续行(垂直对齐于圆括号、方括号和花括号),悬挂缩进(第一行不应该包括参数,并且在续行中需要再缩进一级以便清楚表示)。
正确的例子:

# 隐式续行 同开始分界符(左括号)对齐
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)

对于续行来说,4空格的规则可以不遵守。

# 悬挂缩进可以不采用4空格的缩进方法。
foo = long_function_name(
  var_one, var_two,
  var_three, var_four)

每行最大长度

将所有行都限制在79个字符长度以内。当代码仅仅只由一个团队维护时,可以达成一致让行长度增加到80到100字符(实际上最大行长是99字符),注释和文档字符串仍然是以72字符换行。

二元运算符之前还是之后换行?

长期以来一直推荐的风格是在二元运算符之后换行。但是这样会影响代码可读性,包括两个方面:一是运算符会分散在屏幕上的不同列上,二是每个运算符会留在前一行并远离操作数。所以,阅读代码的时候眼睛必须做更多的工作来确定哪些操作数被加,哪些操作数被减:

# 错误的例子:运算符远离操作数
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)
# 正确的例子:更容易匹配运算符与操作数
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

空行

使用2个空行来分隔最外层的函数(function)和类(class)定义。
使用1个空行来分隔类中的方法(method)定义。
可以使用额外的空行(尽量少)来分隔一组相关的函数。在一系列相关的仅占一行的函数之间,空行也可以被省略(比如一组虚函数定义)。
在函数内使用空行(尽量少)使代码逻辑更清晰。

模块引用

Imports应该分行写,而不是都写在一行,例如:

# 分开写
import os
import sys
# 不要像下面一样写在一行
import sys, os

这样写也是可以的:

from subprocess import Popen, PIPE

Imports应该写在代码文件的开头,位于模块(module)注释和文档字符串(docstring)之后,模块全局变量(globals)和常量(constants)声明之前。

Imports应该按照下面的顺序分组来写:

1.标准库imports

2.相关第三方imports

3.本地应用/库的特定imports

不同组的imports之前用空格隔开。

推荐使用绝对(absolute)imports,因为这样通常更易读,在import系统没有正确配置(比如中的路径以sys.path结束)的情况下,也会有更好的表现(或者至少会给出错误信息):

import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example

然而,除了绝对imports,显式的相对imports也是一种可以接受的替代方式。特别是当处理复杂的包布局(package layouts)时,采用绝对imports会显得啰嗦。

from . import sibling
from .sibling import example

避免使用通配符imports(from import *),因为会造成在当前命名空间出现的命名含义不清晰,给读者和许多自动化工具造成困扰。有一个可以正当使用通配符import的情形,即将一个内部接口重新发布成公共API的一部分(比如,使用备选的加速模块中的定义去覆盖纯Python实现的接口,预先无法知晓具体哪些定义将被覆盖)。

模块级的双下划线命名

模块中的“双下滑线”(变量名以两个下划线开头,两个下划线结尾)变量,比如__all__,__author,__version__等,应该写在文档字符串(docstring)之后,除了form __future__引用(imports)的任何其它类型的引用语句之前。Python要求模块中__future__的导入必须出现在除文档字符串(docstring)之外的任何其他代码之前。

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

字符串引用

在Python中表示字符串时,不管用单引号还是双引号都是一样的。但是不推荐将这两种方式看作一样并且混用。最好选择一种规则并坚持使用。当字符串中包含单引号时,采用双引号来表示字符串,反之也是一样,这样可以避免使用反斜杠,代码也更易读。

表达式和语句中的空格

在下列情形中避免使用过多的空白:

  • 方括号,圆括号和花括号之后:
#正确的例子:
spam(ham[1], {eggs: 2})

#错误的例子:
spam( ham[ 1 ], { eggs: 2 } )
  • 逗号,分号或冒号之前:
#正确的例子:
if x == 4: print x, y; x, y = y, x

#错误的例子:
if x == 4 : print x , y ; x , y = y , x
  • 不过,在切片操作时,冒号和二元运算符是一样的,应该在其左右两边保留相同数量的空格(就像对待优先级最低的运算符一样)。在扩展切片操作中,所有冒号的左右两边空格数都应该相等。不过也有例外,当切片操作中的参数被省略时,应该也忽略空格。
#正确的例子:
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]

#错误的例子:
ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]
  • 在调用函数时传递参数list的括号之前:
#正确的例子:
spam(1)

#错误的例子:
pam (1)
  • 在索引和切片操作的左括号之前:
#正确的例子:
dct['key'] = lst[index]

#错误的例子:
dct ['key'] = lst [index]
  • 赋值(或其他)运算符周围使用多个空格来和其他语句对齐:
#正确的例子:
x = 1
y = 2
long_variable = 3

#错误的例子:
x             = 1
y             = 2
long_variable = 3

注释

和代码矛盾的注释还不如没有。当代码有改动时,一定要优先更改注释使其保持最新。

块注释

块注释一般写在对应代码之前,并且和对应代码有同样的缩进级别。块注释的每一行都应该以#和一个空格开头(除非该文本是在注释内缩进对齐的)。

块注释中的段落应该用只含有单个#的一行隔开。

行内注释

尽量少用行内注释。 行内注释是和代码语句写在一行内的注释。行内注释应该至少和代码语句之间有两个空格的间隔,并且以#和一个空格开始。

文档字符串

  • 所有的公共模块,函数,类和方法都应该有文档字符串。对于非公共方法,文档字符串不是必要的,但你应该留有注释说明该方法的功能,该注释应当出现在def的下一行。

  • 多行文档字符串以单行”"”结尾,不能有其他字符,例如:

"""Return a foobang

Optional plotz says to frobnicate the bizbaz first.
"""
  • 对于仅有一行的文档字符串,结尾处的”"”应该也写在这一行。

命名约定

Python标准库的命名约定有一些混乱,因此我们永远都无法保持一致。但如今仍然存在一些推荐的命名标准。新的模块和包(包括第三方框架)应该采用这些标准,但若是已经存在的包有另一套风格的话,还是应当与原有的风格保持内部一致。

对于用户可见的公共部分API,其命名应当表达出功能用途而不是其具体的实现细节。

描述:命名风格

存在很多不同的命名风格,最好能够独立地从命名对象的用途认出采用了哪种命名风格。

  • b (单个小写字母)
  • B (单个大写字母)
  • lowercase(小写)
  • lower_case_with_underscores(带下划线小写)
  • UPPERCASE(大写)
  • UPPER_CASE_WITH_UNDERSCORES(带下划线大写)
  • CapitalizedWords(大驼峰):当CapWords里包含缩写时,将缩写部分的字母都大写。HTTPServerError比HttpServerError要好
  • capitalizedWords(小驼峰)
  • Capitalized_Words_With_Underscores (这种风格超丑!)
  • _single_leading_underscore:以单个下划线开头是”内部使用”的弱标志。 比如, from M import *不会import下划线开头的对象。
  • single_trailing_underscore_:以单个下划线结尾用来避免和Python关键词产生冲突。
  • __double_leading_underscore:以双下划线开头的风格命名类属性表示触发命名修饰(在FooBar类中,__boo命名会被修饰成_FooBar__boo
  • double_leading_and_trailing_underscore__:以双下划线开头和结尾的命名风格表示“魔术”对象或属性,存在于用户控制的命名空间(user-controlled namespaces)里(也就是说,这些命名已经存在,但通常需要用户覆写以实现用户所需要的功能)。 比如, __init, importfile。请依照文档描述来使用这些命名,千万不要自己发明。

规范:命名约定

  • 需要避免的命名

不要使用字符’l’(L的小写的字母),’O’(o大写的字母),或者’I’(i的大写的字母)来作为单个字符的变量名。 在一些字体中,这些字符和数字1和0无法区别开来。比如,当想使用’l’时,使用’L’代替。

  • ASCII兼容性

标准库中使用的标识符必须与ASCII兼容

  • 包和模块命名

模块命名应短小,且为全小写。若下划线能提高可读性,也可以在模块名中使用。Python包命名也应该短小,且为全小写,但不应使用下划线。

  • 类命名

类命名应该使用驼峰(CapWords)的命名约定。

  • 类型变量命名

建议将后缀_co或_contra添加到用于声明相应的协变(covariant)和逆变(contravariant)的行为。例如:

from typing import TypeVar

VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)
  • 异常命名

使用CapWords+Error后缀的方式

  • 全局变量命名

(在此之前,我们先假定这些变量都仅在同一个模块内使用。)这些约定同样也适用于函数命名。

对于引用方式设计为from M import *的模块,应该使用__all__机制来避免import全局变量,或者采用下划线前缀的旧约定来命名全局变量,从而表明这些变量是“模块非公开的”。

  • 函数命名

函数命名应该都是小写,必要时使用下划线来提高可读性。

  • 函数和方法参数

实例方法的第一参数永远都是self。
类方法的第一个参数永远都是cls。\

  • 常量

常量通常是在模块级别定义的,使用全部大写并用下划线将单词分开。如:MAX_OVERFLOW和TOTAL 。

编程建议

  • 代码应该以不影响其他Python实现(PyPy,Jython,IronPython,Cython,Psyco等)的方式编写。 例如,不要依赖于 CPython 在字符串拼接时的优化实现,像这种语句形式a += b和a = a + b。即使是 CPython(仅对某些类型起作用) 这种优化也是脆弱的,不是在所有的实现中都不使用引用计数。在库中性能敏感的部分,用’‘.join形式来代替。这会确保在所有不同的实现中字符串拼接是线性时间的。

  • 与单例作比较,像None应该用is或is not,从不使用==操作符。 同样的,当心if x is not None这样的写法,你是不知真的要判断x不是None。例如,测试一个默认值为None的变量或参数是否设置成了其它值,其它值有可能是某种特殊类型(如容器),这种特殊类型在逻辑运算时其值会被当作Flase来看待。

  • 用is not操作符而不是not … is。

  • 始终使用def语句来代替直接绑定了一个lambda表达式的赋值语句。

  • 异常类应派生自Exception而不是BaseException。直接继承BaseException是为Exception保留的,从BaseException继承并捕获异常这种做法几乎总是错的。

  • 捕获异常时,尽可能使用明确的异常,而不是用一个空的except:语句。

  • 用’‘.startswith()和’‘.endswith()代替字符串切片来检查前缀和后缀。

  • 对象类型的比较应该始终使用isinstance()而不是直接比较。

  • 对于序列(字符串、列表、元组)来说,空的序列为False:

#正确写法:
if not seq:
if seq:
#错误写法:
if len(seq):
if not len(seq):
  • 不要用==比较True和False。
#推荐的写法:
if greeting:

if not greeting:

#不推荐的写法:
if greeting == True:

#更加不推荐的写法:
if greeting is True:

搜索

    内容