python代码规范 总结

2020-06-09  本文已影响0人  Jayce_xi

1.PEP8

PEP: 8
Title: Style Guide for Python Code
Author: Guido van Rossum , Barry Warsaw , Nick Coghlan
Status: Active
Type: Process
Created: 05-Jul-2001
Post-History: 05-Jul-2001, 01-Aug-2013

1.1 简介

PEP 8 全称为 Python Enhancement Proposal #8 , 中文叫做《8号Python增强提案》。PEP 8 中列出了很多编程的细节,认真实践有助于养成良好的编程习惯。

Guido的一个重要观点是代码被读的次数远多于被写的次数。这篇指南旨在提高代码的可读性,使浩瀚如烟的Python代码风格能保持一致。正如PEP 20那首《Zen of Python》的小诗里所说的:“可读性很重要(Readability counts)”。

这本风格指南是关于一致性的。同风格指南保持一致性是重要的,但是同项目保持一致性更加重要,同一个模块和一个函数保持一致性则最为重要。

然而最最重要的是:要知道何时去违反一致性,因为有时风格指南并不适用。当存有疑虑时,请自行做出最佳判断。请参考别的例子去做出最好的决定。并且不要犹豫,尽管提问。

特别的:千万不要为了遵守这篇PEP而破坏向后兼容性!

如果有以下理由,则可以忽略这份风格指南:

  1. 当采用风格指南时会让代码更难读,甚至对于习惯阅读遵循这篇PEP的代码的人来说也是如此。

  2. 需要和周围的代码保持一致性,但这些代码违反了指南中的风格(可是时历史原因造成的)——尽管这可能也是一个收拾别人烂摊子的机会(进入真正的极限编程状态)。

  3. 若是有问题的某段代码早于引入指南的时间,那么没有必要去修改这段代码。

  4. 代码需要和更旧版本的Python保持兼容,而旧版本的Python不支持风格指南所推荐的特性。

1.2 代码布局

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

连续行所包装的元素应该要么采用Python隐式续行,即垂直对齐于圆括号、方括号和花括号,要么采用悬挂缩进(hanging indent)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)

错误的例子:

# 采用垂直对齐时第一行不应该有参数
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)
1.2.2 tab还是空格?

推荐使用空格来进行缩进。
Tab应该只在现有代码已经使用tab进行缩进的情况下使用,以便和现有代码保持一致。
Python 3不允许tab和空格混合使用。
Python 2的代码若有tab和空格混合使用的情况,应该把tab全部转换为只有空格。

1.2.3 每行最大长度

将所有行都限制在79个字符长度以内。

对于连续大段的文字(比如文档字符串(docstring)或注释),其结构上的限制更少,这些行应该被限制在72个字符长度内。

限制编辑器的窗口宽度能让好几个文件同时打开在屏幕上显示,在使用代码评审(code review)工具时在两个相邻窗口显示两个版本的代码效果很好。

很多工具的默认自动换行会破坏代码的结构,使代码更难以理解。在窗口大小设置为80个字符的编辑器中,即使在换行时编辑器可能会在最后一列放置一个记号,为避免自动换行也需要限制每行字符长度。一些基于web的工具可能根本没有自动换行的功能。

一些团队会强烈希望行长度比79个字符更长。当代码仅仅只由一个团队维护时,可以达成一致让行长度增加到80到100字符(实际上最大行长是99字符),注释和文档字符串仍然是以72字符换行。

一种推荐的换行方式是利用Python圆括号、方括号和花括号中的隐式续行。长行可以通过在括号内换行来分成多行。应该最好加上反斜杠来区别续行。

有时续行只能使用反斜杠才。例如,较长的多个with语句不能采用隐式续行,只能接受反斜杠表示换行:

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())
1.2.4 二元运算符之前还是之后换行?

推荐以下形式

# 正确的例子:更容易匹配运算符与操作数
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)
1.2.5 空行的选择

使用2个空行来分隔最外层的函数(function)和类(class)定义。

使用1个空行来分隔类中的方法(method)定义。

1.2.6 模块引用

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
4.不同组的imports之前用空格隔开。

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

1.2.7 模块级的双下划线命名

