Python 编程的 Style Guide

Python的作者既优雅又高冷又龟毛的在 PEP 8 里规定了 Python 程序编写时应该遵循的风格与格式。我通读了一遍,把我目前可以理解的总结在这里以备日后参考。

一. 基本观念

  1. 可读性至上。代码被读的次数肯定远比被写的次数多。因此作者十分重视代码的可读性,后文里的很多规定也都是基于这个出发点来考虑的。
  2. 一致性次之。如果原有代码的style与本指南不符,优先考虑保持一致性。但是新编写的模块优先遵循风格指南。

二. 具体操作

  1. 布局与格式

    • 缩进:优先用4个空格而不是制表符(tab)
    • 多行连续语句:对齐。几个正面例子:
    # 括号内开头对齐
    foo = long_function_name(var_one, var_two,
                             var_three, var_four)
    
    # Hanging indent: 括号第一行不填,第二行开始括号内的部分加倍缩进(多4个空格)
    def long_function_name(
            var_one, var_two, var_three,
            var_four):
        print(var_one)
    
    • 多行的构建赋值:后半括号要独立一行,且和前面的行对齐,两种方式皆可。
    my_list = [
        1, 2, 3,
        4, 5, 6,
        ]
    my_list = [
        1, 2, 3,
        4, 5, 6,
    ]
    
    • 行的长度少于72个字符:主要是为了便于分屏比对代码(作者操心命)。换行的操作是:优先用括号把并列的长语句括起来,然后就可以在连接处出换行了;实在不行的情况下,比如with或者assert语句,再用反斜杠''。
    • 在操作符(+-*/等)之前换行:方便阅读时快速理解操作符后面的内容是被如何操作的。
    • 空白行:顶层function和class前后各2行,method前后各1行。
    • 导入Import:不同的库每个导入各占一行;相同的库里多个模块可以在一行导入。导入顺序:标准库->空白行->第三方库->空白行->本地库。
    • 各种复合语句,最好各占一行。为了可读性,不要在一行内出现多个语句。
  2. 符号

    • 引号:string的两侧单引号双引号都可以。string的内容里如果有单引号,外面就用双引号,反之亦然。尽量不使用反斜杠来escape,影响可读性。
    • 空白符:
      1. 各种括号的内部不直接用空白符
      Yes: spam(ham[1], {eggs: 2})
      No:  spam( ham[ 1 ], { eggs: 2 } )
      
      1. 逗号分号冒号前面不直接用空白符
      Yes: if x == 4: print x, y; x, y = y, x
      No:  if x == 4 : print x , y ; x , y = y , x
      
      但是如果冒号是作为切片(slicing)的符号,两边可以有对称的空白符(当作优先级最低的运算符)。三参数的切片比如[::2],省略参数的地方空白符也要省掉。
      #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]
      
      1. function和variable名字和括号之间不加空白符(这个应该都知道吧。。)
      2. 避免在行尾使用多余的空白符,影响某些符号运行,你又很难发现。
      3. 运算符两端要各加且只加一个空白符,遇到多层优先级的运算,只在最低优先级的运算符两侧加空白符:
      # 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)
      
      1. 关键字参数的赋值等号不用空白符(这个我以前一直用错)
      def complex(real, imag=0.0):
          return magic(r=real, i=imag)
      
    • 末尾逗号:tuple和list的最后一项是可以接逗号的。这里说明一下:如果tuple和list是分行构建的,且每一行最后可以接一个逗号,方便以后的扩展时保持统一格式;如果是在同一行构建的,则没有必要在最后接一个逗号。
  3. 注释

    • 注释时保证注解的内容是保持更新的,不然和代码不符,还真倒不如不注释。
    • 注释要说人话,用完整的语句编写。开头大写,句尾加句号(操心命)
    • 注释句尾句号后加两个空白符在换行。我没理解有什么用,也许是考虑到markdown的格式
    • 大段的注释,每一行用#开头,几个空白符再写内容。段落间的空行也用#开头。(原来不是使用三引号做注释的...)
    • 同行注释尽量不使用,干扰阅读代码。如果某些特殊情况必须使用,和代码直接保持两个以上空白符。
    • Docstring:给所有的公开的模块,函数,类写doctring。私有的就不用写了,用注释代替。这种注释应该出现在 def 的后一行(我原来都写在前面的..)。默认使用三双引号来做docstring:对于多行的docstring,结尾的三引号要独占一行。
  4. 命名

    • 命名的风格有很多种。Python基本使用的是lower_case_with_underscore
    • 使用_single_leading_underscore:表示弱的内部使用。在from X import *是不会导入这一类对象
    • 使用single_tailing_underscore_:为了略微改变那些和 Python 的 keyword冲突的命名
    • 使用 __double_leading_underscore:我理解是表示强的内部使用,类的外面是无法调用这类对象的。(invoke name mangling)
    • 首位都有双下划线的命名只限python内置使用,不要随意起这种名字

    • 不要只用小写的‘l’,大写的‘O’和大写的‘I’,容易引起误解
    • Package和module的命名用lower_case_with_underscore
    • 类的命名使用 CapWords
    • 常量的命名应该使用UPPER_CASE
    • Class内部的命名,能用非公开就用非公开,因为把一个attribute由非公开变为公开要比反过来容易太多
  5. 编程过程中的风格建议

    • 和None做比较时永远用 is / is not 而不是 ==
    • 用 if x is not y 而不是 if not x is y : 更贴近自然语言
    • 定义函数的时候,永远用def 而不是 把一个lambda函数赋给一个变量名。lambda函数的唯一使用场景应该是一个复杂的表达里嵌入使用。
    • 在使Exceptions的时候,从Exception导出而不是BaseException
    • 在使用try...except...排除意外的时候,明确规定 except 的意外情况而不是直接使用‘except:’。这是确保不会把SystemExit和KeyboardInterrupt 算进去,那样的话control-c强退会难以操作。
    • 保证Return的一致性:一个函数里要么都不return,要么每种情况的return都明确规定,即便return None
    def foo(x):
        if x >= 0:
            return math.sqrt(x)
        else:
            return None
    
    def bar(x):
        if x < 0:
            return None
        return math.sqrt(x)
    
    • 永远优先使用string的methods而不是直接使用string module,比如用.startswith(),.endswith()来代替slicing做比较
    • 记住空的有序容器比如tuple set string list的布尔值是False
    • 永远不要把布尔值和True/False做比较,直接用它的自己的真假来控制流程

有关 function annotation 和 variable annotation 的格式规定,由于我对这些功能本身不了解就先不介绍,如果有需求请直接在原文搜索。心累。

你可能感兴趣的:(Python 编程的 Style Guide)