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 的格式规定,由于我对这些功能本身不了解就先不介绍,如果有需求请直接在原文搜索。心累。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容