模块中的“双下滑线”(变量名以两个下划线开头,两个下划线结尾)变量,比如__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

1.3 字符串引用

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

对于三引号表示的字符串,使用双引号字符来表示6,这样可以和PEP 257的文档字符串(docstring)规则保持一致。

1.4表达式和语句中的空格

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

#正确的例子:
spam(ham[1], {eggs: 2})

#错误的例子:
spam( ham[ 1 ], { eggs: 2 } )
#正确的例子:
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]
#正确的例子:
x = 1
y = 2
long_variable = 3

#错误的例子:
x             = 1
y             = 2
long_variable = 3
#正确的例子:
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)

#错误的例子:
i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)

1.5注释

1.5.1 注释指南

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

注释应该是完整的多个句子。如果注释是一个短语或一个句子,其首字母应该大写,除非开头是一个以小写字母开头的标识符(永远不要更改标识符的大小写)。

如果注释很短,结束的句号可以被忽略。块注释通常由一段或几段完整的句子组成,每个句子都应该以句号结束。
你应该在句尾的句号后再加上2个空格。

使用英文写作,参考Strunk和White的《The Elements of Style》

来自非英语国家的Python程序员们,请使用英文来写注释,除非你120%确定你的代码永远不会被不懂你所用语言的人阅读到。

1.5.2块注释

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

1.5.3 行内注释

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

1.5.4 文档字符串

要知道如何写出好的文档字符串(docstring),请参考PEP 257

"""Return a foobang

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

1.6命名约定

1.6.1 首要原则

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

1.6.2 需要避免的命名

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

1.6.3 包和模块命名

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

1.6.4 类命名

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

1.6.5 异常命名

由于异常实际上也是类,因此类命名约定也适用与异常。不同的是,如果异常实际上是抛出错误的话,异常名前应该加上”Error”的前缀。

1.6.6 全局变量命名

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

1.6.7 函数命名

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

1.6.8 函数和方法参数

实例方法的第一参数永远都是self。
类方法的第一个参数永远都是cls。
在函数参数名和保留关键字冲突时,相对于使用缩写或拼写简化,使用以下划线结尾的命名一般更好。比如,class_比clss更好。(或许使用同义词避免这样的冲突是更好的方式。)

1.6.9 方法命名和实例变量

使用函数命名的规则:小写单词,必要时使用下划线分开以提高可读性。
仅对于非公开方法和变量命名在开头使用一个下划线。
避免和子类的命名冲突,使用两个下划线开头来触发Python的命名修饰机制。
Python类名的命名修饰规则:如果类Foo有一个属性叫__a,不能使用Foo.__a的方式访问该变量。(有用户可能仍然坚持使用Foo._Foo__a的方法访问。)一般来说,两个下划线开头的命名方法仅用于避免与设计为子类的类中的属性名冲突。

1.6.10 常量

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

1.6.11 继承的设计

记得永远区别类的方法和实例变量(属性)应该是公开的还是非公开的。如果有疑虑的话,请选择非公开的;因为之后将非公开属性变为公开属性要容易些。
公开属性是那些你希望和你定义的类无关的客户来使用的,并且确保不会出现向后不兼容的问题。非公开属性是那些不希望被第三方使用的部分,你可以不用保证非公开属性不会变化或被移除。
我们在这里没有使用“私有(private)”这个词,因为在Python里没有什么属性是真正私有的(这样设计省略了大量不必要的工作)。
另一类属性属于子类API的一部分(在其他语言中经常被称为”protected”)。一些类是为继承设计的,要么扩展要么修改类的部分行为。当设计这样的类时,需要谨慎明确地决定哪些属性是公开的,哪些属于子类API,哪些真的只会被你的基类调用。

1.7 编程建议

2.Yapf介绍及使用

2.1Yapf 背景

现在的大多数 Python 代码格式化工具(比如:autopep8 和 pep8ify)是可以移除代码中的 lint 错误。这显然有些局限性。比如:遵循 PEP 8 指导的代码可能就不会被格式化了,但这并不说明代码看起来就舒服。

但 YAPF 独辟蹊径。它脱胎于由 Daniel Jasper 开发的 clang-format。大体上来说,这个算法获取代码,然后把初始代码重新编排,即便初始代码并没有违背规范,也可使其达到遵循代码规范的最佳格式。这个理念和 Go 语言中的 gofmt 工具相似,终结关于格式的各种“圣战”。如果一个项目的代码库,无论何时修改,通过 YAPF 优化后,代码风格可统一,在每次 code review 中,也就没有必要争论风格了。

YAPF 的终极目标是生成和遵循代码规范的程序员写出的一样的代码。可帮你减少维护代码的苦差事。

YAPF 支持 Python 2.7 和 3.4.6+

2.2Yapf 安装

pip install yapf

Yapf 使用

# python3 -m yapf -h
usage: __main__.py [-h] [-v] [-d | -i | -q] [-r | -l START-END] [-e PATTERN]
                   [--style STYLE] [--style-help] [--no-local-style] [-p]
                   [-vv]
                   [files [files ...]]

Formatter for Python code.

positional arguments:
  files                 reads from stdin when no files are specified.

optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show version number and exit
  -d, --diff            print the diff for the fixed source  # 输出前后不相同的值
  -i, --in-place        make changes to files in place  # 直接修改
  -q, --quiet           output nothing and set return value
  -r, --recursive       run recursively over directories  # 递归文件下的所有文件
  -l START-END, --lines START-END  # 指定行号
                        range of lines to reformat, one-based
  -e PATTERN, --exclude PATTERN
                        patterns for files to exclude from formatting
  --style STYLE         specify formatting style: either a style name (for
                        example "pep8" or "google"), or the name of a file
                        with style settings. The default is pep8 unless a
                        .style.yapf or setup.cfg file located in the same
                        directory as the source or one of its parent
                        directories (for stdin, the current directory is
                        used).  # 指定格式化的类型,默认为pep8, 还有google,facebook,yapf可选
  --style-help          show style settings and exit; this output can be saved
                        to .style.yapf to make your settings permanent
  --no-local-style      don't search for local style definition
  -p, --parallel        run yapf in parallel when formatting multiple files.  # 并行格式化多个文件
                        Requires concurrent.futures in Python 2.X
  -vv, --verbose        print out file names while processing

2.3日常使用

# python3 -m yapf -rip ./

// 配置谷歌代码风格,可选有pep8, google, yapf, facebook
# python3 -m yapf -rip --style=google ./

2.4如何对某一些代码不使用yapf

# yapf: disable

2.5自定义配置代码风格

Yapf 使用的格式化样式是可配置的,并且有许多“旋钮”可用于调优 YAPF 的格式化方式。

2.6忽略某一些文件

在项目根目录建立一个文件.yapfignore
用法与.gitignore相似

2.7 --style 使用

优先级有高到低

  1. 命令行上指定
--style='{based_on_style: pep8, indent_width: 2}'
  1. .style.yapf文件中自定义(当前目录)
[style]
based_on_style = pep8
spaces_before_comment = 4
  1. setup.config文件中自定义(当前目录)
[yapf]
based_on_style = pep8
spaces_before_comment = 4
  1. ~/.config/yapf/style文件中自定义
[style]
based_on_style = pep8
spaces_before_comment = 4

2.7使用包进行format

主要API有两个:FormatCodeFormatFile

>>> from yapf.yapflib.yapf_api import FormatCode  # reformat a string of code

>>> FormatCode("f ( a = 1, b = 2 )")
'f(a=1, b=2)\n'

>>> FormatCode("def g():\n  return True", style_config='pep8')
'def g():\n    return True\n'
>>> from yapf.yapflib.yapf_api import FormatFile  # reformat a file

>>> print(open("foo.py").read())  # contents of file
a==b

>>> FormatFile("foo.py")
('a == b\n', 'utf-8')

>>> FormatFile("foo.py", in_place=True)
(None, 'utf-8')

>>> print(open("foo.py").read())  # contents of file (now fixed)
a == b

2.8Knobs(按钮🔘)

可自定义按钮

参考文档

pep8官方文档

pep8中文翻译

Google Python Style Guide

yapf

上一篇下一篇

猜你喜欢

热点阅读