# 规范修炼-PEP8规范解读 当你的老大让你去修改别人的代码时,当你怀着热切的心情打开代码定睛一瞧,缩进错乱,命名不规范,通篇没注释,你是不是有那木一刻非常想提起四十米的大刀大喊一声:狗贼,来吃洒家一刀!!! 因代码不规范,码农枪击4名同事的事件真的在美国出现过. 为了防止世界被破坏,为了守护世界的和平,呸呸呸,串台了。咱吧,也不为别的,就为了把代码写的漂亮整洁,让同事一看,嚯,这代码真靓。 有句笑话是这样讲的:代码写的好的人,离职后找个人很快就能接手代码,而代码写的乱的人则是公司的不可替代人才!为何叫不可替代人才呢,自己写的代码回头看一看自己都看不懂,这境界就叫做不可替代。 因此我们需要非常重视编程规范?那么什么是编程规范,又都有哪些呢? ### 1.编程规范指的是什么 编程规范,指的是一组规则或指导意见,建议你,要求你,在编程时,用某种计算机语言写代码时,如何写,要遵循什么意见或建议。 一般来说,不是强制的,但是是多数人都遵守的一些规范。 不过,很多公司,倒是强制性的,使用统一的某种规范,此时,此规范或约定,就是要强制性的实行了。 在讨论编程规范时,另外还有几个常提到的概念,在此处可以理解基本上是同一个意思: + 编程约定Coding Convention(s) + 编程规范Coding Rule(s) + 编程风格Coding Style(s) + 编码标准Coding Standard(s) ### 2.编程规范有哪些 - 不同编程语言有自己的编程规范 - 不同公司或组织有自己的编程规范 - PEP8解读 - google规范解读 规范里面大部分是 **不要做的项多, 要做的比较少, 落地比较容易** **代码最主要的不是性能,而是可读性**, 有了可读性才有维护性 我觉得我的编码习惯比技术更有价值 > 傻瓜都能写出计算机可以读懂的代码, 只有优秀的程序员才能写出人能读懂的代码 > 程序猿不招妹子们喜爱的根本原因在于程序员追求了错误的目标:更短、更小、更快。 ### 3.PEP8解读 python官网PEP8文档地址:https://www.python.org/dev/peps/pep-0008/ #### 1. 什么是PEP? > PEP是 Python Enhancement Proposal 的缩写,翻译过来就是 Python增强建议书 。 > PEP8译者:本文基于 2013-08-02 最后修改的 PEP8 版本翻译,若要查看英文原文,请参考PEP8 然而,有时候编码规范的建议并不适用。 许多项目都有一套专有的编码风格指南,当冲突发生时,应以项目编码规范为优先。 特别是不要为了遵守PEP约定而破坏兼容性! 当以下情况发生时,也是忽略某个风格指南的好理由: 1. 当遵守规范会降低代码可读性,甚至对于那些依循 PEP 去阅读代码的人也是这样时。 2. 当遵守规范会与其他部分的代码风格背离时 — 当然也许这是一个修正某些混乱代码的机会。 3. 当那些并没有遵循规范的旧代码已无法修改时,而且也没有充足的理由去修改他们。 4. 当你的代码需要与旧版本的 Python 保持兼容,而旧版本的 Python 不支持规范中提到的特性时。 #### 2.PEP8的主要内容 1. 代码布局 2. 字符串引号 3. 表达式与空格 4. 注释的使用 5. 文档描述 6. 命名规范 7. 编码建议 #### 3. 代码布局 ##### 1. 缩进 + 每一级缩进使用4个空格。 + 续行应该与其包裹元素对齐,要么使用圆括号、方括号和花括号内的隐式行连接来垂直对齐, ​ 要么使用挂行缩进对齐 + 当使用挂行缩进时,应该考虑到第一行不应该有参数,以及使用缩进以区分自己是续行。 不推荐: ```python # 没有使用垂直对齐时,禁止把参数放在第一行 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) ``` 推荐: ```python # 与左括号对齐 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) ``` + 挂行缩进不一定要用4个空格 ```python # 挂行缩进不一定要用4个空格 foo = long_function_name( var_one, var_two, var_three, var_four) ``` + 当if语句的条件部分长到需要换行写的时候,注意可以在两个字符关键字的连接处(比如if),增加一个空格,再增加一个左括号来创造一个4空格缩进的多行条件。这会与if语句内同样使用4空格缩进的代码产生视觉冲突。对于如何(或是否)在视觉上进一步将这些条件行与`if`语句内的嵌套套件区分开,PEP不做任何明确的表述。可使用的选项包括但不限于下面几种情况: ```python # 没有额外的缩进 if (this_is_one_thing and that_is_another_thing): do_something() # 增加一个注释,在能提供语法高亮的编辑器中可以有一些区分 if (this_is_one_thing and that_is_another_thing): # Since both conditions are true, we can frobnicate. do_something() # 在条件判断的语句添加额外的缩进 if (this_is_one_thing and that_is_another_thing): do_something() ``` + 在多行结构中的大括号/中括号/小括号的右括号可以与内容对齐单独起一行作为最后一行的第一个字符, ```python # 就像这样: my_list = [ 1, 2, 3, 4, 5, 6, ] result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', ) # 或者也可以与多行结构的第一行第一个字符对齐,就像这样: my_list = [ 1, 2, 3, 4, 5, 6, ] result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', ) ``` ##### 2. 制表符还是空格? 空格是首选的缩进方式。 制表符只能用于与同样使用制表符缩进的代码保持一致。 Python3不允许同时使用空格和制表符的缩进。 ##### 3. 行的最大长度 1. 所有行限制的最大字符数为79。 2. 没有结构化限制的大块文本(文档字符或者注释),每行的最大字符数限制在72。 ##### 4. 在二元运算符之前应该换行吗? 几十年来,推荐的风格是在二元运算符之后中断。但是这回影响可读性,原因有二:操作符一般分布在屏幕上不同的列中,而且每个运算符被移到了操作数的上一行。 下面例子这个情况就需要额外注意,那些变量是相加的,那些变量是相减的: ```python # 不推荐: 操作符离操作数太远 income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest) ``` 为了解决这种可读性的问题,数学家和他们的出版商遵循了相反的约定。 遵循数学的传统能产出更多可读性高的代码: ```python # 推荐:运算符和操作数很容易进行匹配 income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest) ``` ##### 5. 空行 1. 顶层函数和类的定义,前后用两个空行隔开。 2. 类里的方法定义用一个空行隔开。 3. 相关的功能组可以用额外的空行(谨慎使用)隔开。 4. 一堆相关的单行代码之间的空白行可以省略。 5. 在函数中使用空行来区分逻辑段(谨慎使用)。 ##### 6. 源文件编码 + Python核心发布版本中的代码总是以UTF-8格式编码(或者在Python2中用ASCII编码)。 + 使用ASCII(在Python2中)或UTF-8(在Python3中)编码的文件不应具有编码声明。 + 对于Python 3和更高版本,标准库规定了以下策略(参见 PEP 3131): + Python标准库中的所有标识符必须使用ASCII标识符,并在可行的情况下使用英语单词(在许多情况下,缩写和技术术语是非英语的)。 + 此外,字符串文字和注释也必须是ASCII。 ##### 7. Imports 导入 1. 导入通常在分开的行,例如: ```python # 推荐: import os import sys # 不推荐: import sys, os # 也可以: from subprocess import Popen, PIPE ``` 2. 导入总是位于文件的顶部,在模块注释和文档字符串之后,在模块的全局变量与常量之前。 3. 导入应该按照以下顺序分组: 1. 标准库导入 2. 相关第三方库导入 3. 本地应用/库特定导入 4. 你应该在每一组导入之间加入空行 4. 推荐使用绝对路径导入,如果导入系统没有正确的配置(比如包里的一个目录在sys.path里的路径后),使用绝对路径会更加可读并且性能更好(至少能提供更好的错误信息): ```python import mypkg.sibling from mypkg import sibling from mypkg.sibling import example ``` 5. 当从一个包含类的模块中导入类时,常常这么写: ```python from myclass import MyClass from foo.bar.yourclass import YourClass ``` 6. 如果上述的写法导致名字的冲突,那么这么写: ```python import myclass import foo.bar.yourclass ``` 然后使用“myclass.MyClass”和“foo.bar.yourclass.YourClass”。 7. 避免通配符的导入(from import *),因为这样做会不知道命名空间中存在哪些名字,会使得读取接口和许多自动化工具之间产生混淆。 ##### 8.模块级Dunder名称 模块级“dunders”(也就是名字里有两个前缀下划线和两个后缀下划线) 如`__all__`,`__author__`,`__version__`等应被放置在模块文档字符串之后,以及除from `__future__` 之外的import表达式前面。Python要求将来在模块中的导入,必须出现在除文档字符串之外的其他代码之前。 ```python """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 ``` #### 4. 字符串引号 在Python中,单引号和双引号字符串是相同的。PEP不会为这个给出建议。选择一条规则并坚持使用下去。当一个字符串中包含单引号或者双引号字符的时候,使用和最外层不同的符号来避免使用反斜杠,从而提高可读性。 对于三引号字符串,总是使用双引号字符来与PEP 257中的文档字符串约定保持一致。 #### 5. 表达式和语句中的空格 总体原则,避免不必要的空格。 1. 各种右括号前不要加空格。 ```python # 符合约定的代码 spam(ham[1], {eggs: 2}) # 不符合约定的代码 spam( ham[ 1 ], { eggs: 2 } ) ``` 2. 逗号、冒号、分号前不要加空格。 ```python # 符合约定的代码 if x == 4: print x, y; x, y = y, x # 不符合约定的代码 if x == 4 : print x , y ; x , y = y , x ``` 3. 函数的左括号前不要加空格。如Func(1)。 ```python # 符合约定代码 spam(1) # 不符合约定的代码 spam (1) ``` 4. 序列的左括号前不要加空格。如list[2]。 ```python # 符合约定的代码 dict['key'] = list[index] # 不符合约定的代码 dict ['key'] = list [index] ``` 5. 操作符左右各加一个空格,不要为了对齐增加空格。 ```python # 符合约定的代码 x = 1 y = 2 long_variable = 3 # 不符合约定的代码 x = 1 y = 2 long_variable = 3 ``` 6. 函数默认参数使用的赋值符左右省略空格。 ```python # 符合约定的代码 def complex(real, imag=0.0): return magic(r=real, i=imag) # 不符合约定的代码 def complex(real, imag = 0.0): return magic(r = real, i = imag) ``` 7. 不要将多句语句写在同一行,尽管使用';'允许。 8. if/for/while语句中,即使执行语句只有一句,也必须另起一行。 复合语句(同一行中的多个语句)通常是不允许的。 ```python # 推荐: if foo == 'blah': do_blah_thing() do_one() do_two() do_three() # 不推荐: 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() ``` #### 6. Comments 注释 **总体原则,错误的注释不如没有注释。所以当一段代码发生变化时,第一件事就是要修改注释!** 注释必须使用英文,最好是完整的句子,首字母大写,句后要有结束符,结束符后跟两个空格,开始下一句。 如果是短语,可以省略结束符。 1. 块注释 在一段代码前增加的注释。在‘#’后加一空格。段落之间以只有‘#’的行间隔。比如: ```python # Description : Module config. # # Input : None # # Output : None ``` 2. 行注释 在一句代码后加注释。比如: ```python x = x + 1 # Increment x ``` ​ 但是这种方式尽量少使用。 3. 避免无谓的注释。 4. 文档字符串 要为所有的公共模块,函数,类以及方法编写文档说明。 非公共的方法没有必要,但是应该有一个描述方法具体作用的注释。这个注释应该在def那一行之后。 对于单行的文档说明,尾部的三引号应该和文档在同一行。 特别需要注意的是,多行文档说明使用的结尾三引号应该自成一行,例如: ```python """Return a foobang Optional plotz says to frobnicate the bizbaz first. """ ``` #### 7. Naming Conventions 命名规范 **总体原则,新编代码必须按下面命名风格进行,现有库的编码尽量保持风格。** Python库的命名规范很乱,从来没能做到完全一致。但是目前有一些推荐的命名标准。 约定俗成的命名约定: 1. Names to Avoid 应避免的名字 + 永远不要使用字母‘l’(小写的L),‘O’(大写的O),或者‘I’(大写的I)作为单字符变量名。 + 在有些字体里,这些字符无法和数字0和1区分,如果想用‘l’,用‘L’代替。 2. Class Names 类名 + 类名一般使用首字母大写的约定。 + 在接口被文档化并且主要被用于调用的情况下,可以使用函数的命名风格代替。 + 注意,对于内置的变量命名有一个单独的约定:大部分内置变量是单个单词(或者两个单词连接在一起),首字母大写的命名法只用于异常名或者内部的常量。 3. Function Names 函数名 + 函数名应该小写,如果想提高可读性可以用下划线分隔。 + 大小写混合仅在为了兼容原来主要以大小写混合风格的情况下使用(比如 threading.py),保持向后兼容性。 4. Function and method arguments 函数和方法参数 + 始终要将 self 作为实例方法的的第一个参数。 + 始终要将 cls 作为类静态方法的第一个参数。 + 如果函数的参数名和已有的关键词冲突,在最后加单一下划线比缩写或随意拼写更好。因此 class_ 比 clss 更好。(也许最好用同义词来避免这种冲突) ### 总结: + 严格要求4个空格缩进,而不是制表符 + 注意代码长度,每行不超过79个字符,并适当使用换行符 + 注意适当的代码空行以更好的区分代码区域 + 代码注释和文档注释说明必须正确,并优先更新 + 源代码编码格式统一使用utf-8,或和旧文件代码保持一致 + 从文件到类与函数甚至是变量的命名都要保持规范,且不要使用中文 + 重要的是要意识到代码的阅读比编写的频率要高很多很